Add draggable compass and restore project fiducials

This commit is contained in:
livio committed 2026-10-04 23:46:22 +02:00
1 parent aa389f7581
commit a15c8e0b36
4 files changed
+203 -31

No files matched your search

+147 -10
View File
@@ -65,8 +65,11 @@ const lmeState = {
compassLayer: null,
compassElement: null,
compassVisible: false,
compassMode: "heading",
compassMode: "fixed",
compassHeading: 0,
compassPosition: null,
compassDragCleanup: null,
compassResizeHandler: null,
infoTimeoutSeconds: 3,
infoTimer: null,
fileImportBound: false,
@@ -321,7 +324,7 @@ function init(dotNetReference) {
restoreLayerOrder();
Object.values(managedLayerPanes()).forEach(paneName => {
if (!lmeState.map.getPane(paneName)) {
const noRotation = paneName === "gpsPositionPane" || paneName === "fiducialsPane";
const noRotation = paneName === "gpsPositionPane";
lmeState.map.createPane(paneName, noRotation
? (lmeState.map._norotatePane || lmeState.map._mapPane)
: (lmeState.map._rotatePane || lmeState.map._mapPane));
@@ -442,22 +445,150 @@ function createCompassLayer() {
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>`;
element.setAttribute("title", "Trascina per spostare la bussola");
element.innerHTML = `<span class="map-compass-rose" aria-hidden="true">
<span class="map-compass-direction is-north" style="--direction-angle:0deg;--label-counter:0deg"><span>N</span></span>
<span class="map-compass-direction is-intermediate" style="--direction-angle:45deg;--label-counter:-45deg"><span>NE</span></span>
<span class="map-compass-direction" style="--direction-angle:90deg;--label-counter:-90deg"><span>E</span></span>
<span class="map-compass-direction is-intermediate" style="--direction-angle:135deg;--label-counter:-135deg"><span>SE</span></span>
<span class="map-compass-direction" style="--direction-angle:180deg;--label-counter:-180deg"><span>S</span></span>
<span class="map-compass-direction is-intermediate" style="--direction-angle:225deg;--label-counter:-225deg"><span>SW</span></span>
<span class="map-compass-direction" style="--direction-angle:270deg;--label-counter:-270deg"><span>W</span></span>
<span class="map-compass-direction is-intermediate" style="--direction-angle:315deg;--label-counter:-315deg"><span>NW</span></span>
<span class="map-compass-needle"></span>
</span><span class="map-compass-center" aria-hidden="true"></span>`;
L.DomEvent.disableClickPropagation(element);
L.DomEvent.disableScrollPropagation(element);
map.getContainer().appendChild(element);
lmeState.compassElement = element;
restoreCompassPosition();
bindCompassDragging(element);
lmeState.compassResizeHandler = () => applyCompassPosition();
map.on("resize", lmeState.compassResizeHandler);
requestAnimationFrame(() => applyCompassPosition());
updateCompassDisplay();
},
onRemove() {
onRemove(map) {
lmeState.compassDragCleanup?.();
lmeState.compassDragCleanup = null;
if (lmeState.compassResizeHandler) {
map.off("resize", lmeState.compassResizeHandler);
lmeState.compassResizeHandler = null;
}
lmeState.compassElement?.remove();
lmeState.compassElement = null;
}
}))();
}
function setCompassOptions(visible, mode = "heading") {
function restoreCompassPosition() {
if (lmeState.compassPosition) return;
try {
const saved = JSON.parse(localStorage.getItem("lmeCompassPosition") || "null");
const x = Number(saved?.x);
const y = Number(saved?.y);
if (Number.isFinite(x) && Number.isFinite(y)) {
lmeState.compassPosition = {
x: Math.max(0, Math.min(1, x)),
y: Math.max(0, Math.min(1, y))
};
}
} catch {
}
}
function applyCompassPosition() {
const element = lmeState.compassElement;
const container = lmeState.map?.getContainer();
if (!element || !container) return;
const width = container.clientWidth;
const height = container.clientHeight;
const elementWidth = element.offsetWidth;
const elementHeight = element.offsetHeight;
if (!width || !height || !elementWidth || !elementHeight) return;
const halfWidth = elementWidth / 2;
const halfHeight = elementHeight / 2;
const position = lmeState.compassPosition || {
x: (12 + halfWidth) / width,
y: 0.5
};
const centerX = Math.max(halfWidth + 4, Math.min(width - halfWidth - 4, position.x * width));
const centerY = Math.max(halfHeight + 4, Math.min(height - halfHeight - 4, position.y * height));
element.style.left = `${centerX - halfWidth}px`;
element.style.top = `${centerY - halfHeight}px`;
element.style.transform = "none";
}
function bindCompassDragging(element) {
let pointerId = null;
let offsetX = 0;
let offsetY = 0;
const move = event => {
if (event.pointerId !== pointerId) return;
event.preventDefault();
event.stopPropagation();
const container = lmeState.map?.getContainer();
if (!container) return;
const containerRect = container.getBoundingClientRect();
const elementWidth = element.offsetWidth;
const elementHeight = element.offsetHeight;
const left = event.clientX - containerRect.left - offsetX;
const top = event.clientY - containerRect.top - offsetY;
const centerX = Math.max(elementWidth / 2 + 4,
Math.min(containerRect.width - elementWidth / 2 - 4, left + elementWidth / 2));
const centerY = Math.max(elementHeight / 2 + 4,
Math.min(containerRect.height - elementHeight / 2 - 4, top + elementHeight / 2));
lmeState.compassPosition = {
x: centerX / containerRect.width,
y: centerY / containerRect.height
};
applyCompassPosition();
};
const finish = event => {
if (event.pointerId !== pointerId) return;
event.preventDefault();
event.stopPropagation();
element.classList.remove("is-dragging");
if (element.hasPointerCapture?.(pointerId)) {
element.releasePointerCapture(pointerId);
}
pointerId = null;
try {
localStorage.setItem("lmeCompassPosition", JSON.stringify(lmeState.compassPosition));
} catch {
}
};
const start = event => {
if (event.pointerType === "mouse" && event.button !== 0) return;
event.preventDefault();
event.stopPropagation();
const rect = element.getBoundingClientRect();
pointerId = event.pointerId;
offsetX = event.clientX - rect.left;
offsetY = event.clientY - rect.top;
element.classList.add("is-dragging");
element.setPointerCapture?.(pointerId);
};
element.addEventListener("pointerdown", start);
element.addEventListener("pointermove", move);
element.addEventListener("pointerup", finish);
element.addEventListener("pointercancel", finish);
lmeState.compassDragCleanup = () => {
element.removeEventListener("pointerdown", start);
element.removeEventListener("pointermove", move);
element.removeEventListener("pointerup", finish);
element.removeEventListener("pointercancel", finish);
};
}
function setCompassOptions(visible, mode = "fixed") {
lmeState.compassVisible = Boolean(visible);
lmeState.compassMode = mode === "fixed" ? "fixed" : "heading";
lmeState.compassMode = mode === "heading" ? "heading" : "fixed";
if (!lmeState.map || !lmeState.compassLayer) return;
if (lmeState.compassVisible) {
lmeState.managerHiddenCategories.delete("compass");
@@ -5052,10 +5183,14 @@ function loadProjectSnapshot(raw, projectName) {
applyStyleFromProperties(layer);
}
}).addTo(lmeState.selectedParcelsLayer);
L.geoJSON(data.fiducials || emptyFeatureCollection(), {
const restoredFiducials = L.geoJSON(data.fiducials || emptyFeatureCollection(), {
pointToLayer: (feature, latlng) => createFiducialMarker(feature, latlng),
onEachFeature: (feature, layer) => bindFiducialPopup(feature, layer)
}).addTo(lmeState.fiducials);
onEachFeature: (feature, layer) => {
bindFiducialPopup(feature, layer);
bindManagedMapSelection(layer, "fiducials", lmeState.fiducials);
}
});
restoredFiducials.eachLayer(layer => lmeState.fiducials.addLayer(layer));
if (lmeState.importedSheetsVisible) {
void ensureImportedSheetOutlines().then(() => {
applyImportedSheetVisibility();
@@ -6003,7 +6138,9 @@ function setFiducialPointsVisible(enabled) {
function setFiducialColor(color) {
if (!/^#[0-9a-f]{6}$/i.test(String(color))) return;
lmeState.fiducialColor = color;
lmeState.fiducials?.eachLayer(layer => {
const category = layerManagerCategories().find(item => item.key === "fiducials");
managedRecordsForCategory(category).forEach(record => {
const layer = record.layer;
if (layer.feature?.properties) {
layer.feature.properties._lmeColor = color;
layer.feature.properties._lmeFillColor = color;