// =============================== // MAPPA GLOBALE — stile Google Photos Web // =============================== window.globalMap = null; window.globalMarkers = null; // qui sarà un MarkerClusterGroup document.addEventListener("DOMContentLoaded", () => { const openBtn = document.getElementById("openMapBtn"); if (!openBtn) { console.error("openMapBtn non trovato nel DOM"); return; } openBtn.addEventListener("click", openGlobalMap); const RADIUS_PX = 50; const DISABLE_CLUSTER_AT_ZOOM = 18; const OPEN_STRIP_CHILDREN_MAX = 20; async function openGlobalMap() { const mapDiv = document.getElementById("globalMap"); const gallery = document.getElementById("gallery"); if (!mapDiv) { console.error("globalMap DIV non trovato"); return; } const isOpen = mapDiv.classList.contains("open"); if (isOpen) { mapDiv.classList.remove("open"); gallery?.classList.remove("hidden"); window.closeBottomSheet?.(); return; } mapDiv.classList.add("open"); gallery?.classList.add("hidden"); if (window.globalMap) { window.leafletMapInstance = window.globalMap; } await new Promise(r => requestAnimationFrame(r)); let tries = 0; while (mapDiv.getBoundingClientRect().height < 50 && tries < 10) { await new Promise(r => setTimeout(r, 30)); tries++; } if (window.globalMap === null) { console.log("Inizializzo mappa Leaflet + MarkerCluster…"); window.globalMap = L.map("globalMap", { zoomControl: true, attributionControl: true }).setView([42.5, 12.5], 6); L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19 }).addTo(window.globalMap); // CLUSTER con iconCreateFunction in stile Google Photos window.globalMarkers = L.markerClusterGroup({ showCoverageOnHover: false, spiderfyOnMaxZoom: true, disableClusteringAtZoom: DISABLE_CLUSTER_AT_ZOOM, iconCreateFunction: function(cluster) { const count = cluster.getChildCount(); // dimensione scalata: base + sqrt(count) * fattore, con cap massimo const size = Math.min(92, Math.round(28 + Math.sqrt(count) * 6)); // classi per stili (piccolo/medio/grande) const cls = count > 200 ? 'cluster-xl' : (count > 50 ? 'cluster-lg' : (count > 10 ? 'cluster-md' : 'cluster-sm')); // prendi fino a 4 thumbnails dai child markers (se disponibili) const children = cluster.getAllChildMarkers().slice(0, 4); const thumbs = children .map(m => m.__photo?.thub2 || m.__photo?.thub1) .filter(Boolean); // se esiste toAbsoluteUrl, trasformiamo le thumb in URL assoluti const resolvedThumbs = (typeof toAbsoluteUrl === "function") ? thumbs.map(t => absUrl(t, children[0]?.__photo?.user, "thumbs", children[0]?.__photo?.cartella)) : thumbs; // crea collage HTML: fino a 4 immagini in griglia 2x2 const imgsHtml = resolvedThumbs.length ? `
${resolvedThumbs.map((t,i)=>`
`).join('')}
` : ''; const html = `
${imgsHtml}
${count}
`; return L.divIcon({ html, className: 'marker-cluster-wrapper', iconSize: L.point(size, size) }); } }); // Listener "clusterclick": zooma oppure, quando pochi, apri strip window.globalMarkers.on("clusterclick", (a) => { const childMarkers = a.layer.getAllChildMarkers(); const count = childMarkers.length; if (count <= OPEN_STRIP_CHILDREN_MAX || window.globalMap.getZoom() >= DISABLE_CLUSTER_AT_ZOOM - 1) { const photos = childMarkers .map(m => m.__photo) .filter(Boolean); if (photos.length > 1) { window.openBottomSheet?.(photos); } else if (photos.length === 1) { openPhotoModal(photos[0]); } } else { window.globalMap.fitBounds(a.layer.getBounds(), { padding: [60, 60], maxZoom: DISABLE_CLUSTER_AT_ZOOM, animate: true }); } }); window.globalMap.addLayer(window.globalMarkers); // Disegna i marker redrawPhotoMarkers(); } setTimeout(() => window.globalMap?.invalidateSize?.(), 120); } function createPhotoIcon(photo) { const thumb = (typeof toAbsoluteUrl === "function") ? absUrl( photo?.thub2 || photo?.thub1, photo?.user, "thumbs", photo?.cartella ) : (photo?.thub2 || photo?.thub1); return L.icon({ iconUrl: thumb || "", iconSize: [56, 56], iconAnchor: [28, 28], className: "photo-marker" }); } function openPhotoModal(photo) { const thumb = (typeof toAbsoluteUrl === "function") ? absUrl( photo?.thub2 || photo?.thub1 || photo?.path, photo?.user, "thumbs", photo?.cartella ) : (photo?.thub2 || photo?.thub1 || photo?.path); const original = (typeof toAbsoluteUrl === "function") ? absUrl( photo?.path, photo?.user, "original", photo?.cartella ) : photo?.path; window.closeBottomSheet?.(); window.openModal?.(original, thumb, photo); } function radiusMetersAtZoom(latlng, px) { if (!window.globalMap) return 0; const p = window.globalMap.latLngToContainerPoint(latlng); const p2 = L.point(p.x + px, p.y); const ll2 = window.globalMap.containerPointToLatLng(p2); return window.globalMap.distance(latlng, ll2); } function distanceMeters(lat1, lng1, lat2, lng2) { const toRad = d => d * Math.PI / 180; const R = 6371000; const φ1 = toRad(lat1), φ2 = toRad(lat2); const dφ = toRad(lat2 - lat1); const dλ = toRad(lng2 - lng1); const a = Math.sin(dφ/2) ** 2 + Math.cos(φ1) * Math.cos(φ2) * Math.sin(dλ/2) ** 2; return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); } function buildGroupByRadius(lat, lng, data, radiusM) { return data.filter(p => { const plat = +p?.gps?.lat; const plng = +p?.gps?.lng; if (!plat || !plng) return false; return distanceMeters(lat, lng, plat, plng) <= radiusM; }); } function redrawPhotoMarkers() { if (!window.globalMarkers || !window.globalMap) return; window.globalMarkers.clearLayers(); const data = Array.isArray(window.photosData) ? window.photosData : []; data.forEach(photo => { const lat = +photo?.gps?.lat; const lng = +photo?.gps?.lng; if (!lat || !lng) return; const marker = L.marker([lat, lng], { icon: createPhotoIcon(photo), title: photo?.name || "" }); marker.__photo = photo; marker.on("click", () => { const here = L.latLng(lat, lng); const radiusM = radiusMetersAtZoom(here, RADIUS_PX); const dataAll = Array.isArray(window.photosData) ? window.photosData : []; const group = buildGroupByRadius(lat, lng, dataAll, radiusM); if (group.length > 1) { window.openBottomSheet?.(group); } else if (group.length === 1) { openPhotoModal(group[0]); } else { openPhotoModal(photo); } }); window.globalMarkers.addLayer(marker); }); } const originalRefresh = window.refreshGallery; window.refreshGallery = function wrappedRefreshGallery(...args) { try { originalRefresh?.apply(this, args); } catch (_) {} if (window.globalMap && window.globalMarkers) { redrawPhotoMarkers(); } }; });