server_photo_s85js/public/js/map/activities/activityMarkers.js
2026-08-11 08:45:04 +02:00

447 lines
7.6 KiB
JavaScript

// 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 = `
<div class="activity-marker-inner"></div>
`;
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,
};
})();