// public/js/mapGlobal.js // =============================== // MAPPA GLOBALE // Controller generico Leaflet + MarkerCluster // =============================== window.globalMap = null; window.globalMarkers = null; document.addEventListener("DOMContentLoaded", () => { "use strict"; const openBtn = document.getElementById("openMapBtn"); if (!openBtn) { console.error("[mapGlobal] Pulsante #openMapBtn non trovato"); return; } const DISABLE_CLUSTER_AT_ZOOM = 18; const OPEN_STRIP_CHILDREN_MAX = 20; let mapInitializationPromise = null; openBtn.addEventListener("click", openGlobalMap); // =============================== // APRI / CHIUDI MAPPA // =============================== async function openGlobalMap() { const mapDiv = document.getElementById("globalMap"); const terrainDiv = document.getElementById("terrainMap"); const gallery = document.getElementById("gallery"); if (!mapDiv) { console.error("[mapGlobal] Contenitore #globalMap non trovato"); return; } const leafletOpen = mapDiv.classList.contains("open"); const terrainOpen = !!window.MapTerrain3D?.enabled?.() || terrainDiv?.classList.contains("open") || false; const anyMapOpen = leafletOpen || terrainOpen; // ================================= // CHIUDE QUALSIASI MAPPA // ================================= if (anyMapOpen) { if (window.MapTerrain3D?.enabled?.()) { window.MapTerrain3D.disable(); } // Nasconde Leaflet, che disable() // potrebbe aver appena ripristinato. mapDiv.classList.remove("open"); mapDiv.style.display = "none"; mapDiv.style.visibility = "hidden"; mapDiv.style.pointerEvents = "none"; // Nasconde anche Mapbox 3D. if (terrainDiv) { terrainDiv.classList.remove("open"); terrainDiv.style.display = "none"; terrainDiv.style.visibility = "hidden"; terrainDiv.style.pointerEvents = "none"; } // Mostra la galleria. gallery?.classList.remove("hidden"); document.body.classList.remove("map-open"); window.closeBottomSheet?.(); window.clearActivityMapLayers?.(); window.hideActivityRoutesToggle?.(); console.log("[mapGlobal] ritorno alla galleria"); return; } // ================================= // APRE LA MAPPA LEAFLET // ================================= mapDiv.style.display = "block"; mapDiv.style.visibility = "visible"; mapDiv.style.pointerEvents = "auto"; mapDiv.classList.add("open"); gallery?.classList.add("hidden"); document.body.classList.add("map-open"); /* * Attende che il browser renda * visibile il contenitore. */ await new Promise((resolve) => { requestAnimationFrame(resolve); }); try { if (!window.globalMap) { /* * Impedisce due inizializzazioni * contemporanee in caso di click * ripetuti. */ if (!mapInitializationPromise) { mapInitializationPromise = initGlobalMap(); } await mapInitializationPromise; } else { window.globalMap.invalidateSize(); } await window.redrawGlobalMap?.(); const savedMapStyle = localStorage.getItem("selectedMapStyle"); if (savedMapStyle === "terrain3d") { const terrainSuccess = await window.MapBaseLayers?.setStyle?.( window.globalMap, "terrain3d", false, ); console.log("[mapGlobal] ripristino Terrain 3D", { success: terrainSuccess, savedMapStyle, }); } } catch (error) { console.error("[mapGlobal] Errore apertura mappa:", error); mapInitializationPromise = null; mapDiv.classList.remove("open"); gallery?.classList.remove("hidden"); document.body.classList.remove("map-open"); } } window.openGlobalMap = openGlobalMap; // =============================== // INIZIALIZZA MAPPA // =============================== async function initGlobalMap() { console.log("[mapGlobal] Inizializzo Leaflet + MarkerCluster"); if (typeof window.L === "undefined") { throw new Error("Leaflet non è stato caricato"); } if (typeof window.L.markerClusterGroup !== "function") { throw new Error("Leaflet MarkerCluster non è stato caricato"); } window.globalMap = L.map("globalMap", { zoomControl: true, attributionControl: true, }).setView([42.5, 12.5], 6); window.MapEngine?.init(window.globalMap); // =============================== // BASEMAP // =============================== if ( window.MapBaseLayers && typeof window.MapBaseLayers.initialize === "function" ) { /* * Attende che config.js abbia recuperato * il token Mapbox da /config. */ await window.MapBaseLayers.initialize(window.globalMap); } else { console.error( "[mapGlobal] MapBaseLayers non disponibile. " + "Uso OpenStreetMap come fallback.", ); L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { subdomains: "abc", maxZoom: 19, attribution: "© OpenStreetMap contributors", }).addTo(window.globalMap); } // =============================== // MARKER CLUSTER // =============================== window.globalMarkers = L.markerClusterGroup({ showCoverageOnHover: false, spiderfyOnMaxZoom: true, disableClusteringAtZoom: DISABLE_CLUSTER_AT_ZOOM, iconCreateFunction: clusterIconRenderer, }); window.MapEngine?.init?.(window.globalMap, window.globalMarkers); window.globalMarkers.on("clusterclick", onClusterClick); window.globalMap.addLayer(window.globalMarkers); window.MapEngine?.init(window.globalMap, window.globalMarkers); // =============================== // AGGIORNAMENTO BOTTOM SHEET // =============================== window.globalMap.on("moveend", refreshOpenBottomSheet); window.globalMap.on("moveend", () => { if (window.state?.mode === "photos") { window.renderPhotoMapLayer?.(); } }); /* * Leaflet viene creato mentre il contenitore * è appena diventato visibile. Un secondo * invalidateSize evita layout incompleti. */ await new Promise((resolve) => { requestAnimationFrame(resolve); }); window.globalMap.invalidateSize(); console.log("[mapGlobal] Mappa inizializzata", { currentStyle: window.MapBaseLayers?.getCurrentStyle?.() || "fallback-osm", styles: window.MapBaseLayers?.getAvailableStyles?.() || [], }); return window.globalMap; } // =============================== // AGGIORNA BOTTOM SHEET DOPO MOVE // =============================== function refreshOpenBottomSheet() { const bottomSheet = document.getElementById("bottomSheet"); if (!bottomSheet?.classList.contains("open")) { return; } if (!window.globalMap || !window.globalMarkers) { return; } const bounds = window.globalMap.getBounds(); const items = []; window.globalMarkers.eachLayer((marker) => { if (!marker.__item) { return; } if (typeof marker.getLatLng !== "function") { return; } if (bounds.contains(marker.getLatLng())) { items.push(marker.__item); } }); if (items.length > 1) { window.refreshBottomSheet?.(items); } } // =============================== // CLUSTER ICON GENERICO // =============================== function clusterIconRenderer(cluster) { const mode = window.state?.mode; if ( mode === "photos" && typeof window.createPhotoClusterIcon === "function" ) { return window.createPhotoClusterIcon(cluster); } if ( mode === "activities" && typeof window.createActivityClusterIcon === "function" ) { return window.createActivityClusterIcon(cluster); } const count = cluster.getChildCount(); console.warn("[mapGlobal] cluster renderer fallback", { mode, count, photoRenderer: typeof window.createPhotoClusterIcon === "function", activityRenderer: typeof window.createActivityClusterIcon === "function", }); return L.divIcon({ html: `