Add heading-up compass and persist fiducial layers

This commit is contained in:
livio committed 2026-10-04 21:40:34 +02:00
1 parent bc037a57d4
commit 812420b86a
6 files changed
+340 -15

No files matched your search

+138 -15
View File
@@ -12,6 +12,7 @@ const lmeState = {
"selectedParcels",
"importedSheets",
"imported",
"compass",
"cadastre"
],
cadastre: null,
@@ -61,6 +62,11 @@ const lmeState = {
gpsTrailStartedAt: null,
gpsWatchId: null,
lastPosition: null,
compassLayer: null,
compassElement: null,
compassVisible: false,
compassMode: "heading",
compassHeading: 0,
infoTimeoutSeconds: 3,
infoTimer: null,
fileImportBound: false,
@@ -185,6 +191,8 @@ window.lmeMap = {
getGpsTrailStats,
setNativeGpsPosition,
setGpsPositionVisible,
setCompassOptions,
setCompassHeading,
setGpsError,
saveGpsMeasurementPoint,
restoreLastTrashItem,
@@ -259,7 +267,13 @@ function init(dotNetReference) {
lmeState.map = L.map("map", {
zoomControl: false,
attributionControl: false
attributionControl: false,
rotate: true,
bearing: 0,
dragRotate: false,
touchRotate: false,
shiftKeyRotate: false,
rotateControl: false
}).setView([41.9028, 12.4964], 17);
L.control.zoom({ position: "bottomright" }).addTo(lmeState.map);
@@ -303,13 +317,19 @@ function init(dotNetReference) {
lmeState.baseLayers.offline = createOfflineTileLayer();
lmeState.currentBase = lmeState.baseLayers.satellite.addTo(lmeState.map);
lmeState.compassLayer = createCompassLayer();
restoreLayerOrder();
Object.values(managedLayerPanes()).forEach(paneName => {
if (!lmeState.map.getPane(paneName)) {
lmeState.map.createPane(paneName);
const noRotation = paneName === "gpsPositionPane" || paneName === "fiducialsPane";
lmeState.map.createPane(paneName, noRotation
? (lmeState.map._norotatePane || lmeState.map._mapPane)
: (lmeState.map._rotatePane || lmeState.map._mapPane));
}
});
const managerHighlightPane = lmeState.map.createPane("managerHighlightPane");
lmeState.map.createPane("drawingsMarkerPane", lmeState.map._norotatePane || lmeState.map._mapPane);
lmeState.map.createPane("gpsAccuracyPane", lmeState.map._rotatePane || lmeState.map._mapPane);
const managerHighlightPane = lmeState.map.createPane("managerHighlightPane", lmeState.map._rotatePane || lmeState.map._mapPane);
managerHighlightPane.style.zIndex = "650";
lmeState.drawingsRenderer = L.canvas({ pane: "drawingsPane", tolerance: 14 });
setLayerOrder(lmeState.layerOrder);
@@ -322,10 +342,10 @@ function init(dotNetReference) {
lmeState.selectedParcelsLayer = L.featureGroup().addTo(lmeState.map);
lmeState.fiducials = L.featureGroup().addTo(lmeState.map);
lmeState.gpsParcelLayer = L.featureGroup().addTo(lmeState.map);
const navigationSheetPane = lmeState.map.createPane("navigationSheetPane");
const navigationSheetPane = lmeState.map.createPane("navigationSheetPane", lmeState.map._rotatePane || lmeState.map._mapPane);
navigationSheetPane.style.zIndex = "465";
navigationSheetPane.style.pointerEvents = "none";
const cadastreZoningPane = lmeState.map.createPane("cadastreZoningPane");
const cadastreZoningPane = lmeState.map.createPane("cadastreZoningPane", lmeState.map._rotatePane || lmeState.map._mapPane);
cadastreZoningPane.style.zIndex = "455";
cadastreZoningPane.style.pointerEvents = "none";
lmeState.navigationSheetLayer = L.layerGroup().addTo(lmeState.map);
@@ -416,6 +436,71 @@ function init(dotNetReference) {
setInfo("Mappa pronta: satellite + catasto AdE");
}
function createCompassLayer() {
return new (L.Layer.extend({
onAdd(map) {
const element = L.DomUtil.create("div", "map-compass");
element.setAttribute("role", "img");
element.setAttribute("aria-label", "Bussola: nord");
element.innerHTML = `<span class="map-compass-north">N</span><span class="map-compass-needle" aria-hidden="true"></span><span class="map-compass-center" aria-hidden="true"></span>`;
L.DomEvent.disableClickPropagation(element);
map.getContainer().appendChild(element);
lmeState.compassElement = element;
updateCompassDisplay();
},
onRemove() {
lmeState.compassElement?.remove();
lmeState.compassElement = null;
}
}))();
}
function setCompassOptions(visible, mode = "heading") {
lmeState.compassVisible = Boolean(visible);
lmeState.compassMode = mode === "fixed" ? "fixed" : "heading";
if (!lmeState.map || !lmeState.compassLayer) return;
if (lmeState.compassVisible) {
lmeState.managerHiddenCategories.delete("compass");
if (!lmeState.map.hasLayer(lmeState.compassLayer)) {
lmeState.compassLayer.addTo(lmeState.map);
}
} else {
lmeState.managerHiddenCategories.add("compass");
if (lmeState.map.hasLayer(lmeState.compassLayer)) {
lmeState.map.removeLayer(lmeState.compassLayer);
}
}
if (!lmeState.compassVisible || lmeState.compassMode === "fixed") {
lmeState.map.setHeading?.(null);
lmeState.map.setBearing?.(0);
} else if (Number.isFinite(lmeState.compassHeading)) {
lmeState.map.setHeading?.(lmeState.compassHeading, { ease: 0.2, deadzone: 1 });
}
updateCompassDisplay();
refreshLayerManager();
}
function setCompassHeading(heading) {
const parsed = Number(heading);
if (!Number.isFinite(parsed)) return;
lmeState.compassHeading = ((parsed % 360) + 360) % 360;
if (lmeState.compassVisible && lmeState.compassMode === "heading") {
lmeState.map?.setHeading?.(lmeState.compassHeading, { ease: 0.2, deadzone: 1 });
}
updateCompassDisplay();
}
function updateCompassDisplay() {
const element = lmeState.compassElement;
if (!element) return;
const rotation = lmeState.compassMode === "fixed" ? 0 : -lmeState.compassHeading;
element.classList.toggle("is-fixed", lmeState.compassMode === "fixed");
element.style.setProperty("--compass-rotation", `${rotation}deg`);
element.setAttribute("aria-label", lmeState.compassMode === "fixed"
? "Bussola: nord fisso in alto"
: `Bussola: nord, orientamento ${Math.round(lmeState.compassHeading)} gradi`);
}
function showSearchResult(latitude, longitude, label) {
const lat = Number(latitude);
const lon = Number(longitude);
@@ -1353,6 +1438,7 @@ function managedLayerPanes() {
fiducials: "fiducialsPane",
importedSheets: "importedSheetPane",
imported: "importedPane",
compass: "compassPane",
cadastre: "cadastrePane"
};
}
@@ -1366,10 +1452,14 @@ function setLayerOrder(order) {
return;
}
normalized.forEach((key, index) => {
const pane = lmeState.map.getPane(panes[key]);
if (pane) {
pane.style.zIndex = String(590 - index * 20);
}
const zIndex = String(590 - index * 20);
const paneNames = [panes[key]];
if (key === "drawings") paneNames.push("drawingsMarkerPane");
if (key === "gpsPosition") paneNames.push("gpsAccuracyPane");
paneNames.forEach(paneName => {
const pane = lmeState.map.getPane(paneName);
if (pane) pane.style.zIndex = zIndex;
});
});
try {
localStorage.setItem("lmeMapLayerOrder", JSON.stringify(normalized));
@@ -1382,6 +1472,10 @@ function restoreLayerOrder() {
const saved = JSON.parse(localStorage.getItem("lmeMapLayerOrder") || "null");
if (Array.isArray(saved)) {
const remaining = saved.filter(key => !["gpsPosition", "gpsTrail", "fiducials"].includes(key));
if (!remaining.includes("compass")) {
const cadastreIndex = remaining.indexOf("cadastre");
remaining.splice(cadastreIndex >= 0 ? cadastreIndex : remaining.length, 0, "compass");
}
lmeState.layerOrder = ["gpsPosition", "gpsTrail", "fiducials", ...remaining];
}
} catch {
@@ -1443,6 +1537,7 @@ function layerManagerCategories() {
{ key: "fiducials", label: "Punti fiduciali", roots: [lmeState.fiducials].filter(Boolean), blocks: true },
{ key: "importedSheets", label: "Cornici e fogli importati", roots: [lmeState.importedSheets].filter(Boolean), blocks: true },
{ key: "imported", label: "Progetti e dati importati", roots: [lmeState.imported].filter(Boolean), blocks: true },
{ key: "compass", label: "Bussola", roots: [lmeState.compassLayer].filter(Boolean), uiOnly: true },
{ key: "cadastre", label: "Layer catastali AdE", roots: [lmeState.cadastre].filter(Boolean) }
];
}
@@ -1480,6 +1575,9 @@ function collectManagedLeafLayers(layer, categoryKey, parent, records) {
}
function managedRecordsForCategory(category) {
if (category.uiOnly) {
return [];
}
const records = [];
category.roots.forEach(root => collectManagedLeafLayers(root, category.key, lmeState.map, records));
lmeState.managerHiddenObjects.forEach(record => {
@@ -2712,7 +2810,7 @@ function addDrawingFeature(feature, select = false) {
style: storedFeatureStyle,
pointToLayer: (item, latlng) => item?.properties?._lmeShape === "circle" && Number(item.properties._lmeRadius) > 0
? L.circle(latlng, { pane: "drawingsPane", renderer: lmeState.drawingsRenderer, radius: Number(item.properties._lmeRadius) })
: L.marker(latlng, { pane: "drawingsPane", draggable: false }),
: L.marker(latlng, { pane: "drawingsMarkerPane", draggable: false }),
onEachFeature: (item, layer) => {
annotateDrawingLayer(layer, item?.properties?._lmeShape);
applyStyleFromProperties(layer);
@@ -3389,6 +3487,13 @@ function setManagedCategoryVisible(category, visible) {
lmeState.fiducialsVisible = visible;
} else if (category.key === "gpsPosition") {
lmeState.gpsPositionVisible = visible;
} else if (category.key === "compass") {
lmeState.compassVisible = visible;
void lmeState.dotNetReference?.invokeMethodAsync("SetCompassFromLayerManager", visible);
if (!visible) {
lmeState.map.setHeading?.(null);
lmeState.map.setBearing?.(0);
}
}
if (visible) {
lmeState.managerHiddenCategories.delete(category.key);
@@ -3992,7 +4097,7 @@ function addProjectMarker(name) {
rememberMapChange();
const center = lmeState.map.getCenter();
const label = (name || "Nuovo POI").trim();
const marker = L.marker(center, { pane: "drawingsPane", draggable: false }).addTo(lmeState.drawings);
const marker = L.marker(center, { pane: "drawingsMarkerPane", draggable: false }).addTo(lmeState.drawings);
marker.lmeManagerLabel = `POI: ${label}`;
marker.feature = {
type: "Feature",
@@ -4697,7 +4802,7 @@ function addMeasurementLabel(targetGroup, latLng, html, labelType, context) {
return;
}
L.marker(latLng, {
pane: context.sourceType === "drawing" ? "drawingsPane" : "importedPane",
pane: "tooltipPane",
interactive: false,
keyboard: false,
icon: L.divIcon({
@@ -4840,7 +4945,7 @@ function circleToPolygon(center, radiusMeters) {
function createProjectSnapshot(includeCadastralParcels) {
const center = lmeState.map.getCenter();
return JSON.stringify({
schemaVersion: 6,
schemaVersion: 7,
view: {
latitude: center.lat,
longitude: center.lng,
@@ -4905,7 +5010,7 @@ function loadProjectSnapshot(raw, projectName) {
fillOpacity: 0.2
});
}
return L.marker(latlng, { pane: "drawingsPane", draggable: false });
return L.marker(latlng, { pane: "drawingsMarkerPane", draggable: false });
},
onEachFeature: (feature, layer) => {
bindStoredFeaturePopup(feature, layer);
@@ -5023,6 +5128,8 @@ function captureDisplayOptions() {
showDrawingMeasurements: lmeState.showDrawingMeasurements,
drawToolsVisible: Boolean(lmeState.drawControl),
gpsPositionVisible: lmeState.gpsPositionVisible,
compassVisible: lmeState.compassVisible,
compassMode: lmeState.compassMode,
selectedParcelsVisible: lmeState.selectedParcelsVisible,
fiducialsVisible: lmeState.fiducialsVisible,
fiducialColor: lmeState.fiducialColor,
@@ -5092,6 +5199,9 @@ function applyProjectDisplayOptions(options) {
setBaseLayer(options.baseLayer);
}
setGpsPositionVisible(options.gpsPositionVisible !== false);
const compassVisible = Boolean(options.compassVisible)
&& !lmeState.managerHiddenCategories.has("compass");
setCompassOptions(compassVisible, options.compassMode);
setSelectedParcelsVisible(options.selectedParcelsVisible !== false);
if (typeof options.fiducialColor === "string") {
setFiducialColor(options.fiducialColor);
@@ -5145,6 +5255,19 @@ function emptyFeatureCollection() {
function managedGroupGeoJson(group, categoryKey) {
const collection = group?.toGeoJSON?.() || emptyFeatureCollection();
const features = [...(collection.features || [])];
const knownIds = new Set(features.map(feature => feature?.properties?._lmeManagerId).filter(Boolean));
lmeState.managerHiddenObjects.forEach(record => {
if (record.categoryKey !== categoryKey || typeof record.layer?.toGeoJSON !== "function") return;
const stored = record.layer.toGeoJSON();
const hiddenFeatures = stored?.type === "FeatureCollection" ? stored.features : [stored];
hiddenFeatures.filter(feature => feature?.type === "Feature").forEach(feature => {
const id = feature?.properties?._lmeManagerId;
if (!id || !knownIds.has(id)) {
features.push(feature);
if (id) knownIds.add(id);
}
});
});
const categoryName = lmeState.managerCategoryNames[categoryKey];
if (categoryName) {
features.forEach(feature => {
@@ -6102,7 +6225,7 @@ function updateGpsPositionLayers(latlng, accuracy) {
lmeState.gpsMarker.lmeManagerId = "gps-position-marker";
lmeState.gpsMarker.lmeManagerLabel = "Posizione GPS";
lmeState.gpsAccuracy = L.circle(latlng, {
pane: "gpsPositionPane",
pane: "gpsAccuracyPane",
radius: accuracy,
color: "#2563eb",
weight: 2,