From b527f5a8ee455f93acfea233ba800285c86236ed Mon Sep 17 00:00:00 2001 From: Ole-Morten Duesund Date: Sun, 24 Aug 2025 15:09:54 +0200 Subject: [PATCH] Switch to light map theme by default with dark mode toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major improvements to map theming and aircraft type display: Map Theme Changes: - Changed default map from dark to light theme (CartoDB Positron) - Added night mode toggle button with sun/moon icons - Both main map and coverage map now switch themes together - Light theme provides better daylight visibility Aircraft Type Display: - Now displays actual ADS-B category directly (e.g., "Medium 34000-136000kg") - Removed guessing/interpretation of aircraft types - Icons still use simplified categories for visual distinction - More accurate and standards-compliant display This provides a cleaner, more professional appearance with the light map and gives users accurate ADS-B category information instead of interpreted types. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- assets/static/index.html | 1 + assets/static/js/app.js | 8 +++ assets/static/js/modules/aircraft-manager.js | 53 +++++++------------- assets/static/js/modules/map-manager.js | 41 +++++++++++++-- 4 files changed, 66 insertions(+), 37 deletions(-) diff --git a/assets/static/index.html b/assets/static/index.html index 4428bdd..2d29e6a 100644 --- a/assets/static/index.html +++ b/assets/static/index.html @@ -79,6 +79,7 @@ + diff --git a/assets/static/js/app.js b/assets/static/js/app.js index 5bf6e94..2189120 100644 --- a/assets/static/js/app.js +++ b/assets/static/js/app.js @@ -103,6 +103,14 @@ class SkyView { }); } + const toggleDarkModeBtn = document.getElementById('toggle-dark-mode'); + if (toggleDarkModeBtn) { + toggleDarkModeBtn.addEventListener('click', () => { + const isDarkMode = this.mapManager.toggleDarkMode(); + toggleDarkModeBtn.innerHTML = isDarkMode ? '☀️ Light Mode' : '🌙 Night Mode'; + }); + } + // Coverage controls const toggleHeatmapBtn = document.getElementById('toggle-heatmap'); const coverageSourceSelect = document.getElementById('coverage-source'); diff --git a/assets/static/js/modules/aircraft-manager.js b/assets/static/js/modules/aircraft-manager.js index bf46082..7bc281d 100644 --- a/assets/static/js/modules/aircraft-manager.js +++ b/assets/static/js/modules/aircraft-manager.js @@ -129,14 +129,14 @@ export class AircraftManager { } createAircraftIcon(aircraft) { - const type = this.getAircraftType(aircraft); - const color = this.getAircraftColor(type); + const iconType = this.getAircraftIconType(aircraft); + const color = this.getAircraftColor(iconType); const size = aircraft.OnGround ? 12 : 16; // Create different SVG shapes based on aircraft type let aircraftPath; - switch (type) { + switch (iconType) { case 'helicopter': // Helicopter shape with rotor disc aircraftPath = ` @@ -197,44 +197,29 @@ export class AircraftManager { } 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, we still need basic categories + // This determines which SVG shape to use if (aircraft.OnGround) return 'ground'; - // Use ADS-B Category field for proper aircraft classification if (aircraft.Category) { const cat = aircraft.Category.toLowerCase(); - // Standard ADS-B aircraft categories - check specific terms first - if (cat.includes('helicopter') || cat.includes('rotorcraft') || cat.includes('gyrocopter')) return 'helicopter'; + // Map to basic icon types for visual representation + if (cat.includes('helicopter') || cat.includes('rotorcraft')) return 'helicopter'; if (cat.includes('military') || cat.includes('fighter') || cat.includes('bomber')) return 'military'; - - // ADS-B weight-based categories (RTCA DO-260B standard) - if (cat.includes('heavy') || cat.includes('super') || cat.includes('136000kg')) return 'cargo'; // Heavy/Super category - if (cat.includes('light') || cat.includes('15500kg') || cat.includes('5700kg') || cat.includes('glider') || cat.includes('ultralight') || cat.includes('sport')) return 'ga'; // Light categories - if (cat.includes('medium') || cat.includes('34000kg')) return 'commercial'; // Medium category - typically commercial airliners - - // Specific aircraft type categories - if (cat.includes('cargo') || cat.includes('transport') || cat.includes('freighter')) return 'cargo'; - if (cat.includes('airliner') || cat.includes('jet') || cat.includes('turboprop')) return 'commercial'; + if (cat.includes('cargo') || cat.includes('heavy') || cat.includes('super')) return 'cargo'; + if (cat.includes('light') || cat.includes('glider') || cat.includes('ultralight')) return 'ga'; } - // Fallback to callsign analysis for classification - if (aircraft.Callsign) { - const cs = aircraft.Callsign.toLowerCase(); - - // Helicopter identifiers - if (cs.includes('heli') || cs.includes('rescue') || cs.includes('medevac') || cs.includes('lifeguard') || - cs.includes('police') || cs.includes('sheriff') || cs.includes('medic')) return 'helicopter'; - - // Military identifiers - if (cs.includes('mil') || cs.includes('army') || cs.includes('navy') || cs.includes('air force') || - cs.includes('usaf') || cs.includes('usmc') || cs.includes('uscg')) return 'military'; - - // Cargo identifiers - if (cs.includes('cargo') || cs.includes('fedex') || cs.includes('ups') || cs.includes('dhl') || - cs.includes('freight') || cs.includes('express')) return 'cargo'; - } - - // Default to commercial for unclassified aircraft + // Default commercial icon for everything else return 'commercial'; } @@ -306,7 +291,7 @@ export class AircraftManager { Country: ${country}
- Type: ${type.charAt(0).toUpperCase() + type.slice(1)} + Type: ${type}
diff --git a/assets/static/js/modules/map-manager.js b/assets/static/js/modules/map-manager.js index e4716c9..6ccc5ff 100644 --- a/assets/static/js/modules/map-manager.js +++ b/assets/static/js/modules/map-manager.js @@ -15,6 +15,11 @@ export class MapManager { // Data references this.sourcesData = new Map(); + + // Map theme + this.isDarkMode = false; + this.currentTileLayer = null; + this.coverageTileLayer = null; } async initializeMap() { @@ -34,8 +39,8 @@ export class MapManager { this.map = L.map('map').setView([origin.latitude, origin.longitude], 10); - // Dark tile layer - L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { + // Light tile layer by default + this.currentTileLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { attribution: '© OpenStreetMap contributors © CARTO', subdomains: 'abcd', maxZoom: 19 @@ -59,7 +64,7 @@ export class MapManager { this.coverageMap = L.map('coverage-map').setView([origin.latitude, origin.longitude], 10); - L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { + this.coverageTileLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { attribution: '© OpenStreetMap contributors' }).addTo(this.coverageMap); } @@ -223,6 +228,36 @@ export class MapManager { return this.showSources; } + toggleDarkMode() { + this.isDarkMode = !this.isDarkMode; + + const lightUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'; + const darkUrl = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'; + const tileUrl = this.isDarkMode ? darkUrl : lightUrl; + + const tileOptions = { + attribution: '© OpenStreetMap contributors © CARTO', + subdomains: 'abcd', + maxZoom: 19 + }; + + // Update main map + if (this.map && this.currentTileLayer) { + this.map.removeLayer(this.currentTileLayer); + this.currentTileLayer = L.tileLayer(tileUrl, tileOptions).addTo(this.map); + } + + // Update coverage map + if (this.coverageMap && this.coverageTileLayer) { + this.coverageMap.removeLayer(this.coverageTileLayer); + this.coverageTileLayer = L.tileLayer(tileUrl, { + attribution: '© OpenStreetMap contributors' + }).addTo(this.coverageMap); + } + + return this.isDarkMode; + } + // Coverage map methods updateCoverageControls() { const select = document.getElementById('coverage-source');