photo_server_json_flutter_c.../public/js/mapGlobal.js
2026-03-05 17:07:30 +01:00

250 lines
7.8 KiB
JavaScript

// ===============================
// 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
? `<div class="cluster-collage">${resolvedThumbs.map((t,i)=>`<div class="c${i}"><img src="${t}" alt=""></div>`).join('')}</div>`
: '';
const html = `
<div class="gp-cluster ${cls}" style="width:${size}px;height:${size}px;">
${imgsHtml}
<div class="gp-count"><span>${count}</span></div>
</div>`;
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 = toRad(lat2 - lat1);
const = toRad(lng2 - lng1);
const a = Math.sin(/2) ** 2 +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(/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();
}
};
});