@@ -2493,8 +2511,7 @@ function renderLayerSelectionToolbar() {
const records = [...lmeState.managerSelectedObjectIds]
.map(id => lmeState.managerRecords?.get(id)).filter(Boolean);
if (button.dataset.action === "clear") {
- lmeState.managerSelectedObjectIds.clear();
- refreshLayerManager();
+ clearManagedSelection();
} else if (!records.length) {
setInfo("Seleziona almeno un oggetto dalle sue opzioni.");
} else if (button.dataset.action === "duplicate") duplicateManagedRecords(records);
@@ -2514,16 +2531,19 @@ function renderManagedGroupTools(records) {
const properties = firstLayer?.feature?.properties || {};
const lineColor = normalizedColor(properties._lmeColor || firstLayer?.options?.color, "#16806a");
const fillColor = normalizedColor(properties._lmeFillColor || firstLayer?.options?.fillColor, lineColor);
+ const selectionColor = normalizedColor(properties._lmeSelectionColor, "#d500f9");
tools.innerHTML = `
${managedColorEditorMarkup("color", "Colore linea", lineColor)}
${managedColorEditorMarkup("fillColor", "Colore riempimento", fillColor)}
+ ${managedColorEditorMarkup("selectionColor", "Colore selezione", selectionColor)}
Spessore
Opacita
`;
const applyCurrentStyle = () => {
applyManagedStyle(records, {
color: tools.querySelector("[data-color-picker='color']").value,
fillColor: tools.querySelector("[data-color-picker='fillColor']").value,
+ selectionColor: tools.querySelector("[data-color-picker='selectionColor']").value,
weight: Number(tools.querySelector("[data-field='weight']").value),
opacity: Number(tools.querySelector("[data-field='opacity']").value)
});
@@ -2531,7 +2551,18 @@ function renderManagedGroupTools(records) {
bindManagedColorEditors(tools, applyCurrentStyle);
tools.querySelectorAll("input[type='range']").forEach(input => input.addEventListener("change", applyCurrentStyle));
tools.querySelector("[data-group-action='select']").addEventListener("click", () => {
- records.forEach(record => lmeState.managerSelectedObjectIds.add(record.id)); refreshLayerManager();
+ if (lmeState.selectionMode === "off") {
+ setInfo("Selezione disattivata. Scegli Singola o Multipla dalla barra mappa.");
+ return;
+ }
+ if (lmeState.selectionMode === "single") {
+ selectManagedRecord(records[0]);
+ return;
+ }
+ records.forEach(record => lmeState.managerSelectedObjectIds.add(record.id));
+ refreshLayerManager();
+ refreshMultipleMapSelection(records.at(-1));
+ setInfo(`${lmeState.managerSelectedObjectIds.size} oggetti selezionati.`);
});
tools.querySelector("[data-group-action='lock']").addEventListener("click", () => {
const lock = !records.every(record => Boolean(record.layer?.feature?.properties?._lmeLocked));
@@ -2658,11 +2689,13 @@ function renderManagedObjectTools(record) {
tools.className = "layer-object-tools";
const lineColor = normalizedColor(properties._lmeColor || record.layer?.options?.color, "#f97316");
const fillColor = normalizedColor(properties._lmeFillColor || record.layer?.options?.fillColor, lineColor);
- tools.innerHTML = `
+ const selectionColor = normalizedColor(properties._lmeSelectionColor, "#d500f9");
+ tools.innerHTML = `
${managedColorEditorMarkup("color", "Colore linea", lineColor)}
${managedColorEditorMarkup("fillColor", "Colore riempimento", fillColor)}
+ ${managedColorEditorMarkup("selectionColor", "Colore selezione", selectionColor)}
Spessore
Opacita
Riempimento %
@@ -2672,15 +2705,25 @@ function renderManagedObjectTools(record) {
${properties._lmePhoto ? `
Rimuovi foto ` : ""}
Attributi Duplica Coordinate WGS84 / UTM Sposta geometria Buffer
`;
tools.querySelector("[data-field='selected']").addEventListener("change", event => {
- if (event.target.checked) lmeState.managerSelectedObjectIds.add(record.id);
- else lmeState.managerSelectedObjectIds.delete(record.id);
- refreshLayerManager();
+ if (lmeState.selectionMode === "off") {
+ event.target.checked = lmeState.managerSelectedObjectIds.has(record.id);
+ setInfo("Selezione disattivata. Scegli Singola o Multipla dalla barra mappa.");
+ return;
+ }
+ if (event.target.checked) {
+ selectManagedRecord(record);
+ } else {
+ lmeState.managerSelectedObjectIds.delete(record.id);
+ refreshLayerManager();
+ refreshMultipleMapSelection();
+ }
});
tools.querySelector("[data-field='locked']").addEventListener("change", event => updateManagedProperties([record], value => { value._lmeLocked = event.target.checked; }));
tools.querySelector("[data-field='label']").addEventListener("change", event => updateManagedProperties([record], value => { value._lmeLabelVisible = event.target.checked; }));
const applyCurrentStyle = () => applyManagedStyle([record], {
color: tools.querySelector("[data-color-picker='color']").value,
fillColor: tools.querySelector("[data-color-picker='fillColor']").value,
+ selectionColor: tools.querySelector("[data-color-picker='selectionColor']").value,
weight: Number(tools.querySelector("[data-field='weight']").value),
opacity: Number(tools.querySelector("[data-field='opacity']").value),
fillOpacity: Number(tools.querySelector("[data-field='fillOpacity']").value),
@@ -3005,24 +3048,35 @@ function showObjectTable() {
const details = managedObjectDetails(record.layer);
const selected = lmeState.managerSelectedObjectIds.has(record.id);
const locked = Boolean(properties._lmeLocked);
- return `
⌖ ✎ Note (${managedNotes(properties).length}) ${escapeHtml(managedObjectLabel(record, index))} ${escapeHtml(managedCategoryName(layerManagerCategories().find(item => item.key === record.categoryKey) || { key: record.categoryKey, label: record.categoryKey }, [record]))} ${escapeHtml(properties.foglio || properties.FOGLIO || "")} ${escapeHtml(properties.particella || properties.PARTICELLA || properties.LABEL || "")} ${escapeHtml(details.slice(0, 2).join(" | "))} `;
+ return `
⌖ ✎ Note (${managedNotes(properties).length}) ${escapeHtml(managedObjectLabel(record, index))} ${escapeHtml(managedCategoryName(layerManagerCategories().find(item => item.key === record.categoryKey) || { key: record.categoryKey, label: record.categoryKey }, [record]))} ${escapeHtml(properties.foglio || properties.FOGLIO || "")} ${escapeHtml(properties.particella || properties.PARTICELLA || properties.LABEL || "")} ${escapeHtml(details.slice(0, 2).join(" | "))} `;
}).join("");
- const dialog = showAppDialog("Elenco oggetti e particelle", `
Filtra Seleziona più oggetti con i flag nella colonna Selez. Il lucchetto impedisce modifiche e rimozioni; la matita modifica i metadati. Note apre l'elenco completo; le modifiche richiedono LME Pro.
Selez. Blocca Mappa Metadati Note Nome Gruppo Foglio Particella Misure ${body || "Nessun oggetto "}
`);
+ const dialog = showAppDialog("Elenco oggetti e particelle", `
Filtra Modalita: ${escapeHtml(selectionModeLabel())} . La selezione e condivisa con mappa e pannello Layer. Cancella selezione Il lucchetto impedisce modifiche e rimozioni; la matita modifica i metadati. Note apre l'elenco completo; le modifiche richiedono LME Pro.
Selez. Blocca Mappa Metadati Note Nome Gruppo Foglio Particella Misure ${body || "Nessun oggetto "}
`);
const byId = new Map(records.map(record => [record.id, record]));
dialog.querySelector("input[type='search']").addEventListener("input", event => {
const query = event.target.value.toLocaleLowerCase("it");
dialog.querySelectorAll("tbody tr").forEach(row => { row.hidden = query && !row.textContent.toLocaleLowerCase("it").includes(query); });
});
+ dialog.querySelector("[data-table-clear]").addEventListener("click", () => {
+ clearManagedSelection();
+ dialog.querySelectorAll("[data-select]").forEach(candidate => {
+ candidate.checked = false;
+ candidate.closest("tr")?.classList.remove("object-table-selected");
+ });
+ });
dialog.querySelectorAll("[data-select]").forEach(input => input.addEventListener("change", () => {
- if (input.checked) lmeState.managerSelectedObjectIds.add(input.dataset.select);
- else lmeState.managerSelectedObjectIds.delete(input.dataset.select);
const record = byId.get(input.dataset.select);
- if (record && input.checked) {
- lmeState.managerHighlightedObjectId = record.id;
- lmeState.managerHighlightedCategoryKey = record.categoryKey;
- renderManagerHighlight([record]);
+ if (!record) return;
+ if (input.checked) {
+ selectManagedRecord(record);
+ } else {
+ lmeState.managerSelectedObjectIds.delete(record.id);
+ refreshLayerManager();
+ refreshMultipleMapSelection();
}
- refreshLayerManager();
+ dialog.querySelectorAll("[data-select]").forEach(candidate => {
+ candidate.checked = lmeState.managerSelectedObjectIds.has(candidate.dataset.select);
+ candidate.closest("tr")?.classList.toggle("object-table-selected", candidate.checked);
+ });
setInfo(`${lmeState.managerSelectedObjectIds.size} oggetti selezionati.`);
}));
dialog.querySelectorAll("[data-lock]").forEach(input => input.addEventListener("change", () => {
@@ -3033,7 +3087,9 @@ function showObjectTable() {
}));
dialog.querySelectorAll("[data-locate]").forEach(button => button.addEventListener("click", () => {
const record = byId.get(button.dataset.locate); if (!record) return;
- dialog.remove(); highlightManagedObject(record);
+ dialog.remove();
+ if (lmeState.selectionMode !== "off") selectManagedRecord(record);
+ fitLayer(record.layer);
}));
dialog.querySelectorAll("[data-attributes]").forEach(button => button.addEventListener("click", () => {
const record = byId.get(button.dataset.attributes); if (!record) return;
@@ -3054,6 +3110,7 @@ function showAuditLog() {
function updateManagedProperties(records, change) {
if (lmeState.readOnly) { setInfo("Il progetto e' in sola lettura."); return; }
+ restoreDirectManagerHighlights();
rememberMapChange();
records.forEach(record => {
record.layer.feature ||= { type: "Feature", properties: {} };
@@ -3073,6 +3130,7 @@ function applyManagedStyle(records, style) {
properties._lmeFillColor = style.fillColor || style.color;
properties._lmeWeight = style.weight;
properties._lmeOpacity = style.opacity;
+ if (style.selectionColor !== undefined) properties._lmeSelectionColor = style.selectionColor;
if (style.fillOpacity !== undefined) properties._lmeFillOpacity = style.fillOpacity;
if (style.dash !== undefined) properties._lmeDash = style.dash;
});
@@ -3081,6 +3139,7 @@ function applyManagedStyle(records, style) {
function applyStyleFromProperties(layer) {
const properties = layer?.feature?.properties || {};
if (typeof layer?.setStyle === "function") {
+ lmeState.managerDirectHighlightStyles.delete(layer);
layer.setStyle({
color: properties._lmeColor || layer.options.color,
fillColor: properties._lmeFillColor || layer.options.fillColor,
@@ -3419,18 +3478,8 @@ function hideComparisonMenu() {
lmeState.comparisonMenu = null;
}
-function toggleMultiSelection() {
- lmeState.multiSelectionEnabled = !lmeState.multiSelectionEnabled;
- lmeState.multiSelectionButton?.classList.toggle("is-active", lmeState.multiSelectionEnabled);
- lmeState.multiSelectionButton?.setAttribute("aria-pressed", String(lmeState.multiSelectionEnabled));
- setInfo(lmeState.multiSelectionEnabled
- ? "Selezione multipla attiva: tocca gli oggetti per aggiungerli o rimuoverli dalla selezione."
- : "Selezione multipla disattivata.");
- return lmeState.multiSelectionEnabled;
-}
-
-function multiSelectionHasObjects() {
- return lmeState.multiSelectionEnabled && lmeState.managerSelectedObjectIds.size > 0;
+function selectionHasObjects() {
+ return lmeState.managerSelectedObjectIds.size > 0;
}
function refreshMultipleMapSelection(preferredRecord) {
@@ -3440,9 +3489,119 @@ function refreshMultipleMapSelection(preferredRecord) {
const current = selectedRecords.find(record => record.id === preferredRecord?.id) || selectedRecords.at(-1);
lmeState.managerHighlightedObjectId = current?.id || null;
lmeState.managerHighlightedCategoryKey = current?.categoryKey || null;
+ setSelectedDrawing(selectedRecords.length === 1 && current?.categoryKey === "drawings" ? current.layer : null);
renderManagerHighlight(selectedRecords);
}
+function selectionModeLabel(mode = lmeState.selectionMode) {
+ return mode === "multiple" ? "Multipla" : mode === "off" ? "Disattivata" : "Singola";
+}
+
+function selectionModeIcon(mode = lmeState.selectionMode) {
+ return mode === "multiple" ? "☑" : mode === "off" ? "×" : "✓";
+}
+
+function updateSelectionModeButton() {
+ const button = lmeState.selectionModeButton;
+ if (!button) return;
+ const label = `Selezione: ${selectionModeLabel()}`;
+ button.textContent = selectionModeIcon();
+ button.title = `${label}. Apri le opzioni`;
+ button.setAttribute("aria-label", button.title);
+ button.setAttribute("aria-pressed", String(lmeState.selectionMode !== "off"));
+ button.classList.toggle("is-active", lmeState.selectionMode !== "off");
+ button.classList.toggle("is-multiple", lmeState.selectionMode === "multiple");
+}
+
+function setSelectionMode(mode) {
+ const normalized = ["single", "multiple", "off"].includes(mode) ? mode : "single";
+ lmeState.selectionMode = normalized;
+ if (normalized === "off") {
+ lmeState.managerSelectedObjectIds.clear();
+ clearManagerHighlight();
+ } else if (normalized === "single" && lmeState.managerSelectedObjectIds.size > 1) {
+ const selectedIds = [...lmeState.managerSelectedObjectIds];
+ const retained = lmeState.managerSelectedObjectIds.has(lmeState.managerHighlightedObjectId)
+ ? lmeState.managerHighlightedObjectId
+ : selectedIds.at(-1);
+ lmeState.managerSelectedObjectIds.clear();
+ if (retained) lmeState.managerSelectedObjectIds.add(retained);
+ }
+ updateSelectionModeButton();
+ refreshLayerManager();
+ renderSelectedManagerHighlight();
+ setInfo(normalized === "single"
+ ? "Selezione singola attiva: tocca un oggetto per sostituire la selezione corrente."
+ : normalized === "multiple"
+ ? "Selezione multipla attiva: tocca gli oggetti per aggiungerli o rimuoverli."
+ : "Selezione disattivata e selezione corrente cancellata. Nessun oggetto e' stato rimosso.");
+ return normalized;
+}
+
+function clearManagedSelection(announce = true) {
+ lmeState.managerSelectedObjectIds.clear();
+ clearManagerHighlight();
+ refreshLayerManager();
+ if (announce) setInfo("Selezione cancellata. Nessun oggetto e' stato rimosso.");
+}
+
+function selectManagedRecord(record, event, announce = true) {
+ if (!record) return false;
+ if (lmeState.selectionMode === "off") {
+ if (announce) setInfo("Selezione disattivata. Attivala dal pulsante Selezione nella barra mappa.");
+ return false;
+ }
+ const multiple = lmeState.selectionMode === "multiple"
+ || event?.ctrlKey || event?.metaKey || event?.shiftKey;
+ if (multiple) {
+ if (lmeState.managerSelectedObjectIds.has(record.id)) lmeState.managerSelectedObjectIds.delete(record.id);
+ else lmeState.managerSelectedObjectIds.add(record.id);
+ } else {
+ lmeState.managerSelectedObjectIds.clear();
+ lmeState.managerSelectedObjectIds.add(record.id);
+ }
+ refreshLayerManager();
+ refreshMultipleMapSelection(record);
+ if (lmeState.managerSelectedObjectIds.has(record.id)) {
+ window.requestAnimationFrame(() => revealManagedRecordInLayerManager(record.id));
+ }
+ if (announce) setInfo(`${lmeState.managerSelectedObjectIds.size} oggetti selezionati (${selectionModeLabel().toLocaleLowerCase("it")}).`);
+ return true;
+}
+
+function showSelectionMenu(button) {
+ if (lmeState.selectionMenu) {
+ hideSelectionMenu();
+ return;
+ }
+ const menu = L.DomUtil.create("div", "lme-selection-actions", button.parentElement);
+ menu.style.top = `${button.offsetTop}px`;
+ menu.innerHTML = `
✓ Selezione singola
+
☑ Selezione multipla
+
× Selezione disattivata
+
⌫ Cancella selezione `;
+ lmeState.selectionMenu = menu;
+ menu.querySelectorAll("a").forEach(link => {
+ const active = link.dataset.selectionMode === lmeState.selectionMode;
+ link.classList.toggle("is-current", active);
+ link.setAttribute("aria-current", active ? "true" : "false");
+ L.DomEvent.on(link, "click", L.DomEvent.stop)
+ .on(link, "mousedown", L.DomEvent.stopPropagation)
+ .on(link, "touchstart", L.DomEvent.stopPropagation)
+ .on(link, "click", () => {
+ const mode = link.dataset.selectionMode;
+ hideSelectionMenu();
+ if (mode) setSelectionMode(mode);
+ else clearManagedSelection();
+ });
+ });
+}
+
+function hideSelectionMenu() {
+ lmeState.selectionMenu?.remove();
+ lmeState.selectionMenu = null;
+}
+
function showGeometryResult(title, message, tone = "success") {
setInfo(`${title}: ${message}`);
const icons = { success: "✓", warning: "!", error: "×" };
@@ -3464,7 +3623,7 @@ function showLeafletToolbarHelp() {
▱ Elimina disegni Premi il cestino e scegli Solo selezionato oppure Tutti. L'oggetto rimosso può essere recuperato con Annulla o dal cestino negli strumenti avanzati.
✓ Controlla e ripara Verifica gli oggetti selezionati e prova a correggere coordinate duplicate o poligoni non validi. Esempio: usalo quando un contorno importato non si esporta o si riempie male; al termine compare un riepilogo.
◉ Crea foro Seleziona esattamente due poligoni: prima l'area esterna, poi quella interna. Esempio: sottrai il cortile dal perimetro di un fabbricato o un'isola esclusa da un'area di lavoro.
-
☑ Selezione multipla Attiva il comando e tocca più oggetti sulla mappa per aggiungerli o rimuoverli dalla selezione. Gli oggetti restano evidenziati anche aprendo e chiudendo i pannelli. Disattivalo per tornare alla selezione singola.
+
✓ Modalita di selezione Scegli Singola, Multipla o Disattivata. Mappa, pannello Layer e tabella oggetti condividono la stessa selezione. Disattivata e Cancella selezione deselezionano tutto senza eliminare alcun oggetto.
⇄ Confronta due confini Apri il comando e scegli Confronta dopo aver selezionato esattamente due poligoni. Il gruppo Confronto confini viene collocato sopra gli altri layer: rosso indica solo il primo, blu solo il secondo e verde la sovrapposizione. Usa Pulisci confronto per rimuovere soltanto questi risultati.
PF Scarica Punti Fiduciali Seleziona una particella o un foglio catastale; in alternativa usa la particella rilevata dal GPS. I PF vengono aggiunti come sottogruppo del relativo foglio e richiedono LME Pro.
? Guida Riapre questa descrizione. I risultati di riparazione e confronto vengono mostrati anche in una finestra; per uscire dalla visualizzazione rosso, blu e verde usa Pulisci confronto.
@@ -3481,7 +3640,7 @@ function addGeometryToolsControl() {
const actions = [
{ icon: "✓", title: "Controlla e ripara geometrie", run: validateAndRepairSelected },
{ icon: "◉", title: "Crea foro da due poligoni", run: createHoleFromSelected },
- { icon: "☑", title: "Attiva o disattiva selezione multipla", run: toggleMultiSelection, multiSelection: true },
+ { icon: selectionModeIcon(), title: "Modalita e comandi di selezione", selection: true },
{ icon: "⇄", title: "Confronta o pulisci confronto", comparison: true },
{ icon: "PF", title: "Scarica PF del foglio o della particella", run: downloadFiducialsForMapContext, fiducials: true },
{ icon: "?", title: "Guida strumenti mappa", run: showLeafletToolbarHelp }
@@ -3498,14 +3657,15 @@ function addGeometryToolsControl() {
lmeState.fiducialDownloadButton = button;
}
if (action.comparison) button.classList.add("lme-comparison-button");
- if (action.multiSelection) {
- button.classList.add("lme-multi-selection-button");
- button.setAttribute("aria-pressed", String(lmeState.multiSelectionEnabled));
- lmeState.multiSelectionButton = button;
+ if (action.selection) {
+ button.classList.add("lme-selection-mode-button");
+ lmeState.selectionModeButton = button;
+ updateSelectionModeButton();
}
L.DomEvent.on(button, "click", event => {
L.DomEvent.stop(event);
if (action.comparison) showComparisonMenu(button);
+ else if (action.selection) showSelectionMenu(button);
else action.run();
});
});
@@ -4000,22 +4160,10 @@ function managedCategoryEnabled(categoryKey) {
}
function highlightManagedObject(record) {
- 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);
- });
- setSelectedDrawing(record.categoryKey === "drawings" ? record.layer : null);
- renderManagerHighlight([record]);
+ if (!selectManagedRecord(record)) return;
if (lmeState.autoFitSelectedObject) {
fitLayer(record.layer);
}
- revealManagedRecordInLayerManager(record.id);
}
function revealManagedRecordInLayerManager(recordId) {
@@ -4060,7 +4208,7 @@ function setSelectedDrawing(layer) {
}
function selectDrawingLayer(layer) {
- if (drawingEditActive()) {
+ if (drawingEditActive() || lmeState.selectionMode === "off") {
return;
}
const record = {
@@ -4070,14 +4218,10 @@ function selectDrawingLayer(layer) {
parent: lmeState.drawings,
source: String(layer?.feature?.properties?._lmeSource || "Progetto corrente")
};
- clearManagerHighlight();
- lmeState.managerHighlightedObjectId = record.id;
- lmeState.managerHighlightedCategoryKey = "drawings";
- document.querySelectorAll(".layer-manager-object").forEach(row => {
- row.classList.toggle("layer-manager-object-selected", row.dataset.objectId === record.id);
- });
- setSelectedDrawing(layer);
- renderManagerHighlight([record]);
+ lmeState.managerSelectedObjectIds.clear();
+ lmeState.managerSelectedObjectIds.add(record.id);
+ refreshLayerManager();
+ refreshMultipleMapSelection(record);
setInfo(`Selezione: ${managedObjectLabel(record, 0)}. Usa la matita per modificare o il cestino per eliminare.`);
}
@@ -4111,6 +4255,10 @@ function bindManagedParentHighlight(row, key, records) {
}
function highlightManagedParent(row, key, records) {
+ if (lmeState.selectionMode === "off") {
+ setInfo("Selezione disattivata. Scegli Singola o Multipla dalla barra mappa.");
+ return;
+ }
if (lmeState.managerHighlightedParentKey === key) {
clearManagerHighlight();
return;
@@ -4125,7 +4273,7 @@ function highlightManagedParent(row, key, records) {
}
function renderSelectedManagerHighlight() {
- if (multiSelectionHasObjects()) {
+ if (selectionHasObjects()) {
const selected = [...lmeState.managerSelectedObjectIds]
.map(id => lmeState.managerRecords.get(id))
.filter(Boolean);
@@ -4143,13 +4291,18 @@ function renderSelectedManagerHighlight() {
function renderManagerHighlight(records) {
restoreDirectManagerHighlights();
lmeState.managerHighlight?.clearLayers();
- records.forEach(record => applyDirectManagerHighlight(record.layer));
+ records.forEach(record => applyDirectManagerHighlight(record));
const features = records.flatMap(record => {
const geojson = record.layer?.toGeoJSON?.();
if (!geojson) {
return [];
}
- return geojson.type === "FeatureCollection" ? geojson.features : [geojson];
+ const selectionColor = managedSelectionColor(record.layer);
+ const items = geojson.type === "FeatureCollection" ? geojson.features : [geojson];
+ return items.map(feature => ({
+ ...feature,
+ properties: { ...(feature.properties || {}), _lmeSelectionColorResolved: selectionColor }
+ }));
});
if (!features.length) {
return;
@@ -4157,22 +4310,34 @@ function renderManagerHighlight(records) {
L.geoJSON({ type: "FeatureCollection", features }, {
pane: "managerHighlightPane",
interactive: false,
- style: { color: "#ff00d4", weight: 8, opacity: 1, fillColor: "#00fff0", fillOpacity: 0.16 },
+ style: feature => ({
+ color: managedSelectionColor(null, feature?.properties?._lmeSelectionColorResolved),
+ weight: 8,
+ opacity: 1,
+ fillColor: managedSelectionColor(null, feature?.properties?._lmeSelectionColorResolved),
+ fillOpacity: 0.22
+ }),
pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
pane: "managerHighlightPane",
radius: 12,
- color: "#ff00d4",
+ color: managedSelectionColor(null, feature?.properties?._lmeSelectionColorResolved),
weight: 5,
- fillColor: "#00fff0",
+ fillColor: managedSelectionColor(null, feature?.properties?._lmeSelectionColorResolved),
fillOpacity: 0.9,
interactive: false
})
}).addTo(lmeState.managerHighlight);
}
-function applyDirectManagerHighlight(layer) {
+function managedSelectionColor(layer, explicitColor) {
+ return normalizedColor(explicitColor || layer?.feature?.properties?._lmeSelectionColor, "#d500f9");
+}
+
+function applyDirectManagerHighlight(record) {
+ const layer = record?.layer;
if (!layer || typeof layer.setStyle !== "function" || lmeState.managerDirectHighlightStyles.has(layer)) return;
const options = layer.options || {};
+ const selectionColor = managedSelectionColor(layer);
lmeState.managerDirectHighlightStyles.set(layer, {
color: options.color,
weight: options.weight,
@@ -4182,10 +4347,10 @@ function applyDirectManagerHighlight(layer) {
dashArray: options.dashArray ?? null
});
layer.setStyle({
- color: "#ff00d4",
+ color: selectionColor,
weight: Math.max(7, (Number(options.weight) || 2) + 4),
opacity: 1,
- fillColor: "#00fff0",
+ fillColor: selectionColor,
fillOpacity: 0.34,
dashArray: null
});
@@ -4933,7 +5098,7 @@ function bindDrawingLayerSelection(layer) {
}
layer.lmeDrawingSelectionBound = true;
layer.on("click", event => {
- if (drawingEditActive()) {
+ if (drawingEditActive() || lmeState.selectionMode === "off") {
return;
}
lmeState.ignoreMapClickUntil = Date.now() + 1000;
@@ -4941,28 +5106,14 @@ function bindDrawingLayerSelection(layer) {
event.originalEvent._lmeDrawingSelected = true;
L.DomEvent.stopPropagation(event.originalEvent);
}
- const id = managedLayerId(layer, "drawings");
- const multiple = lmeState.multiSelectionEnabled
- || event.originalEvent?.ctrlKey
- || event.originalEvent?.metaKey
- || event.originalEvent?.shiftKey;
- if (multiple) {
- if (lmeState.managerSelectedObjectIds.has(id)) lmeState.managerSelectedObjectIds.delete(id);
- else lmeState.managerSelectedObjectIds.add(id);
- } else {
- lmeState.managerSelectedObjectIds.clear();
- lmeState.managerSelectedObjectIds.add(id);
- }
- refreshLayerManager();
- if (multiple) {
- refreshMultipleMapSelection({ id, categoryKey: "drawings", layer, parent: lmeState.drawings });
- setInfo(`${lmeState.managerSelectedObjectIds.size} oggetti selezionati.`);
- } else {
- selectDrawingLayer(layer);
- }
- if (lmeState.managerSelectedObjectIds.has(id)) {
- window.requestAnimationFrame(() => revealManagedRecordInLayerManager(id));
- }
+ const record = {
+ id: managedLayerId(layer, "drawings"),
+ categoryKey: "drawings",
+ layer,
+ parent: lmeState.drawings,
+ source: String(layer?.feature?.properties?._lmeSource || "Progetto corrente")
+ };
+ selectManagedRecord(record, event.originalEvent);
});
}
@@ -4970,31 +5121,14 @@ function bindManagedMapSelection(layer, categoryKey, parent) {
if (layer.lmeManagedMapSelectionBound) return;
layer.lmeManagedMapSelectionBound = true;
layer.on("click", event => {
- if (Date.now() < lmeState.ignoreMapClickUntil) return;
+ if (Date.now() < lmeState.ignoreMapClickUntil || lmeState.selectionMode === "off") return;
lmeState.ignoreMapClickUntil = Date.now() + 600;
if (event.originalEvent) {
event.originalEvent._lmeDrawingSelected = true;
L.DomEvent.stopPropagation(event.originalEvent);
}
const record = { id: managedLayerId(layer, categoryKey), categoryKey, layer, parent, source: String(layer?.feature?.properties?._lmeSource || "Progetto corrente") };
- const multiple = lmeState.multiSelectionEnabled
- || event.originalEvent?.ctrlKey
- || event.originalEvent?.metaKey
- || event.originalEvent?.shiftKey;
- if (!multiple) lmeState.managerSelectedObjectIds.clear();
- if (multiple && lmeState.managerSelectedObjectIds.has(record.id)) lmeState.managerSelectedObjectIds.delete(record.id);
- else lmeState.managerSelectedObjectIds.add(record.id);
- refreshLayerManager();
- if (multiple) {
- refreshMultipleMapSelection(record);
- setInfo(`${lmeState.managerSelectedObjectIds.size} oggetti selezionati.`);
- } else {
- highlightManagedObject(record);
- setInfo(`Selezionato dalla mappa: ${managedObjectEditableName(record) || "oggetto"}.`);
- }
- if (lmeState.managerSelectedObjectIds.has(record.id)) {
- window.requestAnimationFrame(() => revealManagedRecordInLayerManager(record.id));
- }
+ selectManagedRecord(record, event.originalEvent);
});
}
@@ -5523,7 +5657,7 @@ function loadProjectSnapshot(raw, projectName) {
? Boolean(projectDisplayOptions.importedSheetsVisible)
: data.schemaVersion >= 2 && Boolean(data.importedSheetsVisible);
applyImportedSheetVisibility();
- L.geoJSON(data.selectedParcels || emptyFeatureCollection(), {
+ const restoredParcels = L.geoJSON(data.selectedParcels || emptyFeatureCollection(), {
pane: "selectedParcelsPane",
style: feature => ({
color: feature?.properties?._lmeColor || "#ef4444",
@@ -5544,6 +5678,15 @@ function loadProjectSnapshot(raw, projectName) {
applyStyleFromProperties(layer);
}
}).addTo(lmeState.selectedParcelsLayer);
+ restoredParcels.eachLayer(layer => {
+ const properties = layer?.feature?.properties || {};
+ const reference = properties.NATIONALCADASTRALREFERENCE
+ || properties.INSPIREID_LOCALID
+ || properties.LABEL
+ || "Particella catastale";
+ layer.lmeParcelReference = reference;
+ bindManagedMapSelection(layer, "selectedParcels", restoredParcels);
+ });
const restoredFiducials = L.geoJSON(data.fiducials || emptyFeatureCollection(), {
pointToLayer: (feature, latlng) => createFiducialMarker(feature, latlng),
onEachFeature: (feature, layer) => {
@@ -5790,6 +5933,7 @@ function resetLayerManagerProjectState() {
}
function clearManagedCategoryState(categoryKey) {
+ clearManagedCategorySelection(categoryKey);
const category = layerManagerCategories().find(item => item.key === categoryKey);
if (category) {
managedRecordsForCategory(category).forEach(record => {
@@ -5807,6 +5951,21 @@ function clearManagedCategoryState(categoryKey) {
delete lmeState.managerObjectOrder[categoryKey];
}
+function clearManagedCategorySelection(categoryKey) {
+ const ids = new Set();
+ lmeState.managerRecords?.forEach?.(record => {
+ if (record.categoryKey === categoryKey) ids.add(record.id);
+ });
+ const category = layerManagerCategories().find(item => item.key === categoryKey);
+ if (category) managedRecordsForCategory(category).forEach(record => ids.add(record.id));
+ ids.forEach(id => lmeState.managerSelectedObjectIds.delete(id));
+ if (lmeState.managerHighlightedCategoryKey === categoryKey) {
+ lmeState.managerHighlightedObjectId = null;
+ lmeState.managerHighlightedCategoryKey = null;
+ }
+ restoreDirectManagerHighlights();
+}
+
function bindStoredFeaturePopup(feature, layer) {
updateStoredFeaturePopup(layer, feature?.properties);
}