skyview/assets/static/js/modules/aircraft-manager.js

610 lines
26 KiB
JavaScript
Raw Normal View History

// Aircraft marker and data management module
export class AircraftManager {
constructor(map, callsignManager = null) {
this.map = map;
this.callsignManager = callsignManager;
this.aircraftData = new Map();
this.aircraftMarkers = new Map();
this.aircraftTrails = new Map();
this.showTrails = false;
// Debug: Track marker lifecycle
this.markerCreateCount = 0;
this.markerUpdateCount = 0;
this.markerRemoveCount = 0;
// SVG icon cache
this.iconCache = new Map();
this.loadIcons();
// Selected aircraft trail tracking
this.selectedAircraftCallback = null;
// Map event listeners removed - let Leaflet handle positioning naturally
}
async loadIcons() {
const iconTypes = ['commercial', 'helicopter', 'military', 'cargo', 'ga', 'ground'];
for (const type of iconTypes) {
try {
const response = await fetch(`/static/icons/${type}.svg`);
const svgText = await response.text();
this.iconCache.set(type, svgText);
} catch (error) {
console.warn(`Failed to load icon for ${type}:`, error);
// Fallback to inline SVG if needed
this.iconCache.set(type, this.createFallbackIcon(type));
}
}
}
createFallbackIcon(type) {
// Fallback inline SVG if file loading fails
const color = 'currentColor';
let path = '';
switch (type) {
case 'helicopter':
path = `<circle cx="0" cy="0" r="10" fill="none" stroke="${color}" stroke-width="1" opacity="0.3"/>
<path d="M0,-8 L-6,6 L-1,6 L0,8 L1,6 L6,6 Z" fill="${color}"/>
<path d="M0,-6 L0,-10" stroke="${color}" stroke-width="2"/>
<path d="M0,6 L0,8" stroke="${color}" stroke-width="2"/>`;
break;
case 'military':
path = `<path d="M0,-12 L-4,2 L-8,8 L-2,6 L0,12 L2,6 L8,8 L4,2 Z" fill="${color}"/>`;
break;
case 'cargo':
path = `<path d="M0,-12 L-10,8 L-3,8 L0,12 L3,8 L10,8 Z" fill="${color}"/>
<rect x="-2" y="-6" width="4" height="8" fill="${color}"/>`;
break;
case 'ga':
path = `<path d="M0,-10 L-5,6 L-1,6 L0,10 L1,6 L5,6 Z" fill="${color}"/>`;
break;
case 'ground':
path = `<rect x="-6" y="-4" width="12" height="8" fill="${color}" rx="2"/>
<circle cx="-3" cy="2" r="2" fill="#333"/>
<circle cx="3" cy="2" r="2" fill="#333"/>`;
break;
default:
path = `<path d="M0,-12 L-8,8 L-2,8 L0,12 L2,8 L8,8 Z" fill="${color}"/>`;
}
return `<?xml version="1.0" encoding="UTF-8"?>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(16,16)">
${path}
</g>
</svg>`;
}
updateAircraftData(data) {
if (data.aircraft) {
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// Track which aircraft are new or have position changes for debugging
let newCount = 0;
let updatedCount = 0;
let positionChanges = 0;
// Clear old data and update with new data
const previousData = new Map(this.aircraftData);
this.aircraftData.clear();
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
for (const [icao, aircraft] of Object.entries(data.aircraft)) {
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
const previousAircraft = previousData.get(icao);
if (!previousAircraft) {
newCount++;
} else {
updatedCount++;
// Check for position changes
if (previousAircraft.Latitude !== aircraft.Latitude ||
previousAircraft.Longitude !== aircraft.Longitude ||
previousAircraft.Track !== aircraft.Track ||
previousAircraft.Altitude !== aircraft.Altitude) {
positionChanges++;
}
}
this.aircraftData.set(icao, aircraft);
}
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// Debug logging for track propagation issues
if (newCount > 0 || positionChanges > 0) {
console.debug(`Aircraft update: ${newCount} new, ${updatedCount} updated, ${positionChanges} position changes`);
}
}
}
updateMarkers() {
if (!this.map) {
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
console.debug("Map not available for updateMarkers");
return;
}
// Clear stale aircraft markers
const currentICAOs = new Set(this.aircraftData.keys());
for (const [icao, marker] of this.aircraftMarkers) {
if (!currentICAOs.has(icao)) {
this.map.removeLayer(marker);
this.aircraftMarkers.delete(icao);
// Remove trail if it exists
if (this.aircraftTrails.has(icao)) {
const trail = this.aircraftTrails.get(icao);
if (trail.polyline) {
this.map.removeLayer(trail.polyline);
}
this.aircraftTrails.delete(icao);
}
// Notify if this was the selected aircraft
if (this.selectedAircraftCallback && this.selectedAircraftCallback(icao)) {
// Aircraft was selected and disappeared - could notify main app
// For now, the callback will return false automatically since selectedAircraft will be cleared
}
this.markerRemoveCount++;
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
console.debug(`Removed stale aircraft marker: ${icao}`);
}
}
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// Update aircraft markers - process ALL aircraft, not just those with valid coordinates yet
// Let updateAircraftMarker handle coordinate validation
for (const [icao, aircraft] of this.aircraftData) {
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// More comprehensive coordinate check
const hasCoords = aircraft.Latitude != null && aircraft.Longitude != null &&
aircraft.Latitude !== 0 && aircraft.Longitude !== 0;
const validLat = aircraft.Latitude >= -90 && aircraft.Latitude <= 90;
const validLng = aircraft.Longitude >= -180 && aircraft.Longitude <= 180;
if (hasCoords && validLat && validLng) {
this.updateAircraftMarker(icao, aircraft);
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
} else if (hasCoords) {
// Log invalid coordinates for debugging
console.debug(`Invalid coordinates for ${icao}: lat=${aircraft.Latitude}, lng=${aircraft.Longitude}`);
}
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// If no coordinates, we still want to process for other updates (trails, etc.)
}
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
console.debug(`Markers update complete: ${this.aircraftMarkers.size} active markers, ${this.aircraftData.size} aircraft`);
}
updateAircraftMarker(icao, aircraft) {
const pos = [aircraft.Latitude, aircraft.Longitude];
// Check for invalid coordinates - proper geographic bounds
const isValidLat = pos[0] >= -90 && pos[0] <= 90;
const isValidLng = pos[1] >= -180 && pos[1] <= 180;
if (!isValidLat || !isValidLng || isNaN(pos[0]) || isNaN(pos[1])) {
console.error(`🚨 Invalid coordinates for ${icao}: [${pos[0]}, ${pos[1]}] (lat must be -90 to +90, lng must be -180 to +180)`);
return; // Don't create/update marker with invalid coordinates
}
if (this.aircraftMarkers.has(icao)) {
// Update existing marker - KISS approach
const marker = this.aircraftMarkers.get(icao);
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// Always update position - don't try to be too smart about detecting changes
const oldPos = marker.getLatLng();
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
const positionChanged = Math.abs(oldPos.lat - pos[0]) > 0.0001 || Math.abs(oldPos.lng - pos[1]) > 0.0001;
// ALWAYS update position regardless of change detection to ensure propagation
marker.setLatLng(pos);
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
if (positionChanged) {
// Debug significant position updates
console.debug(`Position change for ${icao}: [${pos[0].toFixed(4)}, ${pos[1].toFixed(4)}] (was [${oldPos.lat.toFixed(4)}, ${oldPos.lng.toFixed(4)}])`);
}
// Check if icon needs to be updated (track rotation, aircraft type, or ground status changes)
const currentRotation = marker._currentRotation || 0;
const currentType = marker._currentType || null;
const currentOnGround = marker._currentOnGround || false;
const newType = this.getAircraftIconType(aircraft);
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// Fix rotation change detection - handle undefined/null tracks properly
const newTrack = aircraft.Track || 0;
const rotationChanged = aircraft.Track !== undefined && aircraft.Track !== null &&
Math.abs(currentRotation - newTrack) > 10; // Increased threshold
const typeChanged = currentType !== newType;
const groundStatusChanged = currentOnGround !== aircraft.OnGround;
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
// Update icon if anything changed, OR if this is a new track value and we didn't have one before
const firstTrackUpdate = currentRotation === 0 && aircraft.Track !== undefined && aircraft.Track !== null && aircraft.Track !== 0;
if (rotationChanged || typeChanged || groundStatusChanged || firstTrackUpdate) {
marker.setIcon(this.createAircraftIcon(aircraft));
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
marker._currentRotation = newTrack;
marker._currentType = newType;
marker._currentOnGround = aircraft.OnGround || false;
Fix aircraft track propagation issues in web frontend This commit addresses issue #23 where aircraft track changes were not propagating properly to the web frontend. The fixes include: **Server-side improvements:** - Enhanced WebSocket broadcast reliability with timeout-based queueing - Increased broadcast channel buffer size (1000 -> 2000) - Improved error handling and connection management - Added write timeouts to prevent slow clients from blocking updates - Enhanced connection cleanup and ping/pong handling - Added debug endpoint /api/debug/websocket for troubleshooting - Relaxed position validation thresholds for better track acceptance **Frontend improvements:** - Enhanced WebSocket manager with exponential backoff reconnection - Improved aircraft position update detection and logging - Fixed position update logic to always propagate changes to map - Better coordinate validation and error reporting - Enhanced debugging with detailed console logging - Fixed track rotation update thresholds and logic - Improved marker lifecycle management and cleanup - Better handling of edge cases in aircraft state transitions **Key bug fixes:** - Removed overly aggressive position change detection that blocked updates - Fixed track rotation sensitivity (5° -> 10° threshold) - Enhanced coordinate validation to handle null/undefined values - Improved WebSocket message ordering and processing - Fixed marker position updates to always propagate to Leaflet These changes ensure reliable real-time aircraft tracking with proper position, heading, and altitude updates across multiple data sources. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 10:14:03 +02:00
if (rotationChanged || firstTrackUpdate) {
console.debug(`Updated track for ${icao}: ${aircraft.Track}° (was ${currentRotation}°)`);
}
}
// Handle popup exactly like Leaflet expects
if (marker.isPopupOpen()) {
marker.setPopupContent(this.createPopupContent(aircraft));
// Enhance callsign display for updated popup
const popupElement = marker.getPopup().getElement();
if (popupElement) {
this.enhanceCallsignDisplay(popupElement);
}
}
this.markerUpdateCount++;
} else {
// Create new marker
const icon = this.createAircraftIcon(aircraft);
try {
const marker = L.marker(pos, {
icon: icon
}).addTo(this.map);
// Store current properties for future update comparisons
marker._currentRotation = aircraft.Track || 0;
marker._currentType = this.getAircraftIconType(aircraft);
marker._currentOnGround = aircraft.OnGround || false;
marker.bindPopup(this.createPopupContent(aircraft), {
maxWidth: 450,
className: 'aircraft-popup'
});
// Enhance callsign display when popup opens
marker.on('popupopen', (e) => {
const popupElement = e.popup.getElement();
if (popupElement) {
this.enhanceCallsignDisplay(popupElement);
}
});
this.aircraftMarkers.set(icao, marker);
this.markerCreateCount++;
// Force immediate visibility
if (marker._icon) {
marker._icon.style.display = 'block';
marker._icon.style.opacity = '1';
marker._icon.style.visibility = 'visible';
}
} catch (error) {
console.error(`Failed to create marker for ${icao}:`, error);
}
}
// Update trails - check both global trails and individual selected aircraft
if (this.showTrails || this.isSelectedAircraftTrailEnabled(icao)) {
this.updateAircraftTrail(icao, aircraft);
}
}
createAircraftIcon(aircraft) {
const iconType = this.getAircraftIconType(aircraft);
const color = this.getAircraftColor(iconType, aircraft);
const size = aircraft.OnGround ? 12 : 16;
const rotation = aircraft.Track || 0;
// Get SVG template from cache
let svgTemplate = this.iconCache.get(iconType) || this.iconCache.get('commercial');
if (!svgTemplate) {
// Ultimate fallback - create a simple circle
svgTemplate = `<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(16,16)">
<circle cx="0" cy="0" r="8" fill="currentColor"/>
</g>
</svg>`;
}
// Apply color and rotation to the SVG
let svg = svgTemplate
.replace(/currentColor/g, color)
.replace(/width="32"/, `width="${size * 2}"`)
.replace(/height="32"/, `height="${size * 2}"`);
// Add rotation to the transform
if (rotation !== 0) {
svg = svg.replace(/transform="translate\(16,16\)"/, `transform="translate(16,16) rotate(${rotation})"`);
}
return L.divIcon({
html: svg,
iconSize: [size * 2, size * 2],
iconAnchor: [size, size],
className: 'aircraft-marker'
});
}
getAircraftType(aircraft) {
// For display purposes, return the actual ADS-B category
// This is used in the popup display
if (aircraft.OnGround) return 'On Ground';
if (aircraft.Category) return aircraft.Category;
return 'Unknown';
}
getAircraftIconType(aircraft) {
// For icon selection, determine which SVG shape to use based on category
if (aircraft.OnGround) return 'ground';
if (aircraft.Category) {
const cat = aircraft.Category.toLowerCase();
// Specialized aircraft types
if (cat.includes('helicopter') || cat.includes('rotorcraft')) return 'helicopter';
if (cat.includes('military') || cat.includes('fighter') || cat.includes('bomber')) return 'military';
if (cat.includes('glider') || cat.includes('ultralight')) return 'ga';
// Weight-based categories with specific icons
if (cat.includes('light') && cat.includes('7000')) return 'light';
if (cat.includes('medium') && cat.includes('7000-34000')) return 'medium';
if (cat.includes('large') && cat.includes('34000-136000')) return 'large';
if (cat.includes('heavy') && cat.includes('136000')) return 'heavy';
if (cat.includes('high vortex')) return 'large'; // Use large icon for high vortex
// Fallback category matching
if (cat.includes('heavy') || cat.includes('super')) return 'heavy';
if (cat.includes('large')) return 'large';
if (cat.includes('medium')) return 'medium';
if (cat.includes('light')) return 'light';
}
// Default to medium icon for unknown aircraft
return 'medium';
}
getAircraftColor(type, aircraft) {
// Color mapping based on aircraft type/size
switch (type) {
case 'military': return '#ff4444'; // Red-orange for military
case 'helicopter': return '#ff00ff'; // Magenta for helicopters
case 'ground': return '#888888'; // Gray for ground vehicles
case 'ga': return '#ffff00'; // Yellow for general aviation
case 'light': return '#00bfff'; // Sky blue for light aircraft
case 'medium': return '#00ff88'; // Green for medium aircraft
case 'large': return '#ff8c00'; // Orange for large aircraft
case 'heavy': return '#ff0000'; // Red for heavy aircraft
default: return '#00ff88'; // Default green for unknown
}
}
updateAircraftTrail(icao, aircraft) {
// Use server-provided position history
if (!aircraft.position_history || aircraft.position_history.length < 2) {
// No trail data available or not enough points
if (this.aircraftTrails.has(icao)) {
const trail = this.aircraftTrails.get(icao);
if (trail.polyline) {
this.map.removeLayer(trail.polyline);
}
this.aircraftTrails.delete(icao);
}
return;
}
// Convert position history to Leaflet format
const trailPoints = aircraft.position_history.map(point => [point.lat, point.lon]);
// Get or create trail object
if (!this.aircraftTrails.has(icao)) {
this.aircraftTrails.set(icao, {});
}
const trail = this.aircraftTrails.get(icao);
// Remove old polyline if it exists
if (trail.polyline) {
this.map.removeLayer(trail.polyline);
}
// Create gradient effect - newer points are brighter
const segments = [];
for (let i = 1; i < trailPoints.length; i++) {
const opacity = 0.2 + (0.6 * (i / trailPoints.length)); // Fade from 0.2 to 0.8
const segment = L.polyline([trailPoints[i-1], trailPoints[i]], {
color: '#00d4ff',
weight: 2,
opacity: opacity
});
segments.push(segment);
}
// Create a feature group for all segments
trail.polyline = L.featureGroup(segments).addTo(this.map);
}
createPopupContent(aircraft) {
const type = this.getAircraftType(aircraft);
const country = aircraft.country || 'Unknown';
const flag = aircraft.flag || '🏳️';
const altitude = aircraft.Altitude || aircraft.BaroAltitude || 0;
const altitudeM = altitude ? Math.round(altitude * 0.3048) : 0;
const speedKmh = aircraft.GroundSpeed ? Math.round(aircraft.GroundSpeed * 1.852) : 0;
const distance = this.calculateDistance(aircraft);
const distanceKm = distance ? (distance * 1.852).toFixed(1) : 'N/A';
return `
<div class="aircraft-popup">
<div class="popup-header">
<div class="flight-info">
<span class="icao-flag">${flag}</span>
<span class="flight-id">${aircraft.ICAO24 || 'N/A'}</span>
${aircraft.Callsign ? `→ <span class="callsign-loading" data-callsign="${aircraft.Callsign}"><span class="callsign">${aircraft.Callsign}</span></span>` : ''}
</div>
</div>
<div class="popup-details">
<div class="detail-row">
<strong>Country:</strong> ${country}
</div>
<div class="detail-row">
<strong>Type:</strong> ${type}
</div>
Fix transponder capability overwriting aircraft type in popups Add dedicated transponder fields to prevent Mode S decoder from overwriting the aircraft Category field with transponder capability information. **Problem Fixed:** - DF11 All-Call Reply messages were setting aircraft.Category to "Enhanced Transponder" - This overwrote the actual aircraft type (Light, Medium, Heavy, etc.) from ADS-B - Users saw "Enhanced Transponder" instead of proper aircraft classification **Solution:** - Added dedicated TransponderCapability and TransponderLevel fields to Aircraft struct - Updated JSON marshaling to include new transponder fields - Modified decodeAllCallReply() to use dedicated fields instead of Category - Enhanced popup display to show transponder info separately from aircraft type - Removed Category overwriting in decodeCommD() for DF24 messages **New Aircraft Fields:** - TransponderCapability: Human-readable capability description - TransponderLevel: Numeric capability level (0-7) **Popup Display:** - Aircraft type now shows correctly (Light, Medium, Heavy, etc.) - Transponder capability displayed as separate "Transponder:" field when available - Only shows transponder info when DF11 messages have been received **Data Quality Indicators Clarification:** - NACp: Navigation Accuracy Category - Position (0-11, higher = more accurate) - NACv: Navigation Accuracy Category - Velocity (0-4, higher = more accurate) - SIL: Surveillance Integrity Level (0-3, higher = more reliable) Users now see both proper aircraft classification AND transponder capability information without conflicts between different message types. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-24 19:28:12 +02:00
${aircraft.TransponderCapability ? `
<div class="detail-row">
<strong>Transponder:</strong> ${aircraft.TransponderCapability}
</div>` : ''}
Improve transponder display and add combined signal quality indicator **Transponder Display Improvements:** - Simplified transponder capability labels to be more concise - "Level 2+ Transponder" → "Level 2+" - "Enhanced Transponder" → "Enhanced" - "Alert/Emergency Status" → "Alert/Emergency" - Removes redundant "Transponder" suffix for cleaner popup display **New Signal Quality Feature:** - Added SignalQuality field combining NACp, NACv, and SIL into human-readable assessment - Quality levels: "Excellent", "Good", "Fair", "Poor" based on aviation standards - Prioritizes surveillance integrity (SIL) then position accuracy (NACp) - Automatically calculated when operational status messages (TC 31) are received - Displayed in aircraft popup when available **Signal Quality Algorithm:** - **Excellent**: High integrity (SIL ≥ 2) with precise position (NACp ≥ 9) - **Good**: Good integrity with moderate accuracy OR very high accuracy - **Fair**: Some integrity with basic accuracy OR high accuracy without integrity - **Poor**: Low but usable quality indicators - **Empty**: No quality data available **Frontend Enhancements:** - Added "Signal Quality" row to aircraft popup - Only displays when quality data is available - Clean integration with existing popup layout - Shows alongside transponder information **Data Quality Context:** - NACp: Position accuracy (0-11, higher = more precise location) - NACv: Velocity accuracy (0-4, higher = more precise speed/heading) - SIL: Surveillance integrity (0-3, higher = more reliable data) This gives users a quick understanding of data reliability without needing to interpret technical NACp/NACv/SIL values directly. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-24 19:31:46 +02:00
${aircraft.SignalQuality ? `
<div class="detail-row">
<strong>Signal Quality:</strong> ${aircraft.SignalQuality}
</div>` : ''}
<div class="detail-grid">
<div class="detail-item">
<div class="label">Altitude:</div>
<div class="value${!altitude ? ' no-data' : ''}">${altitude ? `${altitude} ft | ${altitudeM} m` : 'N/A'}</div>
</div>
<div class="detail-item">
<div class="label">Squawk:</div>
<div class="value${!aircraft.Squawk ? ' no-data' : ''}">${aircraft.Squawk || 'N/A'}</div>
</div>
<div class="detail-item">
<div class="label">Speed:</div>
<div class="value${!aircraft.GroundSpeed ? ' no-data' : ''}">${aircraft.GroundSpeed !== undefined && aircraft.GroundSpeed !== null ? `${aircraft.GroundSpeed} kt | ${speedKmh} km/h` : 'N/A'}</div>
</div>
<div class="detail-item">
<div class="label">Track:</div>
<div class="value${aircraft.Track === undefined || aircraft.Track === null ? ' no-data' : ''}">${aircraft.Track !== undefined && aircraft.Track !== null ? `${aircraft.Track}°` : 'N/A'}</div>
</div>
<div class="detail-item">
<div class="label">V/Rate:</div>
<div class="value${!aircraft.VerticalRate ? ' no-data' : ''}">${aircraft.VerticalRate ? `${aircraft.VerticalRate} ft/min` : 'N/A'}</div>
</div>
<div class="detail-item">
<div class="label">Distance:</div>
<div class="value${distance ? '' : ' no-data'}">${distanceKm !== 'N/A' ? `${distanceKm} km` : 'N/A'}</div>
</div>
</div>
<div class="detail-row">
<strong>Position:</strong> ${aircraft.Latitude?.toFixed(4)}°, ${aircraft.Longitude?.toFixed(4)}°
</div>
<div class="detail-row">
<strong>Messages:</strong> ${aircraft.TotalMessages || 0}
</div>
<div class="detail-row">
<strong>Age:</strong> ${aircraft.Age ? aircraft.Age.toFixed(1) : '0'}s
</div>
</div>
</div>
`;
}
calculateDistance(aircraft) {
if (!aircraft.Latitude || !aircraft.Longitude) return null;
// Use closest source as reference point
let minDistance = Infinity;
for (const [id, srcData] of Object.entries(aircraft.sources || {})) {
if (srcData.distance && srcData.distance < minDistance) {
minDistance = srcData.distance;
}
}
return minDistance === Infinity ? null : minDistance;
}
// Enhance callsign display in popup after it's created
async enhanceCallsignDisplay(popupElement) {
if (!this.callsignManager) return;
const callsignElements = popupElement.querySelectorAll('.callsign-loading');
for (const element of callsignElements) {
const callsign = element.dataset.callsign;
if (!callsign) continue;
try {
const callsignInfo = await this.callsignManager.getCallsignInfo(callsign);
const richDisplay = this.callsignManager.generateCallsignDisplay(callsignInfo, callsign);
element.innerHTML = richDisplay;
element.classList.remove('callsign-loading');
element.classList.add('callsign-enhanced');
} catch (error) {
console.warn(`Failed to enhance callsign display for ${callsign}:`, error);
// Keep the simple display on error
element.classList.remove('callsign-loading');
}
}
}
toggleTrails() {
this.showTrails = !this.showTrails;
if (!this.showTrails) {
// Clear all trails
this.aircraftTrails.forEach((trail, icao) => {
if (trail.polyline) {
this.map.removeLayer(trail.polyline);
}
});
this.aircraftTrails.clear();
}
return this.showTrails;
}
showAircraftTrail(icao) {
const aircraft = this.aircraftData.get(icao);
if (aircraft && aircraft.position_history && aircraft.position_history.length >= 2) {
this.updateAircraftTrail(icao, aircraft);
}
}
hideAircraftTrail(icao) {
if (this.aircraftTrails.has(icao)) {
const trail = this.aircraftTrails.get(icao);
if (trail.polyline) {
this.map.removeLayer(trail.polyline);
}
this.aircraftTrails.delete(icao);
}
}
setSelectedAircraftCallback(callback) {
this.selectedAircraftCallback = callback;
}
isSelectedAircraftTrailEnabled(icao) {
return this.selectedAircraftCallback && this.selectedAircraftCallback(icao);
}
centerMapOnAircraft(includeSourcesCallback = null) {
const validAircraft = Array.from(this.aircraftData.values())
.filter(a => a.Latitude && a.Longitude);
const allPoints = [];
// Add aircraft positions
validAircraft.forEach(a => {
allPoints.push([a.Latitude, a.Longitude]);
});
// Add source positions if callback provided
if (includeSourcesCallback && typeof includeSourcesCallback === 'function') {
const sourcePositions = includeSourcesCallback();
allPoints.push(...sourcePositions);
}
if (allPoints.length === 0) return;
if (allPoints.length === 1) {
// Center on single point
this.map.setView(allPoints[0], 12);
} else {
// Fit bounds to all points (aircraft + sources)
const bounds = L.latLngBounds(allPoints);
this.map.fitBounds(bounds.pad(0.1));
}
}
}