// 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 gallery = document.getElementById("gallery"); if (!mapDiv) { console.error("[mapGlobal] Contenitore #globalMap non trovato"); return; } const isOpen = mapDiv.classList.contains("open"); // Chiude la mappa. if (isOpen) { mapDiv.classList.remove("open"); gallery?.classList.remove("hidden"); window.closeBottomSheet?.(); window.clearActivityMapLayers?.(); return; } // Apre la mappa. mapDiv.classList.add("open"); gallery?.classList.add("hidden"); // 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 clic ripetuti. */ if (!mapInitializationPromise) { mapInitializationPromise = initGlobalMap(); } await mapInitializationPromise; } else { window.globalMap.invalidateSize(); } await window.redrawGlobalMap?.(); } catch (error) { console.error("[mapGlobal] Errore apertura mappa:", error); mapInitializationPromise = null; } } 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) { if ( window.state?.mode === "photos" && typeof window.createPhotoClusterIcon === "function" ) { return window.createPhotoClusterIcon(cluster); } if ( window.state?.mode === "activities" && typeof window.createActivityClusterIcon === "function" ) { return window.createActivityClusterIcon(cluster); } const count = cluster.getChildCount(); return L.divIcon({ html: `