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

394 lines
7.1 KiB
JavaScript

// public/js/mapBaseLayers.js
// Basemap 2D Leaflet e selettore livelli.
(() => {
"use strict";
const STORAGE_KEY = "selectedMapStyle";
const DEFAULT_STYLE = "osm";
let currentLayer = null;
let layers = {};
let layerControl = null;
let initializedMap = null;
function getMapboxToken() {
return window.AppConfig?.mapboxToken || "";
}
function createOsmLayer() {
return L.tileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{
subdomains: "abc",
maxZoom: 19,
attribution:
"© OpenStreetMap contributors"
}
);
}
function createOpenTopoLayer() {
return L.tileLayer(
"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
{
subdomains: "abc",
maxZoom: 17,
attribution:
"Map data: © OpenStreetMap contributors, SRTM | " +
"Map style: © OpenTopoMap"
}
);
}
function createMapboxLayer(styleId, token) {
if (!token) {
return null;
}
const encodedToken =
encodeURIComponent(token);
const tileUrl =
`https://api.mapbox.com/styles/v1/mapbox/${styleId}` +
`/tiles/512/{z}/{x}/{y}@2x` +
`?access_token=${encodedToken}`;
return L.tileLayer(tileUrl, {
tileSize: 512,
zoomOffset: -1,
maxZoom: 22,
attribution:
"© Mapbox © OpenStreetMap contributors"
});
}
function createLayers() {
const token = getMapboxToken();
const result = {
osm: {
label: "OpenStreetMap",
layer: createOsmLayer()
},
opentopo: {
label: "Topografica",
layer: createOpenTopoLayer()
}
};
if (!token) {
console.warn(
"[mapBaseLayers] Token Mapbox non configurato. " +
"Saranno disponibili solamente OpenStreetMap " +
"e OpenTopoMap."
);
return result;
}
const mapboxDefinitions = {
mapboxStreets: {
label: "Mapbox Streets",
styleId: "streets-v12"
},
mapboxOutdoors: {
label: "Mapbox Outdoors",
styleId: "outdoors-v12"
},
mapboxSatellite: {
label: "Mapbox Satellite",
styleId: "satellite-v9"
},
mapboxSatelliteStreets: {
label: "Mapbox Satellite + strade",
styleId: "satellite-streets-v12"
}
};
for (
const [id, definition]
of Object.entries(mapboxDefinitions)
) {
const layer = createMapboxLayer(
definition.styleId,
token
);
if (!layer) {
continue;
}
result[id] = {
label: definition.label,
layer
};
}
return result;
}
function getSavedStyle() {
const savedStyle =
localStorage.getItem(STORAGE_KEY);
if (savedStyle && layers[savedStyle]) {
return savedStyle;
}
return DEFAULT_STYLE;
}
function findStyleIdByLayer(selectedLayer) {
for (
const [styleId, definition]
of Object.entries(layers)
) {
if (definition.layer === selectedLayer) {
return styleId;
}
}
return null;
}
function dispatchStyleChange(
styleId,
label
) {
window.dispatchEvent(
new CustomEvent("mapstylechange", {
detail: {
engine: "leaflet",
styleId,
label
}
})
);
}
function setStyle(
map,
styleId,
save = true
) {
if (!map) {
console.error(
"[mapBaseLayers] Istanza Leaflet mancante"
);
return false;
}
const selectedStyle =
layers[styleId]
? styleId
: DEFAULT_STYLE;
const definition =
layers[selectedStyle];
if (!definition?.layer) {
console.error(
"[mapBaseLayers] Basemap non trovata:",
styleId
);
return false;
}
if (
currentLayer &&
map.hasLayer(currentLayer)
) {
map.removeLayer(currentLayer);
}
currentLayer = definition.layer;
currentLayer.addTo(map);
if (save) {
localStorage.setItem(
STORAGE_KEY,
selectedStyle
);
}
dispatchStyleChange(
selectedStyle,
definition.label
);
return true;
}
function createControl(map) {
if (layerControl) {
map.removeControl(layerControl);
layerControl = null;
}
const baseLayers = {};
for (
const definition
of Object.values(layers)
) {
baseLayers[definition.label] =
definition.layer;
}
layerControl = L.control.layers(
baseLayers,
null,
{
position: "topright",
collapsed: true
}
);
layerControl.addTo(map);
map.on(
"baselayerchange",
handleBaseLayerChange
);
}
function handleBaseLayerChange(event) {
const styleId =
findStyleIdByLayer(event.layer);
if (!styleId) {
return;
}
currentLayer = event.layer;
localStorage.setItem(
STORAGE_KEY,
styleId
);
dispatchStyleChange(
styleId,
event.name
);
}
async function waitForConfiguration() {
if (window.AppConfigPromise) {
try {
await window.AppConfigPromise;
} catch (error) {
console.error(
"[mapBaseLayers] Errore configurazione:",
error
);
}
}
return window.AppConfig || {};
}
async function initialize(map) {
if (!map) {
console.error(
"[mapBaseLayers] Istanza mappa non disponibile"
);
return false;
}
if (typeof window.L === "undefined") {
console.error(
"[mapBaseLayers] Leaflet non disponibile"
);
return false;
}
await waitForConfiguration();
/*
* Evita una doppia inizializzazione sulla stessa
* istanza Leaflet.
*/
if (
initializedMap === map &&
Object.keys(layers).length > 0
) {
return true;
}
/*
* Se il modulo viene inizializzato su una nuova mappa,
* rimuove il listener dalla precedente.
*/
if (
initializedMap &&
initializedMap !== map
) {
initializedMap.off(
"baselayerchange",
handleBaseLayerChange
);
}
initializedMap = map;
layers = createLayers();
const initialStyle =
getSavedStyle();
const styleApplied = setStyle(
map,
initialStyle,
false
);
createControl(map);
console.log(
"[mapBaseLayers] Basemap inizializzate",
{
currentStyle: getCurrentStyle(),
availableStyles:
getAvailableStyles().map(
style => style.id
),
mapboxConfigured:
Boolean(getMapboxToken())
}
);
return styleApplied;
}
function getCurrentStyle() {
return (
findStyleIdByLayer(currentLayer) ||
DEFAULT_STYLE
);
}
function getAvailableStyles() {
return Object.entries(layers).map(
([id, definition]) => ({
id,
label: definition.label
})
);
}
window.MapBaseLayers = Object.freeze({
initialize,
setStyle,
getCurrentStyle,
getAvailableStyles
});
})();