Compare commits

..
4 Commits
6 changed files with 405 additions and 33 deletions

No files matched your search

+23 -1
View File
@@ -472,6 +472,7 @@
private bool DrawToolsVisible { get; set; } private bool DrawToolsVisible { get; set; }
private bool TrailEnabled { get; set; } private bool TrailEnabled { get; set; }
private bool LayerPanelOpen { get; set; } private bool LayerPanelOpen { get; set; }
private DotNetObjectReference<Home>? MapCallbackReference { get; set; }
private bool IncludeCadastralParcels { get; set; } = true; private bool IncludeCadastralParcels { get; set; } = true;
private bool IncludeKmlSheetLayer { get; set; } private bool IncludeKmlSheetLayer { get; set; }
private bool ShowImportedSheetLayer { get; set; } private bool ShowImportedSheetLayer { get; set; }
@@ -570,7 +571,8 @@
{ {
if (firstRender) if (firstRender)
{ {
await JS.InvokeVoidAsync("lmeMap.init"); MapCallbackReference = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("lmeMap.init", MapCallbackReference);
await ApplyFlagPreferencesToMapAsync(); await ApplyFlagPreferencesToMapAsync();
await CenterOnInitialLocationAsync(); await CenterOnInitialLocationAsync();
await MigrateLegacyProjectAsync(); await MigrateLegacyProjectAsync();
@@ -713,6 +715,10 @@
private async Task ToggleLayerPanel() private async Task ToggleLayerPanel()
{ {
LayerPanelOpen = !LayerPanelOpen; LayerPanelOpen = !LayerPanelOpen;
if (!LayerPanelOpen)
{
await JS.InvokeVoidAsync("lmeMap.clearLayerManagerSelection");
}
if (LayerPanelOpen) if (LayerPanelOpen)
{ {
ActiveTab = "map"; ActiveTab = "map";
@@ -1043,6 +1049,21 @@
await JS.InvokeVoidAsync("lmeMap.setDrawingMeasurementsVisible", ShowDrawingMeasurements); await JS.InvokeVoidAsync("lmeMap.setDrawingMeasurementsVisible", ShowDrawingMeasurements);
} }
[JSInvokable]
public Task SetMeasurementsFromLayerManager(string category, bool enabled)
{
if (category == "imported")
{
ShowImportedMeasurements = enabled;
}
else if (category == "drawings")
{
ShowDrawingMeasurements = enabled;
}
SaveFlagPreferences();
return InvokeAsync(StateHasChanged);
}
private async Task ToggleDrawTools() private async Task ToggleDrawTools()
{ {
DrawToolsVisible = !DrawToolsVisible; DrawToolsVisible = !DrawToolsVisible;
@@ -1499,6 +1520,7 @@
LocationService.StopTracking(); LocationService.StopTracking();
GpsLookupCancellation?.Cancel(); GpsLookupCancellation?.Cancel();
GpsLookupCancellation?.Dispose(); GpsLookupCancellation?.Dispose();
MapCallbackReference?.Dispose();
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
} }
+2 -2
View File
@@ -28,8 +28,8 @@
<ApplicationId>com.companyname.lmemappecatastaliapp</ApplicationId> <ApplicationId>com.companyname.lmemappecatastaliapp</ApplicationId>
<!-- Versions --> <!-- Versions -->
<ApplicationDisplayVersion>0.2.18</ApplicationDisplayVersion> <ApplicationDisplayVersion>0.2.22</ApplicationDisplayVersion>
<ApplicationVersion>22</ApplicationVersion> <ApplicationVersion>26</ApplicationVersion>
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged --> <!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
<WindowsPackageType>None</WindowsPackageType> <WindowsPackageType>None</WindowsPackageType>
+1 -1
View File
@@ -13,7 +13,7 @@ public sealed class AzureMapsService
public AzureMapsService() public AzureMapsService()
{ {
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/0.2.18"); _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/0.2.22");
_httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json"); _httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json");
} }
+1 -1
View File
@@ -13,7 +13,7 @@ public sealed class NetworkLocationService
public NetworkLocationService() public NetworkLocationService()
{ {
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/0.2.18"); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/0.2.22");
} }
public async Task<NetworkLocationSnapshot?> GetApproximateLocationAsync( public async Task<NetworkLocationSnapshot?> GetApproximateLocationAsync(
+50 -1
View File
@@ -274,7 +274,7 @@ input[type="checkbox"] {
.layer-manager-block > summary, .layer-manager-block > summary,
.layer-manager-object > summary { .layer-manager-object > summary {
display: grid; display: grid;
grid-template-columns: 28px 20px minmax(0, 1fr) auto; grid-template-columns: 28px 20px minmax(0, 1fr) auto auto;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
min-height: 38px; min-height: 38px;
@@ -302,6 +302,29 @@ input[type="checkbox"] {
cursor: grab; cursor: grab;
} }
.layer-move-buttons {
display: inline-grid;
grid-template-columns: 24px 24px;
gap: 2px;
}
.layer-move-buttons button {
width: 24px;
height: 28px;
padding: 0;
border: 1px solid #cbd5da;
border-radius: 4px;
background: #f6f8f9;
color: var(--ink);
font-size: 16px;
font-weight: 800;
line-height: 1;
}
.layer-move-buttons button:active {
background: #dce8ec;
}
.layer-manager-title { .layer-manager-title {
min-width: 0; min-width: 0;
overflow-wrap: anywhere; overflow-wrap: anywhere;
@@ -326,11 +349,37 @@ input[type="checkbox"] {
font-size: 10px; font-size: 10px;
} }
.layer-manager-suboption {
display: grid;
grid-template-columns: 18px minmax(0, 1fr);
align-items: center;
gap: 6px;
min-height: 30px;
color: var(--ink);
font-size: 11px;
font-weight: 700;
}
.layer-manager-suboption input {
margin: 0;
}
.layer-manager-info {
display: grid;
gap: 2px;
padding: 3px 0 0 24px;
}
.layer-manager-dragging { .layer-manager-dragging {
opacity: 0.55; opacity: 0.55;
outline: 2px solid #2563eb; outline: 2px solid #2563eb;
} }
.layer-manager-object-selected > summary {
background: #fff0fb;
box-shadow: inset 3px 0 #ff00d4;
}
.gps-overlay { .gps-overlay {
position: absolute; position: absolute;
top: 72px; top: 72px;
+328 -27
View File
@@ -49,10 +49,17 @@ const lmeState = {
azureMapsKey: null, azureMapsKey: null,
azureAttribution: null, azureAttribution: null,
managerHighlight: null, managerHighlight: null,
managerHighlightedObjectId: null,
managerHighlightedCategoryKey: null,
managerOutsideSelectionBound: false,
managerHiddenObjects: new Map(), managerHiddenObjects: new Map(),
managerHiddenObjectIds: new Set(), managerHiddenObjectIds: new Set(),
managerHiddenMeasurementSummaryIds: new Set(),
managerHiddenMeasurementSideIds: new Set(),
managerHiddenCategories: new Set(), managerHiddenCategories: new Set(),
managerObjectOrder: {} managerObjectOrder: {},
managerExpandedRows: new Set(),
dotNetReference: null
}; };
// AdE publishes the cadastral WMS in ETRS89. WMS 1.1.1 keeps the longitude, // AdE publishes the cadastral WMS in ETRS89. WMS 1.1.1 keeps the longitude,
@@ -76,6 +83,7 @@ window.lmeMap = {
setImportedSheetLayerVisible, setImportedSheetLayerVisible,
setLayerOrder, setLayerOrder,
refreshLayerManager, refreshLayerManager,
clearLayerManagerSelection: clearManagerHighlight,
setImportedMeasurementsVisible, setImportedMeasurementsVisible,
setDrawingMeasurementsVisible, setDrawingMeasurementsVisible,
addProjectMarker, addProjectMarker,
@@ -104,7 +112,10 @@ window.lmeMap = {
showInfo: setInfo showInfo: setInfo
}; };
function init() { function init(dotNetReference) {
if (dotNetReference) {
lmeState.dotNetReference = dotNetReference;
}
if (lmeState.map) { if (lmeState.map) {
return; return;
} }
@@ -188,6 +199,7 @@ function init() {
} }
lmeState.map.on("click", async event => { lmeState.map.on("click", async event => {
clearManagerHighlight();
const info = await queryFeatureInfo(event.latlng); const info = await queryFeatureInfo(event.latlng);
if (info) { if (info) {
L.popup().setLatLng(event.latlng).setContent(info).openOn(lmeState.map); L.popup().setLatLng(event.latlng).setContent(info).openOn(lmeState.map);
@@ -209,6 +221,19 @@ function init() {
closeCadastrePreview(); closeCadastrePreview();
} }
}); });
if (!lmeState.managerOutsideSelectionBound) {
document.addEventListener("pointerdown", event => {
if (!lmeState.managerHighlightedObjectId) {
return;
}
const selectedRow = event.target?.closest?.(".layer-manager-object");
if (selectedRow?.dataset.objectId === lmeState.managerHighlightedObjectId) {
return;
}
clearManagerHighlight();
});
lmeState.managerOutsideSelectionBound = true;
}
setInfo("Mappa pronta: satellite + catasto AdE"); setInfo("Mappa pronta: satellite + catasto AdE");
} }
@@ -1162,6 +1187,9 @@ function refreshLayerManager() {
if (!container || !lmeState.map) { if (!container || !lmeState.map) {
return; return;
} }
captureLayerManagerExpansion(container);
const scrollHost = container.closest(".map-layer-panel");
const scrollTop = scrollHost?.scrollTop || 0;
applyStoredLayerManagerVisibility(); applyStoredLayerManagerVisibility();
container.innerHTML = ""; container.innerHTML = "";
lmeState.managerRecords = new Map(); lmeState.managerRecords = new Map();
@@ -1181,6 +1209,13 @@ function refreshLayerManager() {
}); });
applyManagedVisualOrder(container); applyManagedVisualOrder(container);
bindLayerManagerDrag(container); bindLayerManagerDrag(container);
bindLayerManagerMoveButtons(container);
if (scrollHost) {
scrollHost.scrollTop = scrollTop;
window.requestAnimationFrame(() => {
scrollHost.scrollTop = scrollTop;
});
}
} }
function renderLayerManagerCategory(category, records) { function renderLayerManagerCategory(category, records) {
@@ -1188,10 +1223,14 @@ function renderLayerManagerCategory(category, records) {
group.className = "layer-manager-group"; group.className = "layer-manager-group";
group.dataset.dragType = "category"; group.dataset.dragType = "category";
group.dataset.categoryKey = category.key; group.dataset.categoryKey = category.key;
const visible = !lmeState.managerHiddenCategories.has(category.key) bindLayerManagerExpansion(group, `category:${category.key}`);
&& (!category.roots.length || category.roots.every(layer => lmeState.map.hasLayer(layer))); const visibleCount = records.filter(record => !lmeState.managerHiddenObjectIds.has(record.id)).length;
group.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina gruppo">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra ${escapeHtml(category.label)}"><span class="layer-manager-title">${escapeHtml(category.label)}</span><span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`; const categoryVisible = !lmeState.managerHiddenCategories.has(category.key)
&& (!category.roots.length || category.roots.some(layer => lmeState.map.hasLayer(layer)));
const visible = categoryVisible;
group.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina gruppo">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra ${escapeHtml(category.label)}"><span class="layer-manager-title">${escapeHtml(category.label)}</span>${layerManagerMoveButtons("gruppo")}<span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`;
const checkbox = group.querySelector("input"); const checkbox = group.querySelector("input");
checkbox.indeterminate = categoryVisible && records.length > 0 && visibleCount < records.length;
checkbox.addEventListener("click", event => event.stopPropagation()); checkbox.addEventListener("click", event => event.stopPropagation());
checkbox.addEventListener("change", () => setManagedCategoryVisible(category, checkbox.checked)); checkbox.addEventListener("change", () => setManagedCategoryVisible(category, checkbox.checked));
const children = group.querySelector(".layer-manager-children"); const children = group.querySelector(".layer-manager-children");
@@ -1215,9 +1254,10 @@ function renderLayerManagerBlock(category, source, records) {
block.dataset.dragType = "block"; block.dataset.dragType = "block";
block.dataset.categoryKey = category.key; block.dataset.categoryKey = category.key;
block.dataset.blockKey = source; block.dataset.blockKey = source;
bindLayerManagerExpansion(block, `block:${category.key}:${source}`);
const visibleCount = records.filter(record => !lmeState.managerHiddenObjectIds.has(record.id)).length; const visibleCount = records.filter(record => !lmeState.managerHiddenObjectIds.has(record.id)).length;
const visible = visibleCount === records.length; const visible = visibleCount === records.length;
block.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina blocco">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra blocco"><span class="layer-manager-title">${escapeHtml(source)}</span><span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`; block.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina blocco">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra blocco"><span class="layer-manager-title">${escapeHtml(source)}</span>${layerManagerMoveButtons("blocco")}<span class="layer-manager-count">${records.length}</span></summary><div class="layer-manager-children"></div>`;
const checkbox = block.querySelector("input"); const checkbox = block.querySelector("input");
checkbox.indeterminate = visibleCount > 0 && !visible; checkbox.indeterminate = visibleCount > 0 && !visible;
checkbox.addEventListener("click", event => event.stopPropagation()); checkbox.addEventListener("click", event => event.stopPropagation());
@@ -1233,10 +1273,14 @@ function renderLayerManagerBlock(category, source, records) {
function renderLayerManagerObject(record, index) { function renderLayerManagerObject(record, index) {
const object = document.createElement("details"); const object = document.createElement("details");
object.className = "layer-manager-object"; object.className = "layer-manager-object";
if (record.id === lmeState.managerHighlightedObjectId) {
object.classList.add("layer-manager-object-selected");
}
object.dataset.dragType = "object"; object.dataset.dragType = "object";
object.dataset.objectId = record.id; object.dataset.objectId = record.id;
bindLayerManagerExpansion(object, `object:${record.id}`);
const visible = !lmeState.managerHiddenObjectIds.has(record.id); const visible = !lmeState.managerHiddenObjectIds.has(record.id);
object.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina oggetto">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra oggetto"><span class="layer-manager-title">${escapeHtml(managedObjectLabel(record, index))}</span><span class="layer-manager-count">›</span></summary><div class="layer-manager-details"></div>`; object.innerHTML = `<summary><button type="button" class="layer-drag-handle" aria-label="Trascina oggetto">☰</button><input type="checkbox" ${visible ? "checked" : ""} aria-label="Mostra oggetto"><span class="layer-manager-title">${escapeHtml(managedObjectLabel(record, index))}</span>${layerManagerMoveButtons("oggetto")}<span class="layer-manager-count">›</span></summary><div class="layer-manager-details"></div>`;
const checkbox = object.querySelector("input"); const checkbox = object.querySelector("input");
checkbox.addEventListener("click", event => event.stopPropagation()); checkbox.addEventListener("click", event => event.stopPropagation());
checkbox.addEventListener("change", () => setManagedObjectVisible(record, checkbox.checked, true)); checkbox.addEventListener("change", () => setManagedObjectVisible(record, checkbox.checked, true));
@@ -1247,13 +1291,137 @@ function renderLayerManagerObject(record, index) {
highlightManagedObject(record); highlightManagedObject(record);
}); });
const details = object.querySelector(".layer-manager-details"); const details = object.querySelector(".layer-manager-details");
details.appendChild(renderLayerManagerVisibilityOption(
geometryVisibilityLabel(record.layer),
visible,
checked => setManagedObjectVisible(record, checked, true)));
if (supportsManagedMeasurements(record)) {
managedMeasurementKinds(record).forEach(kind => {
const measurementsVisible = managedMeasurementsGloballyVisible(record.categoryKey)
&& !managedMeasurementHiddenSet(kind.key).has(record.id);
details.appendChild(renderLayerManagerVisibilityOption(
kind.label,
measurementsVisible,
checked => setManagedMeasurementKindVisible(record, kind.key, checked)));
});
}
const rows = managedObjectDetails(record.layer); const rows = managedObjectDetails(record.layer);
details.innerHTML = rows.length const info = document.createElement("div");
info.className = "layer-manager-info";
info.innerHTML = rows.length
? rows.map(value => `<span>${escapeHtml(value)}</span>`).join("") ? rows.map(value => `<span>${escapeHtml(value)}</span>`).join("")
: "<span>Nessuna misura disponibile</span>"; : "<span>Nessuna misura disponibile</span>";
details.appendChild(info);
return object; return object;
} }
function renderLayerManagerVisibilityOption(label, checked, onChange) {
const option = document.createElement("label");
option.className = "layer-manager-suboption";
option.innerHTML = `<input type="checkbox" ${checked ? "checked" : ""}><span>${escapeHtml(label)}</span>`;
const checkbox = option.querySelector("input");
checkbox.addEventListener("click", event => event.stopPropagation());
checkbox.addEventListener("change", () => onChange(checkbox.checked));
return option;
}
function geometryVisibilityLabel(layer) {
if (layer instanceof L.Polygon || layer instanceof L.Circle) {
return "Area/geometria sulla mappa";
}
if (layer instanceof L.Polyline) {
return "Linea sulla mappa";
}
return "Punto sulla mappa";
}
function supportsManagedMeasurements(record) {
return ["imported", "drawings"].includes(record.categoryKey)
&& (record.layer instanceof L.Circle
|| record.layer instanceof L.Polygon
|| record.layer instanceof L.Polyline);
}
function managedMeasurementsGloballyVisible(categoryKey) {
return categoryKey === "imported"
? lmeState.showImportedMeasurements
: lmeState.showDrawingMeasurements;
}
function managedMeasurementKinds(record) {
if (record.layer instanceof L.Polygon) {
return [
{ key: "summary", label: "Superficie e perimetro sulla mappa" },
{ key: "sides", label: "Misure dei lati sulla mappa" }
];
}
if (record.layer instanceof L.Circle) {
return [{ key: "summary", label: "Superficie, raggio e circonferenza sulla mappa" }];
}
return [
{ key: "summary", label: "Lunghezza totale sulla mappa" },
{ key: "sides", label: "Misure dei tratti sulla mappa" }
];
}
function managedMeasurementHiddenSet(kind) {
return kind === "sides"
? lmeState.managerHiddenMeasurementSideIds
: lmeState.managerHiddenMeasurementSummaryIds;
}
function setManagedMeasurementKindVisible(record, kind, visible) {
const hiddenSet = managedMeasurementHiddenSet(kind);
if (visible) {
if (!managedMeasurementsGloballyVisible(record.categoryKey)) {
lmeState.managerRecords.forEach(candidate => {
if (candidate.categoryKey === record.categoryKey
&& supportsManagedMeasurements(candidate)) {
lmeState.managerHiddenMeasurementSummaryIds.add(candidate.id);
lmeState.managerHiddenMeasurementSideIds.add(candidate.id);
}
});
if (record.categoryKey === "imported") {
lmeState.showImportedMeasurements = true;
} else {
lmeState.showDrawingMeasurements = true;
}
void lmeState.dotNetReference?.invokeMethodAsync(
"SetMeasurementsFromLayerManager",
record.categoryKey,
true);
}
hiddenSet.delete(record.id);
} else {
hiddenSet.add(record.id);
}
refreshImportedMeasurements();
refreshDrawingMeasurements();
refreshLayerManager();
}
function captureLayerManagerExpansion(container) {
container.querySelectorAll("details[data-expansion-key]").forEach(row => {
if (row.open) {
lmeState.managerExpandedRows.add(row.dataset.expansionKey);
} else {
lmeState.managerExpandedRows.delete(row.dataset.expansionKey);
}
});
}
function bindLayerManagerExpansion(row, key) {
row.dataset.expansionKey = key;
row.open = lmeState.managerExpandedRows.has(key);
row.addEventListener("toggle", () => {
if (row.open) {
lmeState.managerExpandedRows.add(key);
} else {
lmeState.managerExpandedRows.delete(key);
}
});
}
function setManagedCategoryVisible(category, visible) { function setManagedCategoryVisible(category, visible) {
if (category.key === "imported") { if (category.key === "imported") {
lmeState.importedVisible = visible; lmeState.importedVisible = visible;
@@ -1267,7 +1435,10 @@ function setManagedCategoryVisible(category, visible) {
if (visible) { if (visible) {
lmeState.managerHiddenCategories.delete(category.key); lmeState.managerHiddenCategories.delete(category.key);
category.roots.forEach(layer => { category.roots.forEach(layer => {
if (layer && !lmeState.map.hasLayer(layer)) { const objectId = managedLayerId(layer, category.key);
if (layer
&& !lmeState.managerHiddenObjectIds.has(objectId)
&& !lmeState.map.hasLayer(layer)) {
layer.addTo(lmeState.map); layer.addTo(lmeState.map);
} }
}); });
@@ -1278,15 +1449,23 @@ function setManagedCategoryVisible(category, visible) {
lmeState.map.removeLayer(layer); lmeState.map.removeLayer(layer);
} }
}); });
if (lmeState.managerHighlightedCategoryKey === category.key) {
clearManagerHighlight();
}
} }
saveLayerManagerState(); saveLayerManagerState();
refreshImportedMeasurements();
refreshDrawingMeasurements();
refreshLayerManager();
} }
function setManagedObjectVisible(record, visible, refresh = true) { function setManagedObjectVisible(record, visible, refresh = true) {
if (visible) { if (visible) {
const hidden = lmeState.managerHiddenObjects.get(record.id); const hidden = lmeState.managerHiddenObjects.get(record.id);
if (hidden) { if (hidden) {
if (hidden.parent !== lmeState.map || managedCategoryEnabled(record.categoryKey)) {
hidden.parent.addLayer(hidden.layer); hidden.parent.addLayer(hidden.layer);
}
lmeState.managerHiddenObjects.delete(record.id); lmeState.managerHiddenObjects.delete(record.id);
} }
lmeState.managerHiddenObjectIds.delete(record.id); lmeState.managerHiddenObjectIds.delete(record.id);
@@ -1294,6 +1473,9 @@ function setManagedObjectVisible(record, visible, refresh = true) {
lmeState.managerHiddenObjects.set(record.id, record); lmeState.managerHiddenObjects.set(record.id, record);
lmeState.managerHiddenObjectIds.add(record.id); lmeState.managerHiddenObjectIds.add(record.id);
record.parent.removeLayer(record.layer); record.parent.removeLayer(record.layer);
if (lmeState.managerHighlightedObjectId === record.id) {
clearManagerHighlight();
}
} }
saveLayerManagerState(); saveLayerManagerState();
refreshImportedMeasurements(); refreshImportedMeasurements();
@@ -1303,8 +1485,36 @@ function setManagedObjectVisible(record, visible, refresh = true) {
} }
} }
function managedCategoryEnabled(categoryKey) {
if (lmeState.managerHiddenCategories.has(categoryKey)) {
return false;
}
if (categoryKey === "gpsPosition") {
return lmeState.gpsPositionVisible;
}
if (categoryKey === "imported") {
return lmeState.importedVisible;
}
if (categoryKey === "importedSheets") {
return lmeState.importedSheetsVisible;
}
if (categoryKey === "selectedParcels") {
return lmeState.selectedParcelsVisible;
}
return true;
}
function highlightManagedObject(record) { function highlightManagedObject(record) {
lmeState.managerHighlight.clearLayers(); if (lmeState.managerHighlightedObjectId === record.id) {
clearManagerHighlight();
return;
}
clearManagerHighlight();
lmeState.managerHighlightedObjectId = record.id;
lmeState.managerHighlightedCategoryKey = record.categoryKey;
document.querySelectorAll(".layer-manager-object").forEach(row => {
row.classList.toggle("layer-manager-object-selected", row.dataset.objectId === record.id);
});
const geojson = record.layer.toGeoJSON?.(); const geojson = record.layer.toGeoJSON?.();
if (geojson) { if (geojson) {
L.geoJSON(geojson, { L.geoJSON(geojson, {
@@ -1325,6 +1535,19 @@ function highlightManagedObject(record) {
fitLayer(record.layer); fitLayer(record.layer);
} }
function clearManagerHighlight() {
lmeState.managerHighlight?.clearLayers();
lmeState.managerHighlightedObjectId = null;
lmeState.managerHighlightedCategoryKey = null;
document.querySelectorAll(".layer-manager-object-selected").forEach(row => {
row.classList.remove("layer-manager-object-selected");
});
}
function layerManagerMoveButtons(label) {
return `<span class="layer-move-buttons"><button type="button" class="layer-move-up" aria-label="Sposta ${label} su" title="Sposta su">↑</button><button type="button" class="layer-move-down" aria-label="Sposta ${label} giù" title="Sposta giù">↓</button></span>`;
}
function bindLayerManagerDrag(container) { function bindLayerManagerDrag(container) {
container.querySelectorAll(".layer-drag-handle").forEach(handle => { container.querySelectorAll(".layer-drag-handle").forEach(handle => {
handle.addEventListener("pointerdown", event => { handle.addEventListener("pointerdown", event => {
@@ -1335,6 +1558,7 @@ function bindLayerManagerDrag(container) {
row.classList.add("layer-manager-dragging"); row.classList.add("layer-manager-dragging");
handle.setPointerCapture?.(event.pointerId); handle.setPointerCapture?.(event.pointerId);
const move = moveEvent => { const move = moveEvent => {
moveEvent.preventDefault();
const target = document.elementFromPoint(moveEvent.clientX, moveEvent.clientY)?.closest(`[data-drag-type="${row.dataset.dragType}"]`); const target = document.elementFromPoint(moveEvent.clientX, moveEvent.clientY)?.closest(`[data-drag-type="${row.dataset.dragType}"]`);
if (!target || target === row || target.parentElement !== list) { if (!target || target === row || target.parentElement !== list) {
return; return;
@@ -1344,14 +1568,37 @@ function bindLayerManagerDrag(container) {
}; };
const end = () => { const end = () => {
row.classList.remove("layer-manager-dragging"); row.classList.remove("layer-manager-dragging");
handle.removeEventListener("pointermove", move); handle.releasePointerCapture?.(event.pointerId);
handle.removeEventListener("pointerup", end); document.removeEventListener("pointermove", move);
handle.removeEventListener("pointercancel", end); document.removeEventListener("pointerup", end);
document.removeEventListener("pointercancel", end);
applyLayerManagerDomOrder(container); applyLayerManagerDomOrder(container);
}; };
handle.addEventListener("pointermove", move); document.addEventListener("pointermove", move, { passive: false });
handle.addEventListener("pointerup", end); document.addEventListener("pointerup", end);
handle.addEventListener("pointercancel", end); document.addEventListener("pointercancel", end);
});
});
}
function bindLayerManagerMoveButtons(container) {
container.querySelectorAll(".layer-move-up, .layer-move-down").forEach(button => {
button.addEventListener("click", event => {
event.preventDefault();
event.stopPropagation();
const row = button.closest("[data-drag-type]");
const sibling = button.classList.contains("layer-move-up")
? row.previousElementSibling
: row.nextElementSibling;
if (!sibling || sibling.dataset.dragType !== row.dataset.dragType) {
return;
}
if (button.classList.contains("layer-move-up")) {
row.parentElement.insertBefore(row, sibling);
} else {
row.parentElement.insertBefore(sibling, row);
}
applyLayerManagerDomOrder(container);
}); });
}); });
} }
@@ -1520,6 +1767,7 @@ function applyImportedSheetVisibility() {
function setImportedMeasurementsVisible(enabled) { function setImportedMeasurementsVisible(enabled) {
lmeState.showImportedMeasurements = Boolean(enabled); lmeState.showImportedMeasurements = Boolean(enabled);
refreshImportedMeasurements(); refreshImportedMeasurements();
refreshLayerManager();
setInfo(lmeState.showImportedMeasurements setInfo(lmeState.showImportedMeasurements
? "Misure dei file GIS visibili." ? "Misure dei file GIS visibili."
: "Misure dei file GIS nascoste."); : "Misure dei file GIS nascoste.");
@@ -1528,6 +1776,7 @@ function setImportedMeasurementsVisible(enabled) {
function setDrawingMeasurementsVisible(enabled) { function setDrawingMeasurementsVisible(enabled) {
lmeState.showDrawingMeasurements = Boolean(enabled); lmeState.showDrawingMeasurements = Boolean(enabled);
refreshDrawingMeasurements(); refreshDrawingMeasurements();
refreshLayerManager();
setInfo(lmeState.showDrawingMeasurements setInfo(lmeState.showDrawingMeasurements
? "Misure dei disegni visibili." ? "Misure dei disegni visibili."
: "Misure dei disegni nascoste."); : "Misure dei disegni nascoste.");
@@ -1785,7 +2034,7 @@ function renderMeasurements(sourceGroup, targetGroup, visible, sourceType) {
return; return;
} }
targetGroup.clearLayers(); targetGroup.clearLayers();
if (!visible) { if (!visible || !lmeState.map.hasLayer(sourceGroup)) {
return; return;
} }
@@ -1797,15 +2046,27 @@ function renderMeasurements(sourceGroup, targetGroup, visible, sourceType) {
sourceType sourceType
}; };
visitMeasurableLayers(sourceGroup, layer => { visitMeasurableLayers(sourceGroup, layer => {
const categoryKey = sourceType === "imported" ? "imported" : "drawings";
const objectId = managedLayerId(layer, categoryKey);
const showSummary = !lmeState.managerHiddenMeasurementSummaryIds.has(objectId);
const showSides = !lmeState.managerHiddenMeasurementSideIds.has(objectId);
if (!showSummary && !showSides) {
return;
}
if (context.count >= context.max || !layerIsInView(layer, context.bounds)) { if (context.count >= context.max || !layerIsInView(layer, context.bounds)) {
return; return;
} }
const layerContext = {
...context,
showSummary,
showSides: context.showSides && showSides
};
if (layer instanceof L.Circle) { if (layer instanceof L.Circle) {
renderCircleMeasurement(layer, targetGroup, context); renderCircleMeasurement(layer, targetGroup, layerContext);
} else if (layer instanceof L.Polygon) { } else if (layer instanceof L.Polygon) {
renderPolygonMeasurement(layer, targetGroup, context); renderPolygonMeasurement(layer, targetGroup, layerContext);
} else if (layer instanceof L.Polyline) { } else if (layer instanceof L.Polyline) {
renderPolylineMeasurement(layer, targetGroup, context); renderPolylineMeasurement(layer, targetGroup, layerContext);
} }
}); });
} }
@@ -1832,6 +2093,9 @@ function layerIsInView(layer, mapBounds) {
} }
function renderCircleMeasurement(layer, targetGroup, context) { function renderCircleMeasurement(layer, targetGroup, context) {
if (!context.showSummary) {
return;
}
const radius = layer.getRadius(); const radius = layer.getRadius();
const area = Math.PI * radius * radius; const area = Math.PI * radius * radius;
const circumference = 2 * Math.PI * radius; const circumference = 2 * Math.PI * radius;
@@ -1860,6 +2124,7 @@ function renderPolygonMeasurement(layer, targetGroup, context) {
const center = typeof layer.getCenter === "function" const center = typeof layer.getCenter === "function"
? layer.getCenter() ? layer.getCenter()
: layer.getBounds().getCenter(); : layer.getBounds().getCenter();
if (context.showSummary) {
addMeasurementLabel( addMeasurementLabel(
targetGroup, targetGroup,
center, center,
@@ -1867,6 +2132,7 @@ function renderPolygonMeasurement(layer, targetGroup, context) {
"summary", "summary",
context); context);
} }
}
function renderPolylineMeasurement(layer, targetGroup, context) { function renderPolylineMeasurement(layer, targetGroup, context) {
const lines = lineLatLngGroups(layer.getLatLngs()); const lines = lineLatLngGroups(layer.getLatLngs());
@@ -1875,7 +2141,7 @@ function renderPolylineMeasurement(layer, targetGroup, context) {
lines.forEach(line => addSegmentLabels(line, false, targetGroup, context)); lines.forEach(line => addSegmentLabels(line, false, targetGroup, context));
} }
const longest = lines.reduce((current, line) => line.length > current.length ? line : current, []); const longest = lines.reduce((current, line) => line.length > current.length ? line : current, []);
if (longest.length) { if (context.showSummary && longest.length) {
addMeasurementLabel( addMeasurementLabel(
targetGroup, targetGroup,
longest[Math.floor(longest.length / 2)], longest[Math.floor(longest.length / 2)],
@@ -2222,6 +2488,8 @@ function captureDisplayOptions() {
layerOrder: [...lmeState.layerOrder], layerOrder: [...lmeState.layerOrder],
layerObjectOrder: { ...lmeState.managerObjectOrder }, layerObjectOrder: { ...lmeState.managerObjectOrder },
hiddenLayerObjects: [...lmeState.managerHiddenObjectIds], hiddenLayerObjects: [...lmeState.managerHiddenObjectIds],
hiddenLayerMeasurementSummaries: [...lmeState.managerHiddenMeasurementSummaryIds],
hiddenLayerMeasurementSides: [...lmeState.managerHiddenMeasurementSideIds],
hiddenLayerCategories: [...lmeState.managerHiddenCategories], hiddenLayerCategories: [...lmeState.managerHiddenCategories],
trailEnabled: lmeState.gpsTrailEnabled trailEnabled: lmeState.gpsTrailEnabled
}; };
@@ -2258,6 +2526,12 @@ function applyProjectDisplayOptions(options) {
if (Array.isArray(options.hiddenLayerObjects)) { if (Array.isArray(options.hiddenLayerObjects)) {
lmeState.managerHiddenObjectIds = new Set(options.hiddenLayerObjects); lmeState.managerHiddenObjectIds = new Set(options.hiddenLayerObjects);
} }
if (Array.isArray(options.hiddenLayerMeasurementSummaries)) {
lmeState.managerHiddenMeasurementSummaryIds = new Set(options.hiddenLayerMeasurementSummaries);
}
if (Array.isArray(options.hiddenLayerMeasurementSides)) {
lmeState.managerHiddenMeasurementSideIds = new Set(options.hiddenLayerMeasurementSides);
}
if (Array.isArray(options.hiddenLayerCategories)) { if (Array.isArray(options.hiddenLayerCategories)) {
lmeState.managerHiddenCategories = new Set(options.hiddenLayerCategories); lmeState.managerHiddenCategories = new Set(options.hiddenLayerCategories);
} }
@@ -2341,12 +2615,21 @@ function managedGroupGeoJson(group, categoryKey) {
function resetLayerManagerProjectState() { function resetLayerManagerProjectState() {
lmeState.managerHiddenObjects.clear(); lmeState.managerHiddenObjects.clear();
lmeState.managerHiddenObjectIds.clear(); lmeState.managerHiddenObjectIds.clear();
lmeState.managerHiddenMeasurementSummaryIds.clear();
lmeState.managerHiddenMeasurementSideIds.clear();
lmeState.managerHiddenCategories.clear(); lmeState.managerHiddenCategories.clear();
lmeState.managerObjectOrder = {}; lmeState.managerObjectOrder = {};
lmeState.managerHighlight?.clearLayers(); clearManagerHighlight();
} }
function clearManagedCategoryState(categoryKey) { function clearManagedCategoryState(categoryKey) {
const category = layerManagerCategories().find(item => item.key === categoryKey);
if (category) {
managedRecordsForCategory(category).forEach(record => {
lmeState.managerHiddenMeasurementSummaryIds.delete(record.id);
lmeState.managerHiddenMeasurementSideIds.delete(record.id);
});
}
[...lmeState.managerHiddenObjects.entries()].forEach(([id, record]) => { [...lmeState.managerHiddenObjects.entries()].forEach(([id, record]) => {
if (record.categoryKey === categoryKey) { if (record.categoryKey === categoryKey) {
lmeState.managerHiddenObjects.delete(id); lmeState.managerHiddenObjects.delete(id);
@@ -2809,11 +3092,11 @@ function onGpsPosition(position) {
} }
function updateGpsPositionLayers(latlng, accuracy) { function updateGpsPositionLayers(latlng, accuracy) {
if (!lmeState.gpsPositionVisible) { let created = false;
return;
}
if (!lmeState.gpsMarker) { if (!lmeState.gpsMarker) {
lmeState.gpsMarker = L.marker(latlng, { pane: "gpsPositionPane", title: "La tua posizione" }); lmeState.gpsMarker = L.marker(latlng, { pane: "gpsPositionPane", title: "La tua posizione" });
lmeState.gpsMarker.lmeManagerId = "gps-position-marker";
lmeState.gpsMarker.lmeManagerLabel = "Posizione GPS";
lmeState.gpsAccuracy = L.circle(latlng, { lmeState.gpsAccuracy = L.circle(latlng, {
pane: "gpsPositionPane", pane: "gpsPositionPane",
radius: accuracy, radius: accuracy,
@@ -2822,15 +3105,33 @@ function updateGpsPositionLayers(latlng, accuracy) {
fillColor: "#60a5fa", fillColor: "#60a5fa",
fillOpacity: 0.16 fillOpacity: 0.16
}); });
lmeState.gpsAccuracy.lmeManagerId = "gps-accuracy-circle";
lmeState.gpsAccuracy.lmeManagerLabel = "Cerchio precisione GPS";
created = true;
} else { } else {
lmeState.gpsMarker.setLatLng(latlng); lmeState.gpsMarker.setLatLng(latlng);
lmeState.gpsAccuracy.setLatLng(latlng).setRadius(accuracy); lmeState.gpsAccuracy.setLatLng(latlng).setRadius(accuracy);
} }
if (!lmeState.map.hasLayer(lmeState.gpsMarker)) { const categoryVisible = lmeState.gpsPositionVisible
&& !lmeState.managerHiddenCategories.has("gpsPosition");
if (categoryVisible
&& !lmeState.managerHiddenObjectIds.has("gps-position-marker")
&& !lmeState.map.hasLayer(lmeState.gpsMarker)) {
lmeState.gpsMarker.addTo(lmeState.map); lmeState.gpsMarker.addTo(lmeState.map);
} else if ((!categoryVisible || lmeState.managerHiddenObjectIds.has("gps-position-marker"))
&& lmeState.map.hasLayer(lmeState.gpsMarker)) {
lmeState.map.removeLayer(lmeState.gpsMarker);
} }
if (!lmeState.map.hasLayer(lmeState.gpsAccuracy)) { if (categoryVisible
&& !lmeState.managerHiddenObjectIds.has("gps-accuracy-circle")
&& !lmeState.map.hasLayer(lmeState.gpsAccuracy)) {
lmeState.gpsAccuracy.addTo(lmeState.map); lmeState.gpsAccuracy.addTo(lmeState.map);
} else if ((!categoryVisible || lmeState.managerHiddenObjectIds.has("gps-accuracy-circle"))
&& lmeState.map.hasLayer(lmeState.gpsAccuracy)) {
lmeState.map.removeLayer(lmeState.gpsAccuracy);
}
if (created) {
refreshLayerManager();
} }
} }