From 0e5381c16f1ba83f3e618d2fae5db78cfb61d6de Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Mon, 20 Jan 2025 11:41:45 +0100 Subject: [PATCH] Fix fog of war console error --- app/javascript/controllers/maps_controller.js | 289 ++++++------------ app/javascript/maps/fog_of_war.js | 16 +- 2 files changed, 106 insertions(+), 199 deletions(-) diff --git a/app/javascript/controllers/maps_controller.js b/app/javascript/controllers/maps_controller.js index 8a0afdf0..0fd4502f 100644 --- a/app/javascript/controllers/maps_controller.js +++ b/app/javascript/controllers/maps_controller.js @@ -98,35 +98,41 @@ export default class extends Controller { Photos: this.photoMarkers }; - // Add this new custom control BEFORE the scale control - const TestControl = L.Control.extend({ - onAdd: (map) => { - const div = L.DomUtil.create('div', 'leaflet-control'); - const distance = this.element.dataset.distance || '0'; - const pointsNumber = this.element.dataset.points_number || '0'; - const unit = this.distanceUnit === 'mi' ? 'mi' : 'km'; - div.innerHTML = `${distance} ${unit} | ${pointsNumber} points`; - div.style.backgroundColor = 'white'; - div.style.padding = '0 5px'; - div.style.marginRight = '5px'; - div.style.display = 'inline-block'; - return div; + // Initialize layer control first + this.layerControl = L.control.layers(this.baseMaps(), controlsLayer).addTo(this.map); + + // Add the toggle panel button + this.addTogglePanelButton(); + + // Check if we should open the panel based on localStorage or URL params + const urlParams = new URLSearchParams(window.location.search); + const isPanelOpen = localStorage.getItem('mapPanelOpen') === 'true'; + const hasDateParams = urlParams.has('start_at') && urlParams.has('end_at'); + + // Always create the panel first + this.toggleRightPanel(); + + // Then hide it if it shouldn't be open + if (!isPanelOpen && !hasDateParams) { + const panel = document.querySelector('.leaflet-right-panel'); + if (panel) { + panel.style.display = 'none'; + localStorage.setItem('mapPanelOpen', 'false'); + } + } + + // Update event handlers + this.map.on('moveend', () => { + if (document.getElementById('fog')) { + this.updateFog(this.markers, this.clearFogRadius); } }); - // Add the test control first - new TestControl({ position: 'bottomright' }).addTo(this.map); - - // Then add scale control - L.control.scale({ - position: 'bottomright', - imperial: this.distanceUnit === 'mi', - metric: this.distanceUnit === 'km', - maxWidth: 120 - }).addTo(this.map) - - // Initialize layer control - this.layerControl = L.control.layers(this.baseMaps(), controlsLayer).addTo(this.map); + this.map.on('zoomend', () => { + if (document.getElementById('fog')) { + this.updateFog(this.markers, this.clearFogRadius); + } + }); // Fetch and draw areas when the map is loaded fetchAndDrawAreas(this.areasLayer, this.apiKey); @@ -205,39 +211,6 @@ export default class extends Controller { if (this.liveMapEnabled) { this.setupSubscription(); } - - // Add the toggle panel button - this.addTogglePanelButton(); - - // Check if we should open the panel based on localStorage or URL params - const urlParams = new URLSearchParams(window.location.search); - const isPanelOpen = localStorage.getItem('mapPanelOpen') === 'true'; - const hasDateParams = urlParams.has('start_at') && urlParams.has('end_at'); - - // Always create the panel first - this.toggleRightPanel(); - - // Then hide it if it shouldn't be open - if (!isPanelOpen && !hasDateParams) { - const panel = document.querySelector('.leaflet-right-panel'); - if (panel) { - panel.style.display = 'none'; - localStorage.setItem('mapPanelOpen', 'false'); - } - } - - // Update event handlers - this.map.on('moveend', () => { - if (document.getElementById('fog')) { - this.updateFog(this.markers, this.clearFogRadius); - } - }); - - this.map.on('zoomend', () => { - if (document.getElementById('fog')) { - this.updateFog(this.markers, this.clearFogRadius); - } - }); } disconnect() { @@ -786,164 +759,84 @@ export default class extends Controller { } updateMapWithNewSettings(newSettings) { - console.log('Updating map settings:', { - newSettings, - currentSettings: this.userSettings, - hasPolylines: !!this.polylinesLayer, - isVisible: this.polylinesLayer && this.map.hasLayer(this.polylinesLayer) - }); - // Show loading indicator const loadingDiv = document.createElement('div'); loadingDiv.className = 'map-loading-overlay'; loadingDiv.innerHTML = '
Updating map...
'; document.body.appendChild(loadingDiv); - // Debounce the heavy operations - const updateLayers = debounce(() => { - try { - // Store current layer visibility states - const layerStates = { - Points: this.map.hasLayer(this.markersLayer), - Routes: this.map.hasLayer(this.polylinesLayer), - Heatmap: this.map.hasLayer(this.heatmapLayer), - "Fog of War": this.map.hasLayer(this.fogOverlay), - "Scratch map": this.map.hasLayer(this.scratchLayer), - Areas: this.map.hasLayer(this.areasLayer), - Photos: this.map.hasLayer(this.photoMarkers) - }; - - // Check if speed_colored_routes setting has changed - if (newSettings.speed_colored_routes !== this.userSettings.speed_colored_routes) { - if (this.polylinesLayer) { - updatePolylinesColors( - this.polylinesLayer, - newSettings.speed_colored_routes - ); - } + try { + // Update settings first + if (newSettings.speed_colored_routes !== this.userSettings.speed_colored_routes) { + if (this.polylinesLayer) { + updatePolylinesColors( + this.polylinesLayer, + newSettings.speed_colored_routes + ); } - - // Update opacity if changed - if (newSettings.route_opacity !== this.userSettings.route_opacity) { - const newOpacity = parseFloat(newSettings.route_opacity) || 0.6; - if (this.polylinesLayer) { - updatePolylinesOpacity(this.polylinesLayer, newOpacity); - } - } - - // Update the local settings - this.userSettings = { ...this.userSettings, ...newSettings }; - this.routeOpacity = parseFloat(newSettings.route_opacity) || 0.6; - this.clearFogRadius = parseInt(newSettings.fog_of_war_meters) || 50; - - // Remove existing layer control - if (this.layerControl) { - this.map.removeControl(this.layerControl); - } - - // Create new controls layer object with proper initialization - const controlsLayer = { - Points: this.markersLayer || L.layerGroup(), - Routes: this.polylinesLayer || L.layerGroup(), - Heatmap: this.heatmapLayer || L.heatLayer([]), - "Fog of War": new this.fogOverlay(), - "Scratch map": this.scratchLayer || L.layerGroup(), - Areas: this.areasLayer || L.layerGroup(), - Photos: this.photoMarkers || L.layerGroup() - }; - - // Add new layer control - this.layerControl = L.control.layers(this.baseMaps(), controlsLayer).addTo(this.map); - - // Restore layer visibility states - Object.entries(layerStates).forEach(([name, wasVisible]) => { - const layer = controlsLayer[name]; - if (wasVisible && layer) { - layer.addTo(this.map); - } else if (layer && this.map.hasLayer(layer)) { - this.map.removeLayer(layer); - } - }); - - } catch (error) { - console.error('Error updating map settings:', error); - console.error(error.stack); - } finally { - // Remove loading indicator after all updates are complete - setTimeout(() => { - document.body.removeChild(loadingDiv); - }, 500); // Give a small delay to ensure all batches are processed } - }, 250); - updateLayers(); - } - - getLayerControlStates() { - const controls = {}; - - this.map.eachLayer((layer) => { - const layerName = this.getLayerName(layer); - - if (layerName) { - controls[layerName] = this.map.hasLayer(layer); + if (newSettings.route_opacity !== this.userSettings.route_opacity) { + const newOpacity = parseFloat(newSettings.route_opacity) || 0.6; + if (this.polylinesLayer) { + updatePolylinesOpacity(this.polylinesLayer, newOpacity); + } } - }); - return controls; - } + // Update the local settings + this.userSettings = { ...this.userSettings, ...newSettings }; + this.routeOpacity = parseFloat(newSettings.route_opacity) || 0.6; + this.clearFogRadius = parseInt(newSettings.fog_of_war_meters) || 50; - getLayerName(layer) { - const controlLayers = { - Points: this.markersLayer, - Routes: this.polylinesLayer, - Heatmap: this.heatmapLayer, - "Fog of War": this.fogOverlay, - Areas: this.areasLayer, - }; + // Store current layer states + const layerStates = { + Points: this.map.hasLayer(this.markersLayer), + Routes: this.map.hasLayer(this.polylinesLayer), + Heatmap: this.map.hasLayer(this.heatmapLayer), + "Fog of War": this.map.hasLayer(this.fogOverlay), + "Scratch map": this.map.hasLayer(this.scratchLayer), + Areas: this.map.hasLayer(this.areasLayer), + Photos: this.map.hasLayer(this.photoMarkers) + }; - for (const [name, val] of Object.entries(controlLayers)) { - if (val && val.hasLayer && layer && val.hasLayer(layer)) // Check if the group layer contains the current layer - return name; - } + // Remove only the layer control + if (this.layerControl) { + this.map.removeControl(this.layerControl); + } - // Direct instance matching - for (const [name, val] of Object.entries(controlLayers)) { - if (val === layer) return name; - } + // Create new controls layer object + const controlsLayer = { + Points: this.markersLayer || L.layerGroup(), + Routes: this.polylinesLayer || L.layerGroup(), + Heatmap: this.heatmapLayer || L.heatLayer([]), + "Fog of War": new this.fogOverlay(), + "Scratch map": this.scratchLayer || L.layerGroup(), + Areas: this.areasLayer || L.layerGroup(), + Photos: this.photoMarkers || L.layerGroup() + }; - return undefined; // Indicate no matching layer name found - } + // Re-add the layer control in the same position + this.layerControl = L.control.layers(this.baseMaps(), controlsLayer).addTo(this.map); - applyLayerControlStates(states) { - console.log('Applying layer states:', states); - - const layerControl = { - Points: this.markersLayer, - Routes: this.polylinesLayer, - Heatmap: this.heatmapLayer, - "Fog of War": this.fogOverlay, - Areas: this.areasLayer, - }; - - for (const [name, isVisible] of Object.entries(states)) { - const layer = layerControl[name]; - console.log(`Processing layer ${name}:`, { layer, isVisible }); - - if (layer) { - if (isVisible && !this.map.hasLayer(layer)) { - console.log(`Adding layer ${name} to map`); - this.map.addLayer(layer); - } else if (!isVisible && this.map.hasLayer(layer)) { - console.log(`Removing layer ${name} from map`); + // Restore layer visibility states + Object.entries(layerStates).forEach(([name, wasVisible]) => { + const layer = controlsLayer[name]; + if (wasVisible && layer) { + layer.addTo(this.map); + } else if (layer && this.map.hasLayer(layer)) { this.map.removeLayer(layer); } - } - } + }); - // Ensure the layer control reflects the current state - this.map.removeControl(this.layerControl); - this.layerControl = L.control.layers(this.baseMaps(), layerControl).addTo(this.map); + } catch (error) { + console.error('Error updating map settings:', error); + console.error(error.stack); + } finally { + // Remove loading indicator + setTimeout(() => { + document.body.removeChild(loadingDiv); + }, 500); + } } createPhotoMarker(photo) { diff --git a/app/javascript/maps/fog_of_war.js b/app/javascript/maps/fog_of_war.js index 482a161e..8e910274 100644 --- a/app/javascript/maps/fog_of_war.js +++ b/app/javascript/maps/fog_of_war.js @@ -25,7 +25,8 @@ export function initializeFogCanvas(map) { export function drawFogCanvas(map, markers, clearFogRadius) { const fog = document.getElementById('fog'); - if (!fog) return; + // Return early if fog element doesn't exist or isn't a canvas + if (!fog || !(fog instanceof HTMLCanvasElement)) return; const ctx = fog.getContext('2d'); if (!ctx) return; @@ -83,12 +84,25 @@ export function createFogOverlay() { return L.Layer.extend({ onAdd: (map) => { initializeFogCanvas(map); + + // Add drag event handlers to update fog during marker movement + map.on('drag', () => { + const fog = document.getElementById('fog'); + if (fog) { + // Update fog canvas position to match map position + const mapPos = map.getContainer().getBoundingClientRect(); + fog.style.left = `${mapPos.left}px`; + fog.style.top = `${mapPos.top}px`; + } + }); }, onRemove: (map) => { const fog = document.getElementById('fog'); if (fog) { fog.remove(); } + // Clean up event listener + map.off('drag'); } }); }