map tweaks

This commit is contained in:
Graham McIntire 2025-06-22 17:39:10 -05:00
parent 3b0c1213f4
commit 43d4792760
No known key found for this signature in database
3 changed files with 139 additions and 33 deletions

View file

@ -23,12 +23,22 @@ export class TrailManager {
this.trails = new Map();
this.showTrails = true;
this.trailDuration = trailDuration;
// Ensure trail layer is added to the map and visible
if (this.trailLayer && this.trailLayer.bringToFront) {
setTimeout(() => this.trailLayer.bringToFront(), 100);
}
}
setShowTrails(show: boolean) {
this.showTrails = show;
if (!show) {
this.clearAllTrails();
} else {
// When re-enabling trails, recreate all trails immediately
this.trails.forEach((trailState, baseCallsign) => {
this.updateTrailVisualization(baseCallsign, trailState, true);
});
}
}
@ -77,8 +87,9 @@ export class TrailManager {
});
}
// Always update trail visualization
this.updateTrailVisualization(baseCallsign, trailState);
// Always update trail visualization, with immediate=true for historical dots
// This ensures historical trails are immediately visible when loaded all at once
this.updateTrailVisualization(baseCallsign, trailState, isHistoricalDot);
}
private extractBaseCallsign(markerId: string | any): string {
@ -97,7 +108,11 @@ export class TrailManager {
return id;
}
private updateTrailVisualization(baseCallsign: string, trailState: TrailState) {
private updateTrailVisualization(
baseCallsign: string,
trailState: TrailState,
immediate: boolean = false,
) {
// Remove old trail and dots
if (trailState.trail) {
this.trailLayer.removeLayer(trailState.trail);
@ -113,14 +128,17 @@ export class TrailManager {
const latLngs = trailState.positions.map((pos) => [pos.lat, pos.lng]);
// Create blue polyline connecting the historical position dots
// For historical positions (immediate=true), use higher opacity for better visibility
trailState.trail = L.polyline(latLngs, {
color: "#1E90FF",
weight: 3,
opacity: 0.8,
opacity: immediate ? 0.9 : 0.8,
smoothFactor: 1,
lineCap: "round",
lineJoin: "round",
className: "historical-trail-line",
// Ensure the trail renders on top of other map elements
zIndexOffset: immediate ? 1000 : 0,
}).addTo(this.trailLayer);
// Don't create additional dots here since historical positions are now shown as markers

View file

@ -305,9 +305,10 @@ let MapAPRSMap = {
const currentZoom = self.map!.getZoom();
const zoomDifference = self.lastZoom ? Math.abs(currentZoom - self.lastZoom) : 0;
// If zoom changed significantly (more than 2 levels), clear and reload
// If zoom changed significantly (more than 2 levels), request reload of normal markers
// but preserve historical ones and current position
if (zoomDifference > 2) {
self.pushEvent("clear_and_reload_markers", {});
self.pushEvent("refresh_markers", {});
}
self.sendBoundsToServer();
@ -668,25 +669,15 @@ let MapAPRSMap = {
self.markers!.delete(id);
self.markerStates!.delete(id);
// Only remove trail if this was the only marker for this callsign
if (self.trailManager && markerState) {
const callsign = markerState.callsign_group || markerState.callsign || id;
// Check if there are any live markers left for this callsign
let hasLiveMarkers = false;
self.markerStates!.forEach((state) => {
const stateCallsign = state.callsign_group || state.callsign;
if (stateCallsign === callsign && !state.historical) {
hasLiveMarkers = true;
}
});
// Only remove trail if no live markers remain
if (!hasLiveMarkers) {
self.trailManager.removeTrail(callsign);
}
}
// We now NEVER remove trails when clearing historical packets
// This preserves the historical trail visibility
}
});
// Make sure trail layer is visible and on top
if (self.trailLayer && self.trailLayer.bringToFront) {
self.trailLayer.bringToFront();
}
});
// Handle bounds-based marker filtering
@ -708,7 +699,13 @@ let MapAPRSMap = {
// Handle clear_all_markers event
self.handleEvent("clear_all_markers", () => {
// Modified to preserve historical trails - only removes non-historical markers
self.clearAllMarkers();
// Force trail layer to front to ensure visibility
if (self.trailLayer && self.trailLayer.bringToFront) {
self.trailLayer.bringToFront();
}
});
},
@ -720,8 +717,9 @@ let MapAPRSMap = {
const center = self.map!.getCenter();
const zoom = self.map!.getZoom();
// Remove markers that are now outside the visible bounds
self.removeMarkersOutsideBounds(bounds);
// We're no longer removing markers outside bounds during normal panning/zooming
// to preserve historical positions and the current marker
// self.removeMarkersOutsideBounds(bounds);
self.pushEvent("bounds_changed", {
bounds: {
@ -825,6 +823,13 @@ let MapAPRSMap = {
marker.addTo(self.markerLayer!);
self.markers!.set(data.id, marker);
// Make sure historical markers and trails stay visible
if (data.historical || data.is_most_recent_for_callsign) {
if (self.trailLayer && self.trailLayer.bringToFront) {
setTimeout(() => self.trailLayer.bringToFront(), 50);
}
}
// Store marker state for optimization
self.markerStates!.set(data.id, {
lat: lat,
@ -923,13 +928,49 @@ let MapAPRSMap = {
clearAllMarkers() {
const self = this as unknown as LiveViewHookContext;
self.markerLayer!.clearLayers();
// Instead of clearing all markers, only remove non-historical and non-current markers
const markersToPreserve = new Map();
const statesToPreserve = new Map();
// Identify historical markers and most recent markers to preserve
self.markers!.forEach((marker, id) => {
const markerState = self.markerStates!.get(String(id));
const isHistorical = (marker as any)._isHistorical || (markerState && markerState.historical);
const isMostRecent = markerState && markerState.is_most_recent_for_callsign;
// Keep historical markers and current position markers
if (isHistorical || isMostRecent) {
markersToPreserve.set(String(id), marker);
if (markerState) {
statesToPreserve.set(String(id), markerState);
}
} else {
// Remove only non-historical, non-current markers
self.markerLayer!.removeLayer(marker);
}
});
// Replace the markers and states with just the preserved ones
self.markers!.clear();
self.markerStates!.clear();
// Clear all trails
if (self.trailManager) {
self.trailManager.clearAllTrails();
markersToPreserve.forEach((marker, id) => {
self.markers!.set(id, marker);
});
statesToPreserve.forEach((state, id) => {
self.markerStates!.set(id, state);
});
// Don't clear trails - keep them visible
// if (self.trailManager) {
// self.trailManager.clearAllTrails();
// }
// Make sure trail layer is on top
if (self.trailLayer && self.trailLayer.bringToFront) {
setTimeout(() => self.trailLayer.bringToFront(), 100);
}
},
@ -940,6 +981,16 @@ let MapAPRSMap = {
const markersToRemove: string[] = [];
self.markers!.forEach((marker: L.Marker, id: string) => {
// Check if this is a historical marker or the most recent marker for a callsign
const markerState = self.markerStates!.get(String(id));
const isHistorical = (marker as any)._isHistorical || (markerState && markerState.historical);
const isMostRecent = markerState && markerState.is_most_recent_for_callsign;
// Always preserve historical markers and the most recent marker for a callsign
if (isHistorical || isMostRecent) {
return; // Skip this marker, don't remove it
}
const position = marker.getLatLng();
const lat = position.lat;
const lng = position.lng;

View file

@ -160,7 +160,26 @@ defmodule AprsWeb.MapLive.CallsignView do
|> assign(packets_loaded: true)
end
# Don't start historical replay yet - wait for bounds to be available
# Immediately start historical replay without waiting for bounds
# If bounds aren't available, use a default world bounds
socket =
if is_nil(socket.assigns.map_bounds) do
# Use a default global bounds to ensure we get all historical points
default_bounds = %{
north: 90.0,
south: -90.0,
east: 180.0,
west: -180.0
}
socket = assign(socket, map_bounds: default_bounds)
socket = start_historical_replay(socket)
assign(socket, replay_started: true, replay_active: true)
else
# Bounds already available, use them
socket = start_historical_replay(socket)
assign(socket, replay_started: true, replay_active: true)
end
# If we have a pending geolocation, zoom to it after a delay
socket =
@ -252,12 +271,30 @@ defmodule AprsWeb.MapLive.CallsignView do
end
def handle_info(:auto_start_replay, socket) do
if not socket.assigns.replay_started and socket.assigns.map_ready and
not is_nil(socket.assigns.map_bounds) do
# We now handle this in map_ready event, but keep this for backward compatibility
# and in case auto_start_replay is triggered from elsewhere
if socket.assigns.map_ready and not socket.assigns.replay_started do
# Update socket with bounds if needed
socket =
if is_nil(socket.assigns.map_bounds) do
# Use a default global bounds if needed
default_bounds = %{
north: 90.0,
south: -90.0,
east: 180.0,
west: -180.0
}
assign(socket, map_bounds: default_bounds)
else
socket
end
socket = start_historical_replay(socket)
{:noreply, assign(socket, replay_started: true, replay_active: true)}
else
if not socket.assigns.map_ready or is_nil(socket.assigns.map_bounds) do
# Only retry if map isn't ready yet
if not socket.assigns.map_ready do
Process.send_after(self(), :auto_start_replay, 1000)
end