411 lines
9 KiB
JavaScript
411 lines
9 KiB
JavaScript
// 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;
|
|
};
|
|
})();
|