// public/js/mapPhotosLayer.js // =============================== // LAYER MAPPA FOTO // =============================== (function () { let visiblePhotoMarkers = new Map(); let mapPhotoItems = []; let mapPhotoById = new Map(); let mapPhotoLoadPromise = null; let mapPhotoLoadGeneration = 0; let mapPhotoIndexLoaded = false; let mapPhotoBoundsKey = null; let mapPhotoRequestedBoundsKey = null; function getPhotoMapBounds() { const bounds = window.MapEngine?.getBounds?.() || window.globalMap?.getBounds?.(); if (!bounds) return null; const west = bounds.getWest(); const south = bounds.getSouth(); const east = bounds.getEast(); const north = bounds.getNorth(); if (![west, south, east, north].every(Number.isFinite)) { return null; } return { west, south, east, north, key: [west, south, east, north] .map(v => v.toFixed(5)).join(","), }; } async function loadMapPhotoIndex() { const bounds = getPhotoMapBounds(); if (!bounds) return false; if (mapPhotoIndexLoaded && mapPhotoBoundsKey === bounds.key) { return true; } if ( mapPhotoLoadPromise && mapPhotoRequestedBoundsKey === bounds.key ) { return mapPhotoLoadPromise; } const token = localStorage.getItem("token"); if (!token) return false; const generation = ++mapPhotoLoadGeneration; mapPhotoRequestedBoundsKey = bounds.key; mapPhotoIndexLoaded = false; const params = new URLSearchParams({ west: String(bounds.west), south: String(bounds.south), east: String(bounds.east), north: String(bounds.north), limit: "2500", }); if (localStorage.getItem("showDeleted") === "1") { params.set("showDeleted", "1"); } const promise = (async () => { try { const response = await fetch( "/photos/map?" + params.toString(), { headers: { Authorization: "Bearer " + token }, } ); if (!response.ok) { throw new Error("HTTP " + response.status); } const data = await response.json(); if (generation !== mapPhotoLoadGeneration) return false; if (!Array.isArray(data.items)) { throw new Error("Risposta map senza array items"); } mapPhotoItems = data.items.filter(item => Number.isFinite(Number(item.gps?.lat)) && Number.isFinite(Number(item.gps?.lng)) ); mapPhotoById = new Map( mapPhotoItems.map(item => [String(item.id), item]) ); window.MapSpatialIndex.build(mapPhotoItems); mapPhotoBoundsKey = bounds.key; mapPhotoIndexLoaded = true; console.log("[mapPhotosLayer] area caricata", { foto: mapPhotoItems.length, truncated: Boolean(data.truncated), bounds: bounds.key, }); if (data.truncated) { console.warn( "[mapPhotosLayer] raggiunto il limite di risultati; ingrandisci la mappa" ); } window.renderPhotoMapLayer?.(); return true; } catch (error) { if (generation === mapPhotoLoadGeneration) { console.error("[mapPhotosLayer] caricamento area fallito:", error); } return false; } finally { if (generation === mapPhotoLoadGeneration) { mapPhotoLoadPromise = null; } } })(); mapPhotoLoadPromise = promise; return promise; } window.getMapPhotoItem = function (id) { return mapPhotoById.get(String(id)) || null; }; window.refreshMapPhotoIndex = function () { mapPhotoLoadGeneration++; mapPhotoLoadPromise = null; mapPhotoIndexLoaded = false; mapPhotoBoundsKey = null; mapPhotoRequestedBoundsKey = null; mapPhotoItems = []; mapPhotoById.clear(); window.MapSpatialIndex?.build?.([]); window.clearPhotoMapLayer?.(false); return loadMapPhotoIndex(); }; window.clearPhotoMapLayer = function (clearFactoryCache = false) { if (window.globalMarkers) { visiblePhotoMarkers = MapLayerManager.sync( window.globalMarkers, visiblePhotoMarkers, [], ); } else { visiblePhotoMarkers.clear(); } if (clearFactoryCache) { PhotoMarkerFactory.clear(); } console.log( "[mapPhotosLayer] marker Leaflet puliti", { visible: visiblePhotoMarkers.size, cache: PhotoMarkerFactory.size(), clusterLayers: window.globalMarkers ?.getLayers?.() ?.length ?? null, }, ); }; window.photoMapDebug = { cacheSize() { return PhotoMarkerFactory.size(); }, visibleSize() { return visiblePhotoMarkers.size; }, clearCache() { window.clearPhotoMapLayer?.( true, ); }, }; // =============================== // APRI FOTO // =============================== window.openPhotoMapItem = function (item) { const thumb = toAbsoluteUrl( item.thub1 || item.thub2 || item.path, item.name, "thumbs", item.cartella, ); const original = toAbsoluteUrl( item.path, item.name, "original", item.cartella, ); window.openModal(item); //window.openModal?.(original, thumb, item); }; // =============================== // RENDER FOTO // =============================== window.renderPhotoMapLayer = function () { if ( window.state?.mode !== "photos" ) { return; } if (!window.globalMap) { return; } if (!window.MapEngine?.isReady?.()) { console.warn("[mapPhotosLayer] MapEngine non pronto"); return; } const bounds = window.MapEngine?.getBounds?.() || window.globalMap.getBounds(); const area = getPhotoMapBounds(); if ( !area || !mapPhotoIndexLoaded || mapPhotoBoundsKey !== area.key ) { loadMapPhotoIndex(); return; } const items = MapSpatialIndex.search(bounds); const markers = []; for (const entry of items) { const lat = entry.lat; const lng = entry.lng; const photo = entry.item; if (!Number.isFinite(lat) || !Number.isFinite(lng)) { continue; } const marker = PhotoMarkerFactory.get(photo); markers.push(marker); } visiblePhotoMarkers = MapLayerManager.sync( window.globalMarkers, visiblePhotoMarkers, markers, ); for (const marker of markers) { if ( marker && !window.globalMarkers ?.hasLayer?.(marker) ) { window.globalMarkers ?.addLayer?.(marker); } } console.log( "[mapPhotosLayer] marker sincronizzati", { visible: visiblePhotoMarkers.size, clusterLayers: window.globalMarkers ?.getLayers?.() ?.length ?? null, }, ); if (window.MapTerrain3D?.enabled?.()) { const features = window.getPhotoMapGeoJSON?.() || []; window.MapboxMarkers?.setPhotos(features); } console.log( "[mapPhotosLayer]", "visibili:", visiblePhotoMarkers.size, "cache:", PhotoMarkerFactory.size(), "indice:", items.length, ); }; // =============================== // GEOJSON PER MAPBOX // =============================== window.getPhotoMapGeoJSON = function () { const area = getPhotoMapBounds(); if ( !area || !mapPhotoIndexLoaded || mapPhotoBoundsKey !== area.key ) { loadMapPhotoIndex(); return []; } const features = []; let items = []; /* if (window.MapEngine?.isMapbox?.()) { items = MapSpatialIndex.search({ getWest: () => -180, getEast: () => 180, getSouth: () => -90, getNorth: () => 90, }); } else { const bounds = window.MapEngine?.getBounds?.(); items = bounds ? MapSpatialIndex.search(bounds) : []; } */ const bounds = window.MapEngine?.getBounds?.(); items = bounds ? MapSpatialIndex.search(bounds) : []; console.log("[PhotoGeoJSON DEBUG bounds]", { west: bounds?.getWest?.(), east: bounds?.getEast?.(), south: bounds?.getSouth?.(), north: bounds?.getNorth?.(), }); console.log("[PhotoGeoJSON DEBUG items]", items.length); for (const entry of items) { const lat = entry.lat; const lng = entry.lng; const photo = entry.item; if (!Number.isFinite(lat) || !Number.isFinite(lng)) { continue; } features.push({ type: "Feature", geometry: { type: "Point", coordinates: [lng, lat], }, /* properties: { id: photo.id, type: "photo", name: photo.name || "", },*/ properties: { id: photo.id, type: "photo", name: photo.name || "", thumb: toAbsoluteUrl( photo.thub1 || photo.thub2 || photo.path, photo.name, "thumbs", photo.cartella, ), }, }); } return features; }; })();