// public/js/map/activities/activityMarkers.js // =================================== // MAPBOX ACTIVITY MARKERS // =================================== (() => { "use strict"; let mapboxMap = null; const markers = new Map(); const SOURCE_ID = "activities"; // =================================== // SET MAP // =================================== function setMap(map) { mapboxMap = map; console.log("[ActivityMarkers] setMap"); } // =================================== // CREA ELEMENTO ATTIVITÀ // =================================== function createActivityElement(feature) { const el = document.createElement("div"); el.className = "activity-marker"; el.innerHTML = `
`; return el; } // =================================== // CLICK ATTIVITÀ // =================================== function handleActivityClick(feature) { if (!feature) { return; } const id = String( feature.properties?.id ?? "", ); console.log("[ActivityMarkers] click", { id, feature, }); /* * Se esiste un handler globale * per l'apertura dell'attività, * lo utilizziamo. */ if ( typeof window.openActivityMapItem === "function" ) { const items = typeof window.getLocalActivities === "function" ? window.getLocalActivities() : []; const item = items.find( (activity) => String(activity.id) === id, ); if (item) { window.openActivityMapItem(item); } return; } /* * Fallback generico. * * Non facciamo nulla se il progetto * non espone ancora un handler. */ } // =================================== // CREA MARKER // =================================== function createMarker(feature) { if (!mapboxMap) { return null; } if (!feature) { return null; } const id = String( feature.properties?.id ?? "", ); if (!id) { console.warn( "[ActivityMarkers] feature senza id", feature, ); return null; } /* * Evitiamo duplicati. */ if (markers.has(id)) { return markers.get(id); } const coordinates = feature.geometry?.coordinates; if ( !coordinates || coordinates.length < 2 ) { console.warn( "[ActivityMarkers] coordinate non valide", feature, ); return null; } const el = createActivityElement(feature); // ================================= // CLICK // ================================= el.addEventListener( "click", (event) => { event.stopPropagation(); handleActivityClick(feature); }, ); // ================================= // MARKER MAPBOX // ================================= const marker = new mapboxgl.Marker({ element: el, anchor: "center", }) .setLngLat(coordinates) .addTo(mapboxMap); marker.__activityId = id; marker.__originalLngLat = coordinates; markers.set(id, marker); return marker; } // =================================== // ATTIVITÀ VISIBILI // =================================== function getVisibleIds() { if (!mapboxMap) { return new Set(); } if (!mapboxMap.getSource(SOURCE_ID)) { return new Set(); } const sourcePoints = mapboxMap.querySourceFeatures( SOURCE_ID, { filter: [ "!", ["has", "point_count"], ], }, ); return new Set( sourcePoints .map( (feature) => feature.properties?.id, ) .filter( (id) => id !== undefined && id !== null, ) .map((id) => String(id)), ); } // =================================== // UPDATE // =================================== function update(features = []) { if (!mapboxMap) { return; } if (!mapboxMap.getSource(SOURCE_ID)) { return; } const visibleIds = getVisibleIds(); console.log( "[ActivityMarkers] visibility", { features: features.length, visible: visibleIds.size, }, ); // ================================= // RIMUOVI MARKER CLUSTERIZZATI // ================================= for ( const [ id, marker, ] of markers ) { if (!visibleIds.has(id)) { marker.remove(); markers.delete(id); } } // ================================= // CREA / AGGIORNA // ================================= for (const feature of features) { if (!feature) { continue; } const id = String( feature.properties?.id ?? "", ); if (!id) { continue; } /* * Se l'attività è dentro un cluster * non mostriamo il marker singolo. */ if (!visibleIds.has(id)) { continue; } const coordinates = feature.geometry?.coordinates; if ( !coordinates || coordinates.length < 2 ) { continue; } // -------------------------------- // MARKER ESISTENTE // -------------------------------- if (markers.has(id)) { const marker = markers.get(id); marker.setLngLat( coordinates, ); marker.__originalLngLat = coordinates; marker .getElement() .style.display = ""; continue; } // -------------------------------- // CREA MARKER // -------------------------------- createMarker(feature); } // ================================= // RIMOZIONE FINALE // ================================= for ( const [ id, marker, ] of markers ) { if (!visibleIds.has(id)) { marker.remove(); markers.delete(id); } } } // =================================== // GET MARKER // =================================== function get(id) { return markers.get( String(id), ); } // =================================== // GET ALL // =================================== function getAll() { return markers; } // =================================== // REMOVE // =================================== function remove(id) { const key = String(id); const marker = markers.get(key); if (!marker) { return; } marker.remove(); markers.delete(key); } // =================================== // CLEAR // =================================== function clear() { for ( const marker of markers.values() ) { marker.remove(); } markers.clear(); } // =================================== // SIZE // =================================== function size() { return markers.size; } // =================================== // DEBUG // =================================== function debug() { console.log( "[ActivityMarkers] debug", { markers: markers.size, ids: [...markers.keys()], }, ); } // =================================== // API PUBBLICA // =================================== window.MapboxActivityMarkers = { setMap, update, createMarker, getVisibleIds, get, getAll, remove, clear, size, debug, }; })();