gallery_server_client_s190js/public/js/map/mapboxMarkers.js
2026-10-26 11:00:14 +01:00

960 lines
16 KiB
JavaScript

// public/js/map/mapboxMarkers.js
// MAPBOX MARKERS CONTROLLER
(() => {
"use strict";
let mapboxMap = null;
let eventsMap = null;
let idleRefreshScheduled = false;
const state = {
photos: [],
activities: [],
routes: [],
};
function setMap(map) {
if (!map) {
console.error("[MapboxMarkers] mappa non valida");
return;
}
if (mapboxMap === map) {
connectModules();
if (mapboxMap.isStyleLoaded?.()) {
initializeModules();
}
return;
}
/*
* Scollega soltanto gli eventi del
* controller dalla vecchia mappa.
*
* Non richiama clearHtmlMarkers() qui,
* perché i singoli moduli potrebbero
* puntare ancora a una mappa Mapbox
* già distrutta.
*/
detachMapEvents();
mapboxMap = map;
console.log("[MapboxMarkers] mappa collegata");
connectModules();
setupMapEvents();
if (mapboxMap.isStyleLoaded?.()) {
initializeModules();
} else {
mapboxMap.once("load", handleInitialLoad);
}
}
function connectModules() {
if (!mapboxMap) {
return;
}
window.MapboxPhotoClusters?.setMap?.(mapboxMap);
window.MapboxPhotoMarkers?.setMap?.(mapboxMap);
window.MapboxPhotoSpiderfy?.setMap?.(mapboxMap);
window.MapboxActivityClusters?.setMap?.(mapboxMap);
window.MapboxActivityMarkers?.setMap?.(mapboxMap);
window.MapboxActivitySpiderfy?.setMap?.(mapboxMap);
}
function handleInitialLoad() {
if (!mapboxMap) {
return;
}
initializeModules();
}
function initializeModules() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
const mode =
window.state?.mode;
if (mode === "photos") {
window.MapboxActivityMarkers
?.clear?.();
window.MapboxActivityClusters
?.clear?.();
window.PhotoSource?.update?.(
mapboxMap,
state.photos,
);
window.MapboxPhotoClusters
?.createLayer?.();
return;
}
if (mode === "activities") {
window.MapboxPhotoMarkers
?.clear?.();
window.MapboxPhotoClusters
?.clear?.();
window.ActivitySource?.update?.(
mapboxMap,
state.activities,
);
window.MapboxActivityClusters
?.createLayer?.();
updateRoutesSourceAndLayer();
}
}
function setupMapEvents() {
if (!mapboxMap) {
return;
}
if (eventsMap === mapboxMap) {
return;
}
detachMapEvents();
eventsMap = mapboxMap;
eventsMap.on("style.load", handleStyleLoad);
eventsMap.on("sourcedata", handleSourceData);
eventsMap.on("idle", handleIdle);
}
function detachMapEvents() {
if (!eventsMap) {
idleRefreshScheduled = false;
return;
}
eventsMap.off("style.load", handleStyleLoad);
eventsMap.off("sourcedata", handleSourceData);
eventsMap.off("idle", handleIdle);
eventsMap.off("load", handleInitialLoad);
eventsMap = null;
idleRefreshScheduled = false;
}
function handleStyleLoad() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
initializeModules();
}
function scheduleRenderedRefresh() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
if (idleRefreshScheduled) {
return;
}
idleRefreshScheduled = true;
requestAnimationFrame(() => {
idleRefreshScheduled = false;
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
refreshRenderedMarkers();
});
}
function ensureMapInfrastructure() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
const mode =
window.state?.mode;
if (mode === "photos") {
if (
state.photos.length > 0 &&
!mapboxMap.getSource(
"photos",
)
) {
console.log(
"[MapboxMarkers] creazione source photos",
);
window.PhotoSource?.create?.(
mapboxMap,
state.photos,
);
}
if (
mapboxMap.getSource(
"photos",
) &&
!mapboxMap.getLayer(
"photos-clusters",
)
) {
console.log(
"[MapboxMarkers] creazione layer photos",
);
window.MapboxPhotoClusters
?.createLayer?.();
}
return;
}
if (mode === "activities") {
if (mode === "activities") {
window.ActivitySource?.update?.(
mapboxMap,
state.activities,
);
if (
mapboxMap.getSource(
"activities",
) &&
!mapboxMap.getLayer(
"activities-clusters",
)
) {
console.log(
"[MapboxMarkers] creazione layer activities",
);
window.MapboxActivityClusters
?.createLayer?.();
}
}
if (
mapboxMap.getSource(
"activities",
) &&
!mapboxMap.getLayer(
"activities-clusters",
)
) {
console.log(
"[MapboxMarkers] creazione layer activities",
);
window.MapboxActivityClusters
?.createLayer?.();
}
}
}
function handleIdle() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
if (idleRefreshScheduled) {
return;
}
idleRefreshScheduled = true;
requestAnimationFrame(() => {
idleRefreshScheduled = false;
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
ensureMapInfrastructure();
refreshRenderedMarkers();
});
}
function handleSourceData(event) {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
if (event.dataType !== "source") {
return;
}
const sourceId = event.sourceId;
const mode =
window.state?.mode;
if (
mode === "photos" &&
sourceId !== "photos"
) {
return;
}
if (
mode === "activities" &&
sourceId !== "activities"
) {
return;
}
if (sourceId !== "photos" && sourceId !== "activities") {
return;
}
/*
* Aspettiamo che Mapbox abbia terminato
* di caricare e indicizzare la source.
*/
const sourceLoaded =
typeof mapboxMap.isSourceLoaded === "function"
? mapboxMap.isSourceLoaded(sourceId)
: event.isSourceLoaded === true;
if (!sourceLoaded) {
return;
}
scheduleRenderedRefresh();
}
function refreshRenderedMarkers() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
const mode =
window.state?.mode;
if (mode === "photos") {
window.MapboxActivityMarkers
?.clear?.();
window.MapboxActivityClusters
?.clear?.();
if (
mapboxMap.getSource(
"photos",
) &&
!window.MapboxPhotoSpiderfy
?.isOpen?.()
) {
window.MapboxPhotoClusters
?.update?.();
window.MapboxPhotoMarkers
?.update?.(
state.photos,
);
}
return;
}
if (mode === "activities") {
window.MapboxPhotoMarkers
?.clear?.();
window.MapboxPhotoClusters
?.clear?.();
if (
mapboxMap.getSource(
"activities",
) &&
!window.MapboxActivitySpiderfy
?.isOpen?.()
) {
window.MapboxActivityClusters
?.update?.();
window.MapboxActivityMarkers
?.update?.(
state.activities,
);
}
}
}
function refreshPhotos() {
if (
window.state?.mode !==
"photos"
) {
return;
}
if (!mapboxMap) {
return;
}
if (!mapboxMap.isStyleLoaded?.()) {
return;
}
if (
window.MapboxPhotoSpiderfy
?.isOpen?.()
) {
return;
}
if (
!mapboxMap.getSource(
"photos",
)
) {
return;
}
window.MapboxPhotoClusters
?.update?.();
window.MapboxPhotoMarkers
?.update?.(
state.photos,
);
}
function refreshActivities() {
if (
window.state?.mode !==
"activities"
) {
return;
}
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
if (
window.MapboxActivitySpiderfy
?.isOpen?.()
) {
return;
}
if (
!mapboxMap.getSource(
"activities",
)
) {
return;
}
window.MapboxActivityClusters
?.update?.();
window.MapboxActivityMarkers
?.update?.(
state.activities,
);
}
function refreshRoutes() {
updateRoutesSourceAndLayer();
}
function refreshAll() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
initializeModules();
}
function setPhotos(features = []) {
replaceStateArray(
state.photos,
features,
);
const photosMode =
window.state?.mode ===
"photos";
console.log(
"[MapboxMarkers] photos",
{
features:
state.photos.length,
photosMode,
styleLoaded:
!!mapboxMap
?.isStyleLoaded?.(),
PhotoSource:
!!window.PhotoSource,
PhotoClusters:
!!window
.MapboxPhotoClusters,
},
);
if (!mapboxMap) {
return;
}
if (
!mapboxMap
.isStyleLoaded?.()
) {
return;
}
if (!window.PhotoSource) {
console.warn(
"[MapboxMarkers] PhotoSource non disponibile",
);
return;
}
if (!photosMode) {
window.MapboxPhotoMarkers
?.clear?.();
window.MapboxPhotoClusters
?.clear?.();
const existingSource =
mapboxMap.getSource?.(
"photos",
);
if (existingSource) {
window.PhotoSource.update(
mapboxMap,
[],
);
}
return;
}
window.PhotoSource.update(
mapboxMap,
state.photos,
);
const sourceExists =
!!mapboxMap.getSource?.(
"photos",
);
console.log(
"[MapboxMarkers] photos source",
{
exists:
sourceExists,
features:
state.photos.length,
},
);
if (!sourceExists) {
console.warn(
"[MapboxMarkers] source photos non creata",
);
return;
}
window.MapboxPhotoClusters
?.createLayer?.();
console.log(
"[MapboxMarkers] photos layers",
{
clusters:
!!mapboxMap.getLayer?.(
"photos-clusters",
),
count:
!!mapboxMap.getLayer?.(
"photos-cluster-count",
),
},
);
scheduleRenderedRefresh();
}
function setActivities(features = []) {
replaceStateArray(
state.activities,
features,
);
const activitiesMode =
window.state?.mode ===
"activities";
console.log(
"[MapboxMarkers] activities",
{
features:
state.activities.length,
activitiesMode,
styleLoaded:
!!mapboxMap
?.isStyleLoaded?.(),
},
);
if (!mapboxMap) {
return;
}
if (
!mapboxMap.isStyleLoaded?.()
) {
return;
}
if (!window.ActivitySource) {
console.warn(
"[MapboxMarkers] ActivitySource non disponibile",
);
return;
}
if (!activitiesMode) {
window.MapboxActivityMarkers
?.clear?.();
window.MapboxActivityClusters
?.clear?.();
if (
mapboxMap.getSource?.(
"activities",
)
) {
window.ActivitySource.update(
mapboxMap,
[],
);
}
return;
}
window.ActivitySource.update(
mapboxMap,
state.activities,
);
if (
!mapboxMap.getSource?.(
"activities",
)
) {
console.warn(
"[MapboxMarkers] source activities non creata",
);
return;
}
window.MapboxActivityClusters
?.createLayer?.();
scheduleRenderedRefresh();
}
function setRoutes(features = []) {
replaceStateArray(
state.routes,
features,
);
console.log(
"[MapboxMarkers] routes",
state.routes.length,
);
if (
window.state?.mode !==
"activities"
) {
return;
}
if (
!mapboxMap ||
!mapboxMap
.isStyleLoaded?.()
) {
return;
}
updateRoutesSourceAndLayer();
}
function replaceStateArray(target, features) {
target.length = 0;
if (Array.isArray(features)) {
target.push(...features);
}
}
function updateRoutesSourceAndLayer() {
if (!mapboxMap?.isStyleLoaded?.()) {
return;
}
const data = {
type: "FeatureCollection",
features: state.routes,
};
const source = mapboxMap.getSource("routes");
if (source) {
source.setData(data);
} else {
mapboxMap.addSource("routes", {
type: "geojson",
data,
});
}
if (!mapboxMap.getLayer("routes-line")) {
mapboxMap.addLayer({
id: "routes-line",
type: "line",
source: "routes",
paint: {
"line-color": "#ff6b00",
"line-width": 4,
"line-opacity": 0.8,
},
});
}
}
function clearHtmlMarkers() {
if (window.MapboxPhotoSpiderfy?.isOpen?.()) {
window.MapboxPhotoSpiderfy?.clear?.();
}
window.MapboxPhotoMarkers?.clear?.();
window.MapboxPhotoClusters?.clear?.();
if (window.MapboxActivitySpiderfy?.isOpen?.()) {
window.MapboxActivitySpiderfy?.clear?.();
}
window.MapboxActivityMarkers?.clear?.();
window.MapboxActivityClusters?.clear?.();
}
function clearState() {
// Svuota lo stato interno.
state.photos.length = 0;
state.activities.length = 0;
state.routes.length = 0;
// Rimuove marker, cluster e spiderfy HTML.
clearHtmlMarkers();
// La mappa potrebbe non esistere più.
if (!mapboxMap) {
return;
}
// Evita chiamate Mapbox quando lo stile
// è stato rimosso o non è disponibile.
let styleLoaded = false;
try {
styleLoaded = !!mapboxMap.isStyleLoaded?.();
} catch (error) {
console.warn(
"[MapboxMarkers] mappa non più disponibile durante clearState",
error,
);
return;
}
if (!styleLoaded) {
return;
}
// Svuota la source Photos.
try {
if (mapboxMap.getSource?.("photos")) {
window.PhotoSource?.update?.(mapboxMap, []);
}
} catch (error) {
console.warn("[MapboxMarkers] errore pulizia source photos", error);
}
// Svuota la source Activities.
try {
if (mapboxMap.getSource?.("activities")) {
window.ActivitySource?.update?.(mapboxMap, []);
}
} catch (error) {
console.warn("[MapboxMarkers] errore pulizia source activities", error);
}
// Svuota la source Routes.
try {
const routesSource = mapboxMap.getSource?.("routes");
routesSource?.setData?.({
type: "FeatureCollection",
features: [],
});
} catch (error) {
console.warn("[MapboxMarkers] errore pulizia source routes", error);
}
}
function getPhotos() {
return state.photos;
}
function getActivities() {
return state.activities;
}
function getRoutes() {
return state.routes;
}
function getMap() {
return mapboxMap;
}
function debug() {
const result = {
map: !!mapboxMap,
styleLoaded: !!mapboxMap?.isStyleLoaded?.(),
eventsConnected: eventsMap === mapboxMap,
idleRefreshScheduled,
photos: state.photos.length,
activities: state.activities.length,
routes: state.routes.length,
photoSource: !!mapboxMap?.getSource?.("photos"),
photoClustersLayer: !!mapboxMap?.getLayer?.("photos-clusters"),
photoCountLayer: !!mapboxMap?.getLayer?.("photos-cluster-count"),
photoMarkers: window.MapboxPhotoMarkers?.size?.() ?? 0,
photoClusterMarkers: window.MapboxPhotoClusters?.size?.() ?? 0,
activitySource: !!mapboxMap?.getSource?.("activities"),
activityClustersLayer: !!mapboxMap?.getLayer?.("activities-clusters"),
activityCountLayer: !!mapboxMap?.getLayer?.("activities-cluster-count"),
activityMarkers: window.MapboxActivityMarkers?.size?.() ?? 0,
activityClusterMarkers: window.MapboxActivityClusters?.size?.() ?? 0,
activitySpiderfyOpen: window.MapboxActivitySpiderfy?.isOpen?.() ?? false,
activitySpiderMarkers: window.MapboxActivitySpiderfy?.size?.() ?? 0,
routesSource: !!mapboxMap?.getSource?.("routes"),
routesLayer: !!mapboxMap?.getLayer?.("routes-line"),
};
console.log("[MapboxMarkers DEBUG]", result);
return result;
}
window.debugMapboxMarkers = debug;
window.MapboxMarkers = {
setMap,
getMap,
setPhotos,
setActivities,
setRoutes,
getPhotos,
getActivities,
getRoutes,
refreshAll,
refreshPhotos,
refreshActivities,
refreshRoutes,
clearMarkers: clearHtmlMarkers,
clearState,
debug,
};
})();