/** * Flight Display * This class handles displaying flight path on a map and flight information */ class FlightDisplay { /** * Create a flight display handler * @param {string} mapElementId - The ID of the element to render the map in * @param {string} infoElementId - The ID of the element to display flight info */ constructor(mapElementId, infoElementId) { this.mapElementId = mapElementId; this.infoElementId = infoElementId; this.map = null; this.flightPath = null; this.markers = { start: null, end: null }; this.initMap(); } /** * Initialize the Leaflet map */ initMap() { // Initialize the map this.map = L.map(this.mapElementId).setView([51.505, -0.09], 13); // Add OpenStreetMap tiles L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(this.map); } /** * Display the flight path and information * @param {object} flightData - Parsed flight data from IGCParser */ displayFlight(flightData) { if (!flightData || !flightData.fixes || flightData.fixes.length === 0) { return; } // Clear previous flight display this.clearFlight(); // Get coordinates for flight path const coordinates = flightData.fixes.map(fix => [fix.latitude, fix.longitude]); // Create a polyline for the flight path this.flightPath = L.polyline(coordinates, { color: '#4682b4', weight: 3, opacity: 0.8 }).addTo(this.map); // Add takeoff and landing markers const startPoint = coordinates[0]; const endPoint = coordinates[coordinates.length - 1]; this.markers.start = L.marker(startPoint, { title: 'Takeoff', icon: L.divIcon({ className: 'takeoff-marker', html: '
', iconSize: [16, 16], iconAnchor: [8, 8] }) }).addTo(this.map); this.markers.end = L.marker(endPoint, { title: 'Landing', icon: L.divIcon({ className: 'landing-marker', html: '
', iconSize: [16, 16], iconAnchor: [8, 8] }) }).addTo(this.map); // Fit the map to show the entire flight path this.map.fitBounds(this.flightPath.getBounds(), { padding: [30, 30] }); // Display flight information this.displayFlightInfo(flightData); } /** * Clear the flight path and markers from the map */ clearFlight() { if (this.flightPath) { this.map.removeLayer(this.flightPath); this.flightPath = null; } if (this.markers.start) { this.map.removeLayer(this.markers.start); this.markers.start = null; } if (this.markers.end) { this.map.removeLayer(this.markers.end); this.markers.end = null; } } /** * Display flight information * @param {object} flightData - Parsed flight data */ displayFlightInfo(flightData) { const infoElement = document.getElementById(this.infoElementId); if (!infoElement) { console.error(`Element with ID ${this.infoElementId} not found`); return; } const header = flightData.header; const stats = flightData.stats; // Format duration const duration = this.formatDuration(stats.duration); // Format altitudes with 500m scale const altitudeScaleFactor = 500; const maxAltitudeScaled = (stats.maxAltitude / altitudeScaleFactor).toFixed(2); const minAltitudeScaled = (stats.minAltitude / altitudeScaleFactor).toFixed(2); // Create HTML for flight info const html = `
Date: ${header.date || 'Unknown'}
Pilot: ${header.pilot}
Glider: ${header.gliderType} (${header.gliderReg})
Duration: ${duration}
Distance: ${stats.distance.toFixed(2)} km
Max Altitude: ${maxAltitudeScaled} x 500m (${stats.maxAltitude}m)
Min Altitude: ${minAltitudeScaled} x 500m (${stats.minAltitude}m)
Max Climb: ${stats.maxClimb.toFixed(1)} m/min
Max Sink: ${stats.maxSink.toFixed(1)} m/min
`; infoElement.innerHTML = html; } /** * Format duration in seconds to HH:MM:SS * @param {number} seconds - Duration in seconds * @returns {string} - Formatted duration */ formatDuration(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; } }