diff --git a/LMEMappeCatastaliApp.csproj b/LMEMappeCatastaliApp.csproj
index 478cb37..dc9af14 100644
--- a/LMEMappeCatastaliApp.csproj
+++ b/LMEMappeCatastaliApp.csproj
@@ -28,8 +28,8 @@
it.liviomerola.lmemappecatastali.pro
- 1.0.10
- 110
+ 1.0.11
+ 111
$(ApplicationDisplayVersion)
diff --git a/PLAY_STORE_PRO_CHECKLIST.md b/PLAY_STORE_PRO_CHECKLIST.md
index 5a7dea7..eb7d268 100644
--- a/PLAY_STORE_PRO_CHECKLIST.md
+++ b/PLAY_STORE_PRO_CHECKLIST.md
@@ -4,7 +4,7 @@
- Nome: LME Mappe Catastali Pro.
- Package name Android: `it.liviomerola.lmemappecatastali.pro`.
-- Versione corrente da pubblicare: `1.0.10` / version code `110`.
+- Versione corrente da pubblicare: `1.0.11` / version code `111`.
- Formato Release Android: Android App Bundle (`.aab`).
- Target SDK Release: Android 16 / API 36.
diff --git a/Platforms/Android/MainActivity.cs b/Platforms/Android/MainActivity.cs
index abf1583..50c0ab3 100644
--- a/Platforms/Android/MainActivity.cs
+++ b/Platforms/Android/MainActivity.cs
@@ -102,7 +102,11 @@ public class MainActivity : MauiAppCompatActivity
{
if (view is null || insets is null) return insets;
var systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars());
- view.SetPadding(_left, _top, _right, systemBars?.Bottom ?? 0);
+ view.SetPadding(
+ _left,
+ systemBars?.Top ?? _top,
+ _right,
+ systemBars?.Bottom ?? 0);
return insets;
}
}
diff --git a/README.md b/README.md
index 7bf6eea..bca8a1f 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,12 @@ App .NET MAUI Blazor Hybrid per Android, iOS, Mac Catalyst e Windows, pensata pe
Informativa privacy: [PRIVACY_POLICY.md](PRIVACY_POLICY.md).
+## Versione Pro 1.0.11 test
+
+- Il flag Fogli catastali mostra il WMS di tutti i fogli agli zoom panoramici e, in dettaglio, il confine ufficiale WFS del foglio corrente anche senza dati caricati.
+- Se il foglio corrente e gia presente in un progetto o in un'importazione, l'overlay di navigazione non ne crea un duplicato.
+- La toolbar superiore rispetta l'area occupata dalla barra di stato e dalle notifiche Android.
+
## Versione Pro 1.0.10 test
- Migliora la compatibilita del servizio catastale AdE sui dispositivi Android meno recenti con HTTP/1.1, tentativi automatici e una ricerca GPS piu tollerante.
diff --git a/wwwroot/css/app.css b/wwwroot/css/app.css
index 81c1eaf..76698c2 100644
--- a/wwwroot/css/app.css
+++ b/wwwroot/css/app.css
@@ -1318,6 +1318,21 @@ input[type="checkbox"] {
height: 38px;
}
+.cadastral-sheet-label {
+ width: auto !important;
+ height: auto !important;
+ border: 1px solid #9a5b00;
+ border-radius: 4px;
+ padding: 3px 6px;
+ background: rgba(255, 251, 235, 0.94);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
+ color: #713f12;
+ font-size: 11px;
+ font-weight: 800;
+ line-height: 1.2;
+ white-space: nowrap;
+}
+
.lme-drawing-scope-actions a[aria-disabled="true"] {
opacity: 0.45;
pointer-events: none;
diff --git a/wwwroot/js/lme-map.js b/wwwroot/js/lme-map.js
index 66f6f22..e637e56 100644
--- a/wwwroot/js/lme-map.js
+++ b/wwwroot/js/lme-map.js
@@ -14,8 +14,14 @@ const lmeState = {
"cadastre"
],
cadastre: null,
+ cadastreZoning: null,
cadastreLayers: ["CP.CadastralParcel", "codice_plla", "CP.CadastralZoning", "fabbricati"],
cadastreOpacity: 0.75,
+ navigationSheetLayer: null,
+ navigationSheetFeature: null,
+ navigationSheetLabel: null,
+ navigationSheetRefreshTimer: null,
+ navigationSheetRequestId: 0,
imported: null,
importedVisible: true,
importedSheets: null,
@@ -305,6 +311,13 @@ function init(dotNetReference) {
lmeState.drawingMeasurements = L.layerGroup().addTo(lmeState.map);
lmeState.selectedParcelsLayer = L.featureGroup().addTo(lmeState.map);
lmeState.gpsParcelLayer = L.featureGroup().addTo(lmeState.map);
+ const navigationSheetPane = lmeState.map.createPane("navigationSheetPane");
+ navigationSheetPane.style.zIndex = "465";
+ navigationSheetPane.style.pointerEvents = "none";
+ const cadastreZoningPane = lmeState.map.createPane("cadastreZoningPane");
+ cadastreZoningPane.style.zIndex = "455";
+ cadastreZoningPane.style.pointerEvents = "none";
+ lmeState.navigationSheetLayer = L.layerGroup().addTo(lmeState.map);
lmeState.gpsTrail = L.polyline([], {
pane: "gpsTrailPane",
color: "#f97316",
@@ -339,6 +352,7 @@ function init(dotNetReference) {
lmeState.map.on("moveend", () => {
void updateAzureAttribution();
scheduleMeasurementRefresh();
+ scheduleNavigationSheetRefresh();
});
document.getElementById("closeWmsPreview")?.addEventListener("click", closeCadastrePreview);
@@ -5387,6 +5401,9 @@ function setCadastreOpacity(value) {
if (lmeState.cadastre) {
lmeState.cadastre.setOpacity(lmeState.cadastreOpacity);
}
+ if (lmeState.cadastreZoning) {
+ lmeState.cadastreZoning.setOpacity(Math.max(0.8, lmeState.cadastreOpacity));
+ }
}
function toggleCadastreLayer(layer, enabled) {
@@ -5398,6 +5415,13 @@ function toggleCadastreLayer(layer, enabled) {
lmeState.cadastreLayers = lmeState.cadastreLayers.filter(item => item !== layer);
}
refreshCadastreLayer();
+ if (layer === "CP.CadastralZoning") {
+ if (enabled) {
+ scheduleNavigationSheetRefresh(true);
+ } else {
+ clearNavigationSheetOutline();
+ }
+ }
const layerNames = {
"CP.CadastralParcel": "Confini particelle",
codice_plla: "Numeri particelle",
@@ -5413,29 +5437,166 @@ function toggleCadastreLayer(layer, enabled) {
setInfo(`${layerNames[layer] || layer}: ${enabled ? "attivo" : "nascosto"}.`);
}
+function scheduleNavigationSheetRefresh(immediate = false) {
+ window.clearTimeout(lmeState.navigationSheetRefreshTimer);
+ if (!lmeState.map
+ || !lmeState.dotNetReference
+ || !lmeState.cadastreLayers.includes("CP.CadastralZoning")) {
+ return;
+ }
+ if (lmeState.map.getZoom() < 15) {
+ clearNavigationSheetOutline();
+ return;
+ }
+ lmeState.navigationSheetRefreshTimer = window.setTimeout(
+ () => void refreshNavigationSheetOutline(),
+ immediate ? 0 : 650);
+}
+
+async function refreshNavigationSheetOutline() {
+ if (!lmeState.map
+ || !lmeState.dotNetReference
+ || !lmeState.cadastreLayers.includes("CP.CadastralZoning")) {
+ return;
+ }
+
+ const center = lmeState.map.getCenter();
+ const point = [center.lng, center.lat];
+ if (loadedCadastralSheetAt(point)) {
+ clearNavigationSheetOutline();
+ return;
+ }
+ if (lmeState.navigationSheetFeature?.geometry
+ && geometryContainsPoint(lmeState.navigationSheetFeature.geometry, point)) {
+ lmeState.navigationSheetLabel?.setLatLng(center);
+ return;
+ }
+
+ const requestId = ++lmeState.navigationSheetRequestId;
+ try {
+ const xml = await lmeState.dotNetReference.invokeMethodAsync(
+ "GetOfficialCadastralSheetsNear",
+ center.lat,
+ center.lng);
+ if (requestId !== lmeState.navigationSheetRequestId
+ || !lmeState.cadastreLayers.includes("CP.CadastralZoning")) {
+ return;
+ }
+
+ const candidates = gmlParcelsToGeoJson(xml).features || [];
+ const sheet = findContainingPolygon(point, { type: "FeatureCollection", features: candidates })
+ || candidates[0];
+ if (!sheet) {
+ return;
+ }
+
+ const reference = String(sheet.properties?.NATIONALCADASTRALZONINGREFERENCE
+ || sheet.properties?.LABEL
+ || "").trim();
+ const previousReference = String(
+ lmeState.navigationSheetFeature?.properties?.NATIONALCADASTRALZONINGREFERENCE
+ || lmeState.navigationSheetFeature?.properties?.LABEL
+ || "").trim();
+ lmeState.navigationSheetFeature = sheet;
+ lmeState.navigationSheetLayer.clearLayers();
+ L.geoJSON(sheet, {
+ pane: "navigationSheetPane",
+ interactive: false,
+ style: {
+ color: "#f59e0b",
+ weight: 4,
+ opacity: 0.95,
+ dashArray: "10 6",
+ fillColor: "#fbbf24",
+ fillOpacity: 0.055
+ }
+ }).addTo(lmeState.navigationSheetLayer);
+ lmeState.navigationSheetLabel = L.marker(center, {
+ pane: "navigationSheetPane",
+ interactive: false,
+ icon: L.divIcon({
+ className: "cadastral-sheet-label",
+ html: escapeHtml(reference ? `Foglio ${reference}` : "Foglio catastale")
+ })
+ }).addTo(lmeState.navigationSheetLayer);
+ if (reference && reference !== previousReference) {
+ setInfo(`Confine del foglio catastale ${reference} caricato.`);
+ }
+ } catch {
+ if (requestId === lmeState.navigationSheetRequestId) {
+ setInfo("Confine del foglio catastale non disponibile dal servizio AdE. Riprovo al prossimo spostamento.");
+ }
+ }
+}
+
+function clearNavigationSheetOutline() {
+ window.clearTimeout(lmeState.navigationSheetRefreshTimer);
+ lmeState.navigationSheetRequestId++;
+ lmeState.navigationSheetFeature = null;
+ lmeState.navigationSheetLabel = null;
+ lmeState.navigationSheetLayer?.clearLayers();
+}
+
+function loadedCadastralSheetAt(point) {
+ const features = [
+ ...(lmeState.importedSheets?.toGeoJSON?.()?.features || []),
+ ...(lmeState.imported?.toGeoJSON?.()?.features || [])
+ ].filter(feature => ["Polygon", "MultiPolygon"].includes(feature?.geometry?.type)
+ && (feature.properties?._lmeOfficialSheet
+ || feature.properties?.NATIONALCADASTRALZONINGREFERENCE
+ || isSheetOrOverlayFeature(feature)));
+ return findContainingPolygon(point, { type: "FeatureCollection", features });
+}
+
function refreshCadastreLayer() {
if (lmeState.cadastre) {
lmeState.map.removeLayer(lmeState.cadastre);
}
-
- const layers = activeCadastreLayers();
- lmeState.cadastre = null;
- if (!layers.length) {
- return;
+ if (lmeState.cadastreZoning) {
+ lmeState.map.removeLayer(lmeState.cadastreZoning);
}
- const cadastreLayer = L.tileLayer.wms(cadastreUrl(), {
- pane: "cadastrePane",
- layers: layers.join(","),
- format: "image/png",
- transparent: true,
- version: "1.1.1",
- crs: cadastreCrs,
- maxZoom: 22,
- opacity: lmeState.cadastreOpacity,
- attribution: "Agenzia delle Entrate - CC BY 4.0"
- });
+ const zoningEnabled = lmeState.cadastreLayers.includes("CP.CadastralZoning");
+ const layers = activeCadastreLayers().filter(layer => layer !== "CP.CadastralZoning");
+ lmeState.cadastre = null;
+ lmeState.cadastreZoning = null;
+ if (layers.length) {
+ const cadastreLayer = L.tileLayer.wms(cadastreUrl(), {
+ pane: "cadastrePane",
+ layers: layers.join(","),
+ format: "image/png",
+ transparent: true,
+ version: "1.1.1",
+ crs: cadastreCrs,
+ maxZoom: 22,
+ opacity: lmeState.cadastreOpacity,
+ attribution: "Agenzia delle Entrate - CC BY 4.0"
+ });
+ attachCadastreTileRecovery(cadastreLayer);
+ lmeState.cadastre = cadastreLayer.addTo(lmeState.map);
+ }
+
+ if (zoningEnabled) {
+ const zoningLayer = L.tileLayer.wms(cadastreUrl(), {
+ pane: "cadastreZoningPane",
+ layers: "CP.CadastralZoning",
+ format: "image/png",
+ transparent: true,
+ version: "1.1.1",
+ crs: cadastreCrs,
+ minZoom: 8,
+ maxZoom: 14,
+ opacity: Math.max(0.8, lmeState.cadastreOpacity),
+ attribution: "Agenzia delle Entrate - CC BY 4.0"
+ });
+ attachCadastreTileRecovery(zoningLayer);
+ lmeState.cadastreZoning = zoningLayer.addTo(lmeState.map);
+ scheduleNavigationSheetRefresh(true);
+ }
+}
+
+function attachCadastreTileRecovery(cadastreLayer) {
let errorShown = false;
cadastreLayer.on("tileerror", event => {
const tile = event?.tile;
@@ -5459,8 +5620,6 @@ function refreshCadastreLayer() {
cadastreLayer.on("load", () => {
errorShown = false;
});
-
- lmeState.cadastre = cadastreLayer.addTo(lmeState.map);
}
function activeCadastreLayers() {