Merge pull request #14 from aprsme/fix-trail-line-connections
Fix trail lines not connecting historical positions
This commit is contained in:
commit
8906e218dd
6 changed files with 96 additions and 10 deletions
11
.github/workflows/elixir.yaml
vendored
11
.github/workflows/elixir.yaml
vendored
|
|
@ -48,23 +48,18 @@ jobs:
|
||||||
otp: ["27.3.4"]
|
otp: ["27.3.4"]
|
||||||
elixir: ["1.18.4"]
|
elixir: ["1.18.4"]
|
||||||
steps:
|
steps:
|
||||||
# Step: Setup Elixir + Erlang image as the base.
|
# Step: Setup Elixir + Erlang + Gleam using erlef/setup-beam
|
||||||
- name: Set up Elixir
|
- name: Set up Elixir, Erlang, and Gleam
|
||||||
uses: erlef/setup-beam@v1
|
uses: erlef/setup-beam@v1
|
||||||
with:
|
with:
|
||||||
otp-version: ${{matrix.otp}}
|
otp-version: ${{matrix.otp}}
|
||||||
elixir-version: ${{matrix.elixir}}
|
elixir-version: ${{matrix.elixir}}
|
||||||
|
gleam-version: "1.5.1"
|
||||||
|
|
||||||
# Step: Check out the code.
|
# Step: Check out the code.
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Step: Install Gleam
|
|
||||||
- name: Set up Gleam
|
|
||||||
uses: gleam-lang/setup-gleam@v1
|
|
||||||
with:
|
|
||||||
gleam-version: "1.5.1"
|
|
||||||
|
|
||||||
# Step: Install mix_gleam archive
|
# Step: Install mix_gleam archive
|
||||||
- name: Install mix_gleam
|
- name: Install mix_gleam
|
||||||
run: mix archive.install hex mix_gleam 0.6.2 --force
|
run: mix archive.install hex mix_gleam 0.6.2 --force
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,24 @@ export class TrailManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTrailDuration(durationHours: number) {
|
||||||
|
this.trailDuration = durationHours * 60 * 60 * 1000; // Convert hours to milliseconds
|
||||||
|
|
||||||
|
// Clean up existing trails based on new duration
|
||||||
|
const cutoffTime = Date.now() - this.trailDuration;
|
||||||
|
|
||||||
|
this.trails.forEach((trailState, baseCallsign) => {
|
||||||
|
// Filter positions based on new duration (skip historical dots)
|
||||||
|
trailState.positions = trailState.positions.filter((pos) => {
|
||||||
|
const posTimestamp = typeof pos.timestamp === "string" ? new Date(pos.timestamp).getTime() : pos.timestamp;
|
||||||
|
return posTimestamp >= cutoffTime;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update trail visualization
|
||||||
|
this.updateTrailVisualization(baseCallsign, trailState, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addPosition(
|
addPosition(
|
||||||
markerId: string,
|
markerId: string,
|
||||||
lat: number,
|
lat: number,
|
||||||
|
|
|
||||||
|
|
@ -663,6 +663,13 @@ let MapAPRSMap = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle trail duration updates from LiveView
|
||||||
|
self.handleEvent("update_trail_duration", (data: { duration_hours: number }) => {
|
||||||
|
if (self.trailManager) {
|
||||||
|
self.trailManager.setTrailDuration(data.duration_hours);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Handle new packets from LiveView
|
// Handle new packets from LiveView
|
||||||
self.handleEvent("new_packet", (data: MarkerData) => {
|
self.handleEvent("new_packet", (data: MarkerData) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,27 @@ export function parseTimestamp(timestamp: string | number | Date | undefined): n
|
||||||
/**
|
/**
|
||||||
* Get trail ID from marker data
|
* Get trail ID from marker data
|
||||||
*/
|
*/
|
||||||
export function getTrailId(data: { callsign_group?: string; callsign?: string; id: string }): string {
|
export function getTrailId(data: { callsign_group?: string; callsign?: string; id: string | number }): string {
|
||||||
return data.callsign_group || data.callsign || data.id;
|
// Prioritize callsign_group and callsign over extracting from ID
|
||||||
|
if (data.callsign_group) {
|
||||||
|
return data.callsign_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.callsign) {
|
||||||
|
return data.callsign;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For historical markers like "hist_CALLSIGN_123", extract the base callsign
|
||||||
|
const id = String(data.id);
|
||||||
|
if (id.startsWith("hist_")) {
|
||||||
|
// Remove hist_ prefix and any trailing numeric index
|
||||||
|
const withoutPrefix = id.replace(/^hist_/, "");
|
||||||
|
// Remove trailing _digits pattern
|
||||||
|
return withoutPrefix.replace(/_\d+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// For regular IDs, return as string
|
||||||
|
return String(data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -360,6 +360,9 @@ defmodule AprsmeWeb.MapLive.Index do
|
||||||
|
|
||||||
socket = assign(socket, trail_duration: duration, packet_age_threshold: new_threshold)
|
socket = assign(socket, trail_duration: duration, packet_age_threshold: new_threshold)
|
||||||
|
|
||||||
|
# Update client-side TrailManager with new duration
|
||||||
|
socket = push_event(socket, "update_trail_duration", %{duration_hours: hours})
|
||||||
|
|
||||||
# Trigger cleanup to remove packets that are now outside the new duration
|
# Trigger cleanup to remove packets that are now outside the new duration
|
||||||
send(self(), :cleanup_old_packets)
|
send(self(), :cleanup_old_packets)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,50 @@ describe('map_helpers', () => {
|
||||||
};
|
};
|
||||||
expect(getTrailId(data)).toBe('ID-1');
|
expect(getTrailId(data)).toBe('ID-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('extracts base callsign from historical ID format', () => {
|
||||||
|
const data = {
|
||||||
|
id: 'hist_MYCALL_123'
|
||||||
|
};
|
||||||
|
expect(getTrailId(data)).toBe('MYCALL');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles historical ID with complex callsign', () => {
|
||||||
|
const data = {
|
||||||
|
id: 'hist_KD8ABC-9_456'
|
||||||
|
};
|
||||||
|
expect(getTrailId(data)).toBe('KD8ABC-9');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('prioritizes callsign_group over historical ID extraction', () => {
|
||||||
|
const data = {
|
||||||
|
callsign_group: 'GROUP-1',
|
||||||
|
id: 'hist_MYCALL_123'
|
||||||
|
};
|
||||||
|
expect(getTrailId(data)).toBe('GROUP-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('prioritizes callsign over historical ID extraction', () => {
|
||||||
|
const data = {
|
||||||
|
callsign: 'REALCALL',
|
||||||
|
id: 'hist_MYCALL_123'
|
||||||
|
};
|
||||||
|
expect(getTrailId(data)).toBe('REALCALL');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles numeric ID', () => {
|
||||||
|
const data = {
|
||||||
|
id: 12345
|
||||||
|
};
|
||||||
|
expect(getTrailId(data)).toBe('12345');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles numeric historical ID', () => {
|
||||||
|
const data = {
|
||||||
|
id: 'hist_MYCALL_12345'
|
||||||
|
};
|
||||||
|
expect(getTrailId(data)).toBe('MYCALL');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('saveMapState', () => {
|
describe('saveMapState', () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue