Compare commits
1
Commits
main
...
fc1ee8f21a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc1ee8f21a |
No files matched your search
@@ -24,6 +24,7 @@
|
||||
{
|
||||
"layers" => "Qui controlli visibilita, ordine, selezione e organizzazione di tutti gli oggetti presenti sulla mappa.",
|
||||
"cadastre" => "Trova una particella nel catalogo catastale e mostrane il confine ufficiale sulla mappa.",
|
||||
"fiducials" => "Ricerca i Punti Fiduciali di un foglio, consultali sulla mappa e scarica le monografie disponibili.",
|
||||
"data" => "Importa file GIS, crea disegni e misure oppure aggiungi punti di interesse.",
|
||||
"save" => "Esporta gli elementi della mappa in un file compatibile con altre applicazioni GIS e GPS.",
|
||||
"projects" => "Salva il lavoro completo, riaprilo, organizzalo e recupera revisioni o copie di sicurezza.",
|
||||
@@ -40,6 +41,7 @@
|
||||
"Gerarchia: premi la freccia accanto alla maniglia per aprire un gruppo. Il rientro, il carattere e la tonalita indicano se una riga e figlia, nipote o di livello successivo.",
|
||||
"Selezione multipla: attiva il flag sulle righe desiderate. Esempio: seleziona tre poligoni per esportare solo quelli oppure per bloccarli con una sola operazione.",
|
||||
"Rinomina e attributi: Rinomina modifica il nome mostrato e salvato nei progetti e nei file esportati; Attributi apre un editor multilinea per consultare o correggere i dati associati.",
|
||||
"Note: espandi un oggetto e premi Visualizza note per consultarne l'elenco. Con LME Pro, tramite abbonamento o licenza manuale, puoi aggiungere, modificare ed eliminare un numero qualsiasi di note; le vecchie note singole restano compatibili.",
|
||||
"Colori: espandi un padre o un figlio e usa gli editor di linea e riempimento. Puoi scegliere il colore, prelevarlo con il contagocce se supportato, oppure inserire e copiare i valori HEX, RGB e HSL; sul padre viene applicato a tutto il gruppo.",
|
||||
"Blocco: il lucchetto impedisce modifiche e spostamenti involontari. Sblocca l'oggetto prima di usare la matita o gli strumenti geometrici.",
|
||||
"Sposta nel gruppo cambia l'appartenenza. Esempio: trasferisci un poligono da Disegni e POI a Progetti e dati importati, oppure in un nuovo sottogruppo chiamato Rilievo nord.",
|
||||
@@ -56,6 +58,16 @@
|
||||
"Se un oggetto gia presente era nascosto, viene reso nuovamente visibile. La visibilita generale dei contorni evidenziati e dei dati importati resta controllabile dal pannello Layer.",
|
||||
"Quando il servizio ufficiale non restituisce subito il confine, attendi il completamento indicato nel pannello e riprova mantenendo gli stessi dati selezionati."
|
||||
],
|
||||
"fiducials" =>
|
||||
[
|
||||
"La funzione richiede LME Pro: sono validi sia l'abbonamento Google Play sia una licenza manuale attiva.",
|
||||
"Scegli Provincia, Comune catastale e Foglio, quindi premi Cerca Punti Fiduciali. Puoi anche usare il comando PF sulla mappa dopo aver selezionato una particella o un foglio, oppure dalla particella individuata dal GPS.",
|
||||
"Mantieni i PF dei fogli gia caricati conserva i risultati precedenti come sottogruppi distinti; disattivalo per sostituirli con il nuovo foglio.",
|
||||
"Il flag Mostra i PF sulla mappa controlla la visibilita generale. Nel pannello Layer puoi inoltre cambiare colore, ordine, visibilita o rimuovere il singolo gruppo del foglio.",
|
||||
"Tocca un punto rosso per vedere identificativo e coordinate. Quando disponibile, Scarica PDF monografia apre o salva il documento ufficiale dell'Agenzia delle Entrate.",
|
||||
"I PF caricati vengono salvati nel progetto con gruppi, popup e collegamenti alle monografie; riaprendo il progetto tornano disponibili come gli altri oggetti.",
|
||||
"Il posizionamento cartografico e sperimentale e non sostituisce un rilievo topografico o la verifica professionale sul posto."
|
||||
],
|
||||
"data" =>
|
||||
[
|
||||
"Importazione: premi Carica KML, KMZ o GeoJSON e scegli il file dal dispositivo. I nomi e gli attributi riconosciuti vengono mantenuti.",
|
||||
@@ -75,11 +87,11 @@
|
||||
"Attiva solo le categorie da includere. Esempio: per consegnare disegni e particelle importate senza la traccia GPS, lascia attive quelle due voci e disattiva Traccia GPS.",
|
||||
"Solo oggetti selezionati esporta esclusivamente le righe contrassegnate nel pannello Layer. Se non devi filtrare manualmente, lascia questa opzione disattivata.",
|
||||
"Premi Esporta e salva file e scegli la cartella o l'app di destinazione proposta dal dispositivo.",
|
||||
"Nomi rinominati e attributi vengono salvati in GeoJSON, KML e KMZ. Gli sfondi satellitari, stradali e WMS non vengono incorporati nei file vettoriali."
|
||||
"Nomi rinominati, attributi e note compatibili vengono salvati nei formati che supportano metadati. Gli sfondi satellitari, stradali e WMS non vengono incorporati nei file vettoriali."
|
||||
],
|
||||
"projects" =>
|
||||
[
|
||||
"Nuovo progetto: inserisci un nome, scegli se includere i contorni catastali evidenziati e premi Salva progetto. Vengono conservati oggetti, gruppi, nomi, attributi e opzioni di visualizzazione.",
|
||||
"Nuovo progetto: inserisci un nome, scegli se includere i contorni catastali evidenziati e premi Salva progetto. Vengono conservati oggetti, gruppi, nomi, attributi, note, Punti Fiduciali caricati e opzioni di visualizzazione.",
|
||||
"Aggiornamento: dopo aver aperto un progetto, usa di nuovo Salva progetto con lo stesso nome per aggiornarlo. Se nelle Impostazioni attivi Aggiorna automaticamente il progetto aperto, le modifiche vengono registrate dopo 3 secondi di inattivita.",
|
||||
"Cartelle: seleziona un progetto, scrivi per esempio Cantieri 2026 e premi Sposta per organizzarlo nell'archivio; questa operazione non sposta le geometrie sulla mappa.",
|
||||
"Rinomina cambia il nome del progetto selezionato senza alterarne contenuti e revisioni. Duplica crea invece una copia modificabile, utile prima di una variante importante o quando ricevi un progetto in sola lettura.",
|
||||
@@ -91,6 +103,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.",
|
||||
"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.",
|
||||
"Registro operazioni: mostra in ordine cronologico disegni, modifiche, rinomine, spostamenti ed eliminazioni, cosi puoi capire cosa e cambiato durante la sessione.",
|
||||
@@ -102,6 +115,8 @@
|
||||
"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.",
|
||||
"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.",
|
||||
"Backup automatico: scegli Giornaliero o Settimanale. Alla successiva pressione di Salva progetto, se e trascorso l'intervallo, viene creata una copia interna di tutti i progetti; l'app conserva le 10 copie automatiche piu recenti. Disattivato non crea nuove copie programmate.",
|
||||
@@ -109,7 +124,8 @@
|
||||
"Aggancio ai vertici: avvicinando un nuovo vertice a uno esistente, l'app lo allinea entro la distanza in pixel scelta. Aumenta la distanza su schermi piccoli; riducila quando ci sono molti vertici ravvicinati.",
|
||||
"Centra e zooma quando seleziono un oggetto: attivalo per inquadrare automaticamente ogni elemento scelto; disattivalo se vuoi mantenere ferma la vista mentre lavori dall'elenco Layer.",
|
||||
"WebDAV: inserisci indirizzo, utente e password applicazione, salva la configurazione, poi usa Invia al cloud per caricare il backup o Scarica dal cloud per ripristinare quello remoto.",
|
||||
"GPS e percorso: Mostra punto visualizza posizione e precisione; Registra traccia memorizza il cammino; distanza e avvisi aiutano a controllare l'avvicinamento e l'attraversamento del confine selezionato.",
|
||||
"GPS e percorso: Mostra punto visualizza posizione e precisione; Registra traccia memorizza il cammino; distanza e avvisi aiutano a controllare l'avvicinamento e l'attraversamento del confine selezionato. Avvia/Ferma GPS e Centra sono disposti verticalmente e il riquadro puo essere compresso.",
|
||||
"Fogli catastali in navigazione: con Fogli catastali attivo nel pannello Layer, l'app carica il foglio anche senza particelle o progetti e lo mantiene visibile a zoom piu ampi senza duplicarlo se e gia presente.",
|
||||
"Google Maps e Azure Maps richiedono le rispettive chiavi solo quando scegli quegli sfondi. Controlla nuova versione confronta la versione installata con l'ultima pubblicata e mostra le relative modifiche."
|
||||
],
|
||||
_ => []
|
||||
|
||||
@@ -309,6 +309,7 @@
|
||||
<p>Elenco e PDF provengono dall'Agenzia delle Entrate; il posizionamento sperimentale usa il servizio cartografico GloboGIS. Le coordinate sono un ausilio: verificale prima di qualsiasi uso topografico o professionale.</p>
|
||||
</div>
|
||||
}
|
||||
<MenuHelp Topic="fiducials" />
|
||||
</section>
|
||||
}
|
||||
else if (ActiveTab == "data")
|
||||
@@ -2538,6 +2539,7 @@
|
||||
{
|
||||
advancedGeometry = CanUse(AppFeatures.AdvancedGeometry),
|
||||
fiducialPoints = CanUse(AppFeatures.FiducialPoints),
|
||||
multipleNotes = CanUse(AppFeatures.MultipleNotes),
|
||||
maxDrawingObjects = Entitlements.HasProAccess ? (int?)null : EntitlementService.BaseDrawingObjectLimit,
|
||||
drawingLimitMessage = L("drawings.base.limit.reached")
|
||||
}).AsTask();
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
<ApplicationId>it.liviomerola.lmemappecatastali.pro</ApplicationId>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>1.0.18</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>118</ApplicationVersion>
|
||||
<ApplicationDisplayVersion>1.0.19</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>119</ApplicationVersion>
|
||||
<Version>$(ApplicationDisplayVersion)</Version>
|
||||
|
||||
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||
|
||||
@@ -22,7 +22,8 @@ public sealed class EntitlementService(
|
||||
AppFeatures.OfflineMaps,
|
||||
AppFeatures.AdvancedGeometry,
|
||||
AppFeatures.GpsTrail,
|
||||
AppFeatures.FiducialPoints
|
||||
AppFeatures.FiducialPoints,
|
||||
AppFeatures.MultipleNotes
|
||||
};
|
||||
|
||||
public bool IsGoogleProActive { get; private set; }
|
||||
@@ -187,4 +188,5 @@ public static class AppFeatures
|
||||
public const string AdvancedGeometry = "advanced-geometry";
|
||||
public const string GpsTrail = "gps-trail";
|
||||
public const string FiducialPoints = "fiducial-points";
|
||||
public const string MultipleNotes = "multiple-notes";
|
||||
}
|
||||
@@ -906,6 +906,81 @@ input[type="checkbox"] {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.object-table-wrap .object-notes-button {
|
||||
width: auto;
|
||||
min-width: 70px;
|
||||
padding: 4px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.managed-notes-command {
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.managed-notes-command small,
|
||||
.managed-note-pro-message {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.managed-notes-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.managed-note {
|
||||
padding: 10px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
background: #f8fafb;
|
||||
}
|
||||
|
||||
.managed-note > header {
|
||||
padding: 0 0 7px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.managed-note time {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.managed-note p,
|
||||
.lme-popup-notes p {
|
||||
margin: 0 0 8px;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.managed-note-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.managed-note-actions button {
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
.managed-note-editor {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.managed-note-editor label {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.managed-note-editor textarea {
|
||||
min-height: 90px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.object-table-search,
|
||||
.coordinate-result {
|
||||
display: grid;
|
||||
|
||||
+99
-9
@@ -119,6 +119,8 @@ const lmeState = {
|
||||
measurementSystem: "metric",
|
||||
entitlements: {
|
||||
advancedGeometry: false,
|
||||
fiducialPoints: false,
|
||||
multipleNotes: false,
|
||||
maxDrawingObjects: 15,
|
||||
drawingLimitMessage: "Hai raggiunto il limite di 15 oggetti del piano Base."
|
||||
},
|
||||
@@ -2476,6 +2478,89 @@ function managedGroupTotals(records) {
|
||||
return values.join(" | ") || `${records.length} oggetti`;
|
||||
}
|
||||
|
||||
function managedNotes(properties = {}) {
|
||||
const stored = Array.isArray(properties._lmeNotesList) ? properties._lmeNotesList : [];
|
||||
const notes = stored.map((item, index) => {
|
||||
const value = typeof item === "string" ? { text: item } : (item || {});
|
||||
const text = String(value.text || "").trim();
|
||||
return text ? {
|
||||
id: String(value.id || `note-${index}`),
|
||||
text,
|
||||
createdAt: value.createdAt || null,
|
||||
updatedAt: value.updatedAt || null
|
||||
} : null;
|
||||
}).filter(Boolean);
|
||||
const legacy = String(properties._lmeNotes || "").trim();
|
||||
if (!notes.length && legacy) {
|
||||
notes.push({ id: "legacy-note", text: legacy, createdAt: null, updatedAt: null });
|
||||
}
|
||||
return notes;
|
||||
}
|
||||
|
||||
function writeManagedNotes(properties, notes) {
|
||||
properties._lmeNotesList = notes.map(note => ({
|
||||
id: String(note.id),
|
||||
text: String(note.text || "").trim(),
|
||||
createdAt: note.createdAt || null,
|
||||
updatedAt: note.updatedAt || null
|
||||
})).filter(note => note.text);
|
||||
properties._lmeNotes = properties._lmeNotesList.map(note => note.text).join("\n\n");
|
||||
}
|
||||
|
||||
function managedNoteId() {
|
||||
return globalThis.crypto?.randomUUID?.() || `note-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
}
|
||||
|
||||
function managedNoteDate(note) {
|
||||
const value = note.updatedAt || note.createdAt;
|
||||
if (!value) return "Nota importata";
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime()) ? "Nota" : date.toLocaleString("it-IT");
|
||||
}
|
||||
|
||||
function showManagedNotesDialog(record) {
|
||||
const properties = record.layer?.feature?.properties || {};
|
||||
const title = `Note - ${managedObjectEditableName(record) || "Oggetto"}`;
|
||||
const notes = managedNotes(properties);
|
||||
const canEdit = Boolean(lmeState.entitlements?.multipleNotes) && !lmeState.readOnly;
|
||||
const list = notes.map((note, index) => `<article class="managed-note" data-note-id="${escapeHtml(note.id)}">
|
||||
<header><strong>Nota ${index + 1}</strong><time>${escapeHtml(managedNoteDate(note))}</time></header>
|
||||
<p>${escapeHtml(note.text).replaceAll("\n", "<br>")}</p>
|
||||
${canEdit ? `<div class="managed-note-actions"><button type="button" data-edit-note="${escapeHtml(note.id)}">Modifica</button><button type="button" class="danger-command" data-delete-note="${escapeHtml(note.id)}">Elimina</button></div>` : ""}
|
||||
</article>`).join("");
|
||||
const editor = canEdit ? `<form class="managed-note-editor">
|
||||
<label>Nuova nota<textarea name="note" rows="4" maxlength="1000" required placeholder="Scrivi una nuova nota tecnica"></textarea></label>
|
||||
<button type="submit" class="primary-command">Aggiungi nota</button>
|
||||
</form>` : `<p class="managed-note-pro-message">${lmeState.readOnly ? "Il progetto e' in sola lettura: le note possono essere consultate ma non modificate." : "La consultazione e' disponibile; per aggiungere, modificare o eliminare note serve LME Pro con abbonamento o licenza manuale."}</p>`;
|
||||
const dialog = showAppDialog(title, `<div class="managed-notes-list">${list || "<p>Nessuna nota presente.</p>"}</div>${editor}`);
|
||||
dialog.querySelector(".managed-note-editor")?.addEventListener("submit", event => {
|
||||
event.preventDefault();
|
||||
if (!requireProFeature("multipleNotes")) return;
|
||||
const text = String(event.currentTarget.elements.note?.value || "").trim();
|
||||
if (!text) return;
|
||||
const now = new Date().toISOString();
|
||||
updateManagedProperties([record], value => writeManagedNotes(value, [...managedNotes(value), { id: managedNoteId(), text, createdAt: now }]));
|
||||
showManagedNotesDialog(record);
|
||||
});
|
||||
dialog.querySelectorAll("[data-edit-note]").forEach(button => button.addEventListener("click", async () => {
|
||||
if (!requireProFeature("multipleNotes")) return;
|
||||
const current = managedNotes(record.layer?.feature?.properties || {}).find(note => note.id === button.dataset.editNote);
|
||||
if (!current) return;
|
||||
const result = await showFormDialog("Modifica nota", [{ name: "note", label: "Testo", type: "textarea", rows: 6, maxLength: 1000, value: current.text }], "Salva nota");
|
||||
if (!result) { showManagedNotesDialog(record); return; }
|
||||
const text = String(result.note || "").trim();
|
||||
if (!text) { showManagedNotesDialog(record); return; }
|
||||
updateManagedProperties([record], value => writeManagedNotes(value, managedNotes(value).map(note => note.id === current.id ? { ...note, text, updatedAt: new Date().toISOString() } : note)));
|
||||
showManagedNotesDialog(record);
|
||||
}));
|
||||
dialog.querySelectorAll("[data-delete-note]").forEach(button => button.addEventListener("click", () => {
|
||||
if (!requireProFeature("multipleNotes")) return;
|
||||
updateManagedProperties([record], value => writeManagedNotes(value, managedNotes(value).filter(note => note.id !== button.dataset.deleteNote)));
|
||||
showManagedNotesDialog(record);
|
||||
}));
|
||||
return dialog;
|
||||
}
|
||||
|
||||
function renderManagedObjectTools(record) {
|
||||
const properties = record.layer.feature?.properties || (record.layer.feature = { type: "Feature", properties: {} }).properties;
|
||||
const selected = lmeState.managerSelectedObjectIds.has(record.id);
|
||||
@@ -2493,7 +2578,7 @@ function renderManagedObjectTools(record) {
|
||||
<label>Opacita<input type="range" data-field="opacity" min="0.1" max="1" step="0.1" value="${Number(properties._lmeOpacity) || 1}"></label>
|
||||
<label>Riempimento %<input type="range" data-field="fillOpacity" min="0" max="0.8" step="0.05" value="${Number.isFinite(Number(properties._lmeFillOpacity)) ? Number(properties._lmeFillOpacity) : 0.2}"></label>
|
||||
<label>Tratto<select data-field="dash"><option value="">Continuo</option><option value="8 6" ${properties._lmeDash === "8 6" ? "selected" : ""}>Tratteggiato</option><option value="2 6" ${properties._lmeDash === "2 6" ? "selected" : ""}>Puntinato</option></select></label></div>
|
||||
<label>Note<textarea data-field="notes" rows="2" maxlength="1000" placeholder="Note tecniche">${escapeHtml(properties._lmeNotes || "")}</textarea></label>
|
||||
<div class="managed-notes-command"><button type="button" data-action="notes">Visualizza note (${managedNotes(properties).length})</button><small>Le note multiple sono modificabili con LME Pro.</small></div>
|
||||
<label class="layer-photo-picker">Foto<input type="file" data-field="photo" accept="image/*"></label>
|
||||
${properties._lmePhoto ? `<img class="layer-photo-preview" src="${properties._lmePhoto}" alt="Foto allegata"><button type="button" data-action="remove-photo">Rimuovi foto</button>` : ""}
|
||||
<div class="layer-compact-actions"><button type="button" data-action="attributes">Attributi</button><button type="button" data-action="duplicate">Duplica</button><button type="button" data-action="numeric">Coordinate</button><button type="button" data-action="inspect">WGS84 / UTM</button><button type="button" data-action="move">Sposta geometria</button><button type="button" data-action="buffer">Buffer</button></div>`;
|
||||
@@ -2514,7 +2599,7 @@ function renderManagedObjectTools(record) {
|
||||
});
|
||||
bindManagedColorEditors(tools, applyCurrentStyle);
|
||||
tools.querySelectorAll(".layer-style-controls input[type='range'],.layer-style-controls select").forEach(input => input.addEventListener("change", applyCurrentStyle));
|
||||
tools.querySelector("[data-field='notes']").addEventListener("change", event => updateManagedProperties([record], value => { value._lmeNotes = event.target.value.trim(); }));
|
||||
tools.querySelector("[data-action='notes']").addEventListener("click", () => showManagedNotesDialog(record));
|
||||
tools.querySelector("[data-field='photo']").addEventListener("change", async event => {
|
||||
const file = event.target.files?.[0]; if (!file) return;
|
||||
if (file.size > 2_500_000) { setInfo("Foto troppo grande: usa un file inferiore a 2,5 MB."); return; }
|
||||
@@ -2831,9 +2916,9 @@ function showObjectTable() {
|
||||
const details = managedObjectDetails(record.layer);
|
||||
const selected = lmeState.managerSelectedObjectIds.has(record.id);
|
||||
const locked = Boolean(properties._lmeLocked);
|
||||
return `<tr data-object-id="${escapeHtml(record.id)}"><td><input type="checkbox" data-select="${escapeHtml(record.id)}" ${selected ? "checked" : ""} aria-label="Seleziona ${escapeHtml(managedObjectLabel(record, index))}"></td><td><input type="checkbox" data-lock="${escapeHtml(record.id)}" ${locked ? "checked" : ""} ${lmeState.readOnly ? "disabled" : ""} aria-label="Blocca ${escapeHtml(managedObjectLabel(record, index))}"></td><td><button type="button" data-locate="${escapeHtml(record.id)}" title="Mostra sulla mappa" aria-label="Mostra sulla mappa">⌖</button></td><td><button type="button" data-attributes="${escapeHtml(record.id)}" title="Modifica metadati" aria-label="Modifica metadati di ${escapeHtml(managedObjectLabel(record, index))}" ${lmeState.readOnly ? "disabled" : ""}>✎</button></td><td>${escapeHtml(managedObjectLabel(record, index))}</td><td>${escapeHtml(managedCategoryName(layerManagerCategories().find(item => item.key === record.categoryKey) || { key: record.categoryKey, label: record.categoryKey }, [record]))}</td><td>${escapeHtml(properties.foglio || properties.FOGLIO || "")}</td><td>${escapeHtml(properties.particella || properties.PARTICELLA || properties.LABEL || "")}</td><td>${escapeHtml(details.slice(0, 2).join(" | "))}</td></tr>`;
|
||||
return `<tr data-object-id="${escapeHtml(record.id)}"><td><input type="checkbox" data-select="${escapeHtml(record.id)}" ${selected ? "checked" : ""} aria-label="Seleziona ${escapeHtml(managedObjectLabel(record, index))}"></td><td><input type="checkbox" data-lock="${escapeHtml(record.id)}" ${locked ? "checked" : ""} ${lmeState.readOnly ? "disabled" : ""} aria-label="Blocca ${escapeHtml(managedObjectLabel(record, index))}"></td><td><button type="button" data-locate="${escapeHtml(record.id)}" title="Mostra sulla mappa" aria-label="Mostra sulla mappa">⌖</button></td><td><button type="button" data-attributes="${escapeHtml(record.id)}" title="Modifica metadati" aria-label="Modifica metadati di ${escapeHtml(managedObjectLabel(record, index))}" ${lmeState.readOnly ? "disabled" : ""}>✎</button></td><td><button type="button" class="object-notes-button" data-notes="${escapeHtml(record.id)}" title="Visualizza note">Note (${managedNotes(properties).length})</button></td><td>${escapeHtml(managedObjectLabel(record, index))}</td><td>${escapeHtml(managedCategoryName(layerManagerCategories().find(item => item.key === record.categoryKey) || { key: record.categoryKey, label: record.categoryKey }, [record]))}</td><td>${escapeHtml(properties.foglio || properties.FOGLIO || "")}</td><td>${escapeHtml(properties.particella || properties.PARTICELLA || properties.LABEL || "")}</td><td>${escapeHtml(details.slice(0, 2).join(" | "))}</td></tr>`;
|
||||
}).join("");
|
||||
const dialog = showAppDialog("Elenco oggetti e particelle", `<label class="object-table-search">Filtra<input type="search" placeholder="Nome, foglio, particella o gruppo"></label><div class="object-table-legend"><span>Seleziona più oggetti con i flag nella colonna Selez.</span><span>Il lucchetto impedisce modifiche e rimozioni; la matita modifica i metadati.</span></div><div class="object-table-wrap"><table><thead><tr><th>Selez.</th><th>Blocca</th><th>Mappa</th><th>Metadati</th><th>Nome</th><th>Gruppo</th><th>Foglio</th><th>Particella</th><th>Misure</th></tr></thead><tbody>${body || "<tr><td colspan='9'>Nessun oggetto</td></tr>"}</tbody></table></div>`);
|
||||
const dialog = showAppDialog("Elenco oggetti e particelle", `<label class="object-table-search">Filtra<input type="search" placeholder="Nome, foglio, particella o gruppo"></label><div class="object-table-legend"><span>Seleziona più oggetti con i flag nella colonna Selez.</span><span>Il lucchetto impedisce modifiche e rimozioni; la matita modifica i metadati.</span><span>Note apre l'elenco completo; le modifiche richiedono LME Pro.</span></div><div class="object-table-wrap"><table><thead><tr><th>Selez.</th><th>Blocca</th><th>Mappa</th><th>Metadati</th><th>Note</th><th>Nome</th><th>Gruppo</th><th>Foglio</th><th>Particella</th><th>Misure</th></tr></thead><tbody>${body || "<tr><td colspan='10'>Nessun oggetto</td></tr>"}</tbody></table></div>`);
|
||||
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");
|
||||
@@ -2865,6 +2950,10 @@ function showObjectTable() {
|
||||
const record = byId.get(button.dataset.attributes); if (!record) return;
|
||||
dialog.remove(); editManagedAttributes(record);
|
||||
}));
|
||||
dialog.querySelectorAll("[data-notes]").forEach(button => button.addEventListener("click", () => {
|
||||
const record = byId.get(button.dataset.notes); if (!record) return;
|
||||
showManagedNotesDialog(record);
|
||||
}));
|
||||
return records.length;
|
||||
}
|
||||
|
||||
@@ -3180,8 +3269,8 @@ function showLeafletToolbarHelp() {
|
||||
<div><dt>✓</dt><dd><strong>Controlla e ripara</strong>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.</dd></div>
|
||||
<div><dt>◉</dt><dd><strong>Crea foro</strong>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.</dd></div>
|
||||
<div><dt>⇄</dt><dd><strong>Confronta due confini</strong>Seleziona esattamente due poligoni. L'app crea il solo primo in rosso, il solo secondo in blu e la sovrapposizione in verde, mostrando le rispettive aree.</dd></div>
|
||||
<div><dt>PF</dt><dd><strong>Scarica Punti Fiduciali</strong>Seleziona una particella o un foglio catastale; in alternativa usa la particella rilevata dal GPS. I PF vengono aggiunti come sottogruppo del relativo foglio.</dd></div>
|
||||
<div><dt>?</dt><dd><strong>Guida</strong>Riapre questa descrizione. I risultati di riparazione e confronto vengono mostrati anche in una finestra, così l'operazione non resta senza riscontro.</dd></div>
|
||||
<div><dt>PF</dt><dd><strong>Scarica Punti Fiduciali</strong>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.</dd></div>
|
||||
<div><dt>?</dt><dd><strong>Guida</strong>Riapre questa descrizione. I risultati di riparazione e confronto vengono mostrati anche in una finestra; per uscire dal confronto usa Annulla oppure avvia un nuovo confronto.</dd></div>
|
||||
</dl>
|
||||
</div>`);
|
||||
}
|
||||
@@ -5453,11 +5542,12 @@ function bindStoredFeaturePopup(feature, layer) {
|
||||
function updateStoredFeaturePopup(layer, sourceProperties) {
|
||||
const properties = sourceProperties || layer?.feature?.properties || {};
|
||||
const label = properties.name || properties.Name || properties.label || properties.LABEL;
|
||||
const notes = String(properties._lmeNotes || "").trim();
|
||||
const notes = managedNotes(properties);
|
||||
const photo = String(properties._lmePhoto || "");
|
||||
if (label || notes || photo) {
|
||||
if (label || notes.length || photo) {
|
||||
const image = photo.startsWith("data:image/") ? `<img class="lme-popup-photo" src="${photo}" alt="Foto allegata">` : "";
|
||||
layer.bindPopup(`${label ? `<strong>${escapeHtml(label)}</strong>` : ""}${notes ? `<p>${escapeHtml(notes)}</p>` : ""}${image}`);
|
||||
const noteList = notes.length ? `<div class="lme-popup-notes"><strong>Note</strong>${notes.map(note => `<p>${escapeHtml(note.text).replaceAll("\n", "<br>")}</p>`).join("")}</div>` : "";
|
||||
layer.bindPopup(`${label ? `<strong>${escapeHtml(label)}</strong>` : ""}${noteList}${image}`);
|
||||
return;
|
||||
}
|
||||
if (typeof layer?.unbindPopup === "function") {
|
||||
|
||||
Reference in new issue
Block a user