358 lines
9 KiB
JavaScript
358 lines
9 KiB
JavaScript
// 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: `<div class="activity-cluster">` + `${count}` + `</div>`,
|
|
|
|
className: "marker-cluster-wrapper",
|
|
|
|
iconSize: L.point(48, 48),
|
|
});
|
|
}
|
|
|
|
// ===============================
|
|
// CLICK SU CLUSTER
|
|
// ===============================
|
|
function onClusterClick(event) {
|
|
if (!event?.layer || !window.globalMap) {
|
|
return;
|
|
}
|
|
|
|
const markers = event.layer.getAllChildMarkers();
|
|
|
|
const items = markers.map((marker) => marker.__item).filter(Boolean);
|
|
|
|
const count = markers.length;
|
|
|
|
if (
|
|
count <= OPEN_STRIP_CHILDREN_MAX ||
|
|
window.globalMap.getZoom() >= DISABLE_CLUSTER_AT_ZOOM - 1
|
|
) {
|
|
if (items.length > 1) {
|
|
window.currentOpenCluster = event.layer;
|
|
|
|
window.openBottomSheet?.(items);
|
|
} else if (items.length === 1) {
|
|
window.openMapItem?.(items[0]);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
window.globalMap.fitBounds(event.layer.getBounds(), {
|
|
padding: [60, 60],
|
|
maxZoom: DISABLE_CLUSTER_AT_ZOOM,
|
|
animate: true,
|
|
});
|
|
}
|
|
|
|
// ===============================
|
|
// APRI ELEMENTO DALLA MAPPA
|
|
// ===============================
|
|
window.openMapItem = function (item) {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
window.closeBottomSheet?.();
|
|
|
|
if (window.state?.mode === "photos") {
|
|
window.openPhotoMapItem?.(item);
|
|
} else {
|
|
window.openActivityModal?.(item);
|
|
}
|
|
};
|
|
|
|
// ===============================
|
|
// RIDISEGNA MARKER E ATTIVITÀ
|
|
// ===============================
|
|
window.redrawGlobalMap = async function () {
|
|
if (!window.globalMap || !window.globalMarkers) {
|
|
return;
|
|
}
|
|
|
|
window.globalMarkers.clearLayers();
|
|
|
|
if (typeof window.clearActivityMapLayers === "function") {
|
|
window.clearActivityMapLayers();
|
|
}
|
|
|
|
if (window.state?.mode === "photos") {
|
|
window.hideActivityRoutesToggle?.();
|
|
|
|
if (typeof window.renderPhotoMapLayer === "function") {
|
|
await window.renderPhotoMapLayer();
|
|
}
|
|
} else {
|
|
if (typeof window.renderActivityMapLayer === "function") {
|
|
await window.renderActivityMapLayer();
|
|
}
|
|
}
|
|
};
|
|
|
|
// ===============================
|
|
// AGGANCIA REFRESH GALLERY
|
|
// ===============================
|
|
const originalRefresh = window.refreshGallery;
|
|
|
|
if (typeof originalRefresh === "function") {
|
|
window.refreshGallery = function (...args) {
|
|
const result = originalRefresh.apply(this, args);
|
|
|
|
// I dati sono cambiati: invalida l'indice spaziale
|
|
window.MapSpatialIndex?.clear?.();
|
|
|
|
const mapDiv = document.getElementById("globalMap");
|
|
|
|
if (mapDiv?.classList.contains("open")) {
|
|
Promise.resolve(window.redrawGlobalMap?.()).catch((error) => {
|
|
console.error("[mapGlobal] Errore redraw dopo refresh:", error);
|
|
});
|
|
}
|
|
|
|
return result;
|
|
};
|
|
} else {
|
|
console.warn(
|
|
"[mapGlobal] refreshGallery non disponibile " +
|
|
"durante l'inizializzazione",
|
|
);
|
|
}
|
|
});
|