diff --git a/Components/MenuHelp.razor b/Components/MenuHelp.razor
index 98be705..d2e7618 100644
--- a/Components/MenuHelp.razor
+++ b/Components/MenuHelp.razor
@@ -102,7 +102,7 @@
],
"tools" =>
[
- "Tabella oggetti: apre una vista ordinata di particelle, file importati e disegni. Usa i flag per selezionare piu righe, il lucchetto per proteggerle e la matita per modificare i metadati.",
+ "Tabella oggetti: apre una vista ordinata di particelle, file importati e disegni. Usa i flag per selezionare piu righe, evidenziate sulla mappa con contorno magenta e riempimento ciano; il lucchetto le protegge e la matita modifica i metadati.",
"Colonna Note: il pulsante Note e sempre attivo e apre tutte le note dell'oggetto. La consultazione resta disponibile anche senza Pro; aggiunta, modifica ed eliminazione richiedono abbonamento o licenza manuale.",
"Copia metadati: nella maschera Metadati oggetto scegli Copia da e l'oggetto sorgente. Attiva Importa anche i valori per copiare nomi e contenuti; disattivalo per creare soltanto gli stessi campi, lasciandoli vuoti o mantenendo i valori gia presenti.",
"Coordinate WGS84 e UTM: scegli un punto sulla mappa per leggere latitudine e longitudine oppure le coordinate metriche UTM, utili per rilievi e confronti tecnici.",
@@ -115,7 +115,7 @@
"settings" =>
[
"Mappa e sfondo: scegli la cartografia di base e regola Trasparenza catasto per leggere meglio confini e immagini sottostanti. Esempio: riducila quando le linee coprono dettagli del terreno.",
- "Bussola: attiva Mostra bussola per visualizzare N, NE, E, SE, S, SW, W e NW. Puoi trascinarla nella posizione preferita. Con nord fisso la mappa non ruota e la lancetta indica il nord; in modalita Navigatore la mappa segue l'orientamento del dispositivo.",
+ "Bussola: attiva Mostra bussola per visualizzare N, NE, E, SE, S, SW, W e NW. Puoi trascinarla nella posizione preferita. La modalita predefinita mantiene il nord della mappa fisso in alto e la lancetta indica il nord con movimento continuo; in modalita Navigatore la mappa segue l'orientamento del dispositivo.",
"Messaggi mappa: scegli dopo quanti secondi devono chiudersi automaticamente; imposta 0 per mantenerli finche non premi la X.",
"Autosalvataggio locale dopo ogni modifica: se attivo, ogni disegno, modifica, rinomina o eliminazione aggiorna immediatamente una copia di recupero nel dispositivo. Per usarla apri Dati e disegni e premi Recupera ultimo autosalvataggio.",
"Aggiorna automaticamente il progetto aperto: funziona solo dopo aver aperto o salvato un progetto modificabile. Dopo una modifica attende 3 secondi senza altre operazioni e aggiorna quel progetto; un progetto in sola lettura non viene mai sovrascritto.",
diff --git a/Components/Pages/Home.razor b/Components/Pages/Home.razor
index 8675ecb..6bce3ed 100644
--- a/Components/Pages/Home.razor
+++ b/Components/Pages/Home.razor
@@ -681,7 +681,7 @@
@@ -2871,7 +2871,7 @@
{
compass.ReadingChanged += OnCompassReadingChanged;
CompassSensorSubscribed = true;
- compass.Start(Microsoft.Maui.Devices.Sensors.SensorSpeed.UI);
+ compass.Start(Microsoft.Maui.Devices.Sensors.SensorSpeed.Game, applyLowPassFilter: true);
}
else if (!shouldMonitor && CompassSensorSubscribed)
{
diff --git a/LMEMappeCatastaliApp.csproj b/LMEMappeCatastaliApp.csproj
index ea742f1..2162b94 100644
--- a/LMEMappeCatastaliApp.csproj
+++ b/LMEMappeCatastaliApp.csproj
@@ -29,8 +29,8 @@
it.liviomerola.lmemappecatastali.pro
- 1.0.22
- 122
+ 1.0.23
+ 123
$(ApplicationDisplayVersion)
diff --git a/wwwroot/css/app.css b/wwwroot/css/app.css
index df74280..fb3fca3 100644
--- a/wwwroot/css/app.css
+++ b/wwwroot/css/app.css
@@ -1868,7 +1868,6 @@ label {
transform-origin: 50% 100%;
border-radius: 4px 4px 1px 1px;
background: #ef4444;
- transition: transform 120ms linear;
}
.map-compass-needle::after {
diff --git a/wwwroot/js/lme-map.js b/wwwroot/js/lme-map.js
index 98625d3..434fbd8 100644
--- a/wwwroot/js/lme-map.js
+++ b/wwwroot/js/lme-map.js
@@ -70,6 +70,11 @@ const lmeState = {
compassVisible: false,
compassMode: "fixed",
compassHeading: 0,
+ compassDisplayRotation: 0,
+ compassTargetRotation: 0,
+ compassHeadingInitialized: false,
+ compassAnimationFrame: null,
+ compassAnimationTime: 0,
compassPosition: null,
compassDragCleanup: null,
compassResizeHandler: null,
@@ -87,6 +92,7 @@ const lmeState = {
managerHighlightedParentRecords: null,
managerHoveredParentKey: null,
managerHoveredParentRecords: null,
+ managerDirectHighlightStyles: new Map(),
managerOutsideSelectionBound: false,
managerHiddenObjects: new Map(),
managerHiddenObjectIds: new Set(),
@@ -496,6 +502,7 @@ function createCompassLayer() {
updateCompassDisplay();
},
onRemove(map) {
+ stopCompassAnimation();
lmeState.compassDragCleanup?.();
lmeState.compassDragCleanup = null;
if (lmeState.compassResizeHandler) {
@@ -623,6 +630,7 @@ function setCompassOptions(visible, mode = "fixed") {
lmeState.compassLayer.addTo(lmeState.map);
}
} else {
+ stopCompassAnimation();
lmeState.managerHiddenCategories.add("compass");
if (lmeState.map.hasLayer(lmeState.compassLayer)) {
lmeState.map.removeLayer(lmeState.compassLayer);
@@ -641,19 +649,67 @@ function setCompassOptions(visible, mode = "fixed") {
function setCompassHeading(heading) {
const parsed = Number(heading);
if (!Number.isFinite(parsed)) return;
- lmeState.compassHeading = ((parsed % 360) + 360) % 360;
+ const normalizedHeading = ((parsed % 360) + 360) % 360;
+ const requestedRotation = -normalizedHeading;
+ lmeState.compassHeading = normalizedHeading;
+ if (!lmeState.compassHeadingInitialized) {
+ lmeState.compassHeadingInitialized = true;
+ lmeState.compassDisplayRotation = requestedRotation;
+ lmeState.compassTargetRotation = requestedRotation;
+ updateCompassDisplay();
+ } else {
+ const delta = shortestAngleDelta(lmeState.compassTargetRotation, requestedRotation);
+ if (Math.abs(delta) >= 0.15) {
+ lmeState.compassTargetRotation += delta;
+ startCompassAnimation();
+ }
+ }
if (lmeState.compassVisible && lmeState.compassMode === "heading") {
lmeState.map?.setHeading?.(lmeState.compassHeading, { ease: 0.2, deadzone: 1 });
}
+}
+
+function shortestAngleDelta(from, to) {
+ return ((((to - from) + 180) % 360) + 360) % 360 - 180;
+}
+
+function startCompassAnimation() {
+ if (lmeState.compassAnimationFrame !== null) return;
+ lmeState.compassAnimationTime = 0;
+ lmeState.compassAnimationFrame = requestAnimationFrame(animateCompass);
+}
+
+function animateCompass(timestamp) {
+ lmeState.compassAnimationFrame = null;
+ const elapsed = lmeState.compassAnimationTime
+ ? Math.min(50, Math.max(1, timestamp - lmeState.compassAnimationTime))
+ : 16;
+ lmeState.compassAnimationTime = timestamp;
+ const difference = lmeState.compassTargetRotation - lmeState.compassDisplayRotation;
+ const smoothing = 1 - Math.exp(-elapsed / 85);
+ lmeState.compassDisplayRotation += difference * smoothing;
+ if (Math.abs(difference) < 0.05) {
+ lmeState.compassDisplayRotation = lmeState.compassTargetRotation;
+ lmeState.compassAnimationTime = 0;
+ } else {
+ lmeState.compassAnimationFrame = requestAnimationFrame(animateCompass);
+ }
updateCompassDisplay();
}
+function stopCompassAnimation() {
+ if (lmeState.compassAnimationFrame !== null) {
+ cancelAnimationFrame(lmeState.compassAnimationFrame);
+ lmeState.compassAnimationFrame = null;
+ }
+ lmeState.compassAnimationTime = 0;
+}
+
function updateCompassDisplay() {
const element = lmeState.compassElement;
if (!element) return;
- const rotation = -lmeState.compassHeading;
element.classList.toggle("is-fixed", lmeState.compassMode === "fixed");
- element.style.setProperty("--compass-rotation", `${rotation}deg`);
+ element.style.setProperty("--compass-rotation", `${lmeState.compassDisplayRotation}deg`);
element.setAttribute("aria-label", lmeState.compassMode === "fixed"
? `Bussola: mappa con nord fisso, orientamento ${Math.round(lmeState.compassHeading)} gradi`
: `Bussola: nord, orientamento ${Math.round(lmeState.compassHeading)} gradi`);
@@ -4085,7 +4141,9 @@ function renderSelectedManagerHighlight() {
}
function renderManagerHighlight(records) {
+ restoreDirectManagerHighlights();
lmeState.managerHighlight?.clearLayers();
+ records.forEach(record => applyDirectManagerHighlight(record.layer));
const features = records.flatMap(record => {
const geojson = record.layer?.toGeoJSON?.();
if (!geojson) {
@@ -4112,7 +4170,36 @@ function renderManagerHighlight(records) {
}).addTo(lmeState.managerHighlight);
}
+function applyDirectManagerHighlight(layer) {
+ if (!layer || typeof layer.setStyle !== "function" || lmeState.managerDirectHighlightStyles.has(layer)) return;
+ const options = layer.options || {};
+ lmeState.managerDirectHighlightStyles.set(layer, {
+ color: options.color,
+ weight: options.weight,
+ opacity: options.opacity,
+ fillColor: options.fillColor,
+ fillOpacity: options.fillOpacity,
+ dashArray: options.dashArray ?? null
+ });
+ layer.setStyle({
+ color: "#ff00d4",
+ weight: Math.max(7, (Number(options.weight) || 2) + 4),
+ opacity: 1,
+ fillColor: "#00fff0",
+ fillOpacity: 0.34,
+ dashArray: null
+ });
+}
+
+function restoreDirectManagerHighlights() {
+ lmeState.managerDirectHighlightStyles.forEach((style, layer) => {
+ if (typeof layer?.setStyle === "function") layer.setStyle(style);
+ });
+ lmeState.managerDirectHighlightStyles.clear();
+}
+
function clearManagerHighlight() {
+ restoreDirectManagerHighlights();
lmeState.managerHighlight?.clearLayers();
lmeState.managerHighlightedObjectId = null;
lmeState.managerHighlightedCategoryKey = null;