// 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"; /* * Per ora usiamo un marker semplice. * * In seguito possiamo sostituirlo con: * - icona attività * - colore per categoria * - stato attività * - durata * - popup * - icona personalizzata */ el.style.width = "18px"; el.style.height = "18px"; el.style.borderRadius = "50%"; el.style.background = "#008cff"; el.style.border = "2px solid #ffffff"; el.style.boxSizing = "border-box"; el.style.cursor = "pointer"; el.style.boxShadow = "0 1px 4px rgba(0,0,0,0.35)"; return el; } // =================================== // CLICK ATTIVITÀ // =================================== function handleActivityClick(feature) { if (!feature) { return; } const id = String( feature.properties?.id ?? "", ); console.log( "[ActivityMarkers] click", { id, feature, }, ); /* * API futura. * * Quando avremo il modello definitivo * delle attività possiamo collegare qui * il relativo pannello/dettaglio. */ 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 temporaneo. */ console.log( "[ActivityMarkers] attività selezionata:", id, ); } // =================================== // CREA MARKER // =================================== function createMarker(feature) { if (!mapboxMap) { return null; } if (!feature) { return null; } const id = String( feature.properties?.id ?? "", ); if (!id) { console.warn( "[ActivityMarkers] attività senza id", feature, ); return null; } // --------------------------------- // EVITA DUPLICATI // --------------------------------- if (markers.has(id)) { const marker = markers.get(id); if ( feature.geometry?.coordinates ) { marker.setLngLat( feature.geometry.coordinates, ); } return marker; } // --------------------------------- // ELEMENTO HTML // --------------------------------- const el = createActivityElement(feature); // --------------------------------- // CLICK // --------------------------------- el.addEventListener( "click", (event) => { event.stopPropagation(); handleActivityClick(feature); }, ); // --------------------------------- // MAPBOX MARKER // --------------------------------- const marker = new mapboxgl.Marker({ element: el, anchor: "center", }) .setLngLat( feature.geometry.coordinates, ) .addTo(mapboxMap); marker.__activityId = id; marker.__originalLngLat = feature.geometry.coordinates; markers.set(id, marker); return marker; } // =================================== // GET VISIBLE IDS // =================================== 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 ATTIVITÀ CLUSTERIZZATE // =================================== for ( const [ id, marker, ] of markers ) { if ( !visibleIds.has(String(id)) ) { marker.remove(); markers.delete(id); } } // =================================== // CREA / AGGIORNA // =================================== for (const feature of features) { if (!feature) { continue; } if ( !feature.geometry?.coordinates ) { continue; } const id = String( feature.properties?.id ?? "", ); if (!id) { continue; } /* * Se l'attività è dentro un cluster, * non deve avere il marker HTML. */ if (!visibleIds.has(id)) { continue; } // --------------------------------- // MARKER ESISTENTE // --------------------------------- if (markers.has(id)) { const marker = markers.get(id); marker.setLngLat( feature.geometry.coordinates, ); marker.__originalLngLat = feature.geometry.coordinates; continue; } // --------------------------------- // CREA // --------------------------------- createMarker(feature); } // =================================== // RIMOZIONE FINALE // =================================== for ( const [ id, marker, ] of markers ) { if ( !visibleIds.has(String(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, }; })();