Rilascia strumenti avanzati e archivio 0.3.0

This commit is contained in:
livio committed 2026-09-24 16:15:23 +02:00
1 parent b19afbce91
commit 620a4bec26
8 files changed
+1351 -33

No files matched your search

+208 -4
View File
@@ -211,6 +211,10 @@
<section class="panel-section">
<h2>Disegni e misure</h2>
<div class="button-row">
<button class="secondary-command" @onclick="UndoMapChange">Annulla</button>
<button class="secondary-command" @onclick="RedoMapChange">Ripristina</button>
</div>
<label class="toggle-line project-save-option">
<input type="checkbox" @bind="ShowDrawingMeasurements" @bind:after="ApplyDrawingMeasurementsVisibility" />
<span>Mostra superficie, perimetro e lati</span>
@@ -219,6 +223,7 @@
@(DrawToolsVisible ? "Nascondi strumenti sulla mappa" : "Mostra strumenti sulla mappa")
</button>
<button class="danger-command" @onclick="ClearDrawings">Elimina disegni e POI</button>
<button class="secondary-command" @onclick="RestoreAutosave">Recupera ultimo autosalvataggio</button>
</section>
<section class="panel-section">
@@ -246,9 +251,15 @@
<option value="geojson">GeoJSON</option>
<option value="kml">KML</option>
<option value="kmz">KMZ compresso</option>
<option value="gpx">GPX per GPS</option>
<option value="pdf">Rapporto PDF</option>
</select>
</label>
<div class="export-options" role="group" aria-label="Oggetti da esportare">
<label class="toggle-line project-save-option">
<input type="checkbox" @bind="ExportSelectedOnly" @bind:after="SaveFlagPreferences" />
<span>Solo oggetti selezionati nel pannello Layer</span>
</label>
<label class="toggle-line project-save-option">
<input type="checkbox" @bind="ExportImported" @bind:after="SaveFlagPreferences" />
<span>Particelle e dati GIS importati</span>
@@ -306,7 +317,7 @@
<option value="">Seleziona un progetto</option>
@foreach (var project in Projects)
{
<option value="@project.Id">@project.Name - @project.UpdatedAt.ToLocalTime().ToString("dd/MM/yyyy HH:mm")</option>
<option value="@project.Id">@(project.IsArchived ? "[ARCHIVIATO] " : "")@(string.IsNullOrWhiteSpace(project.Folder) ? "" : $"{project.Folder} / ")@project.Name - @project.UpdatedAt.ToLocalTime().ToString("dd/MM/yyyy HH:mm")</option>
}
</select>
</label>
@@ -314,9 +325,44 @@
<button @onclick="LoadProject" disabled="@(ProjectBusy || string.IsNullOrWhiteSpace(SelectedProjectId))">Apri</button>
<button class="danger-command" @onclick="DeleteProject" disabled="@(ProjectBusy || string.IsNullOrWhiteSpace(SelectedProjectId))">Elimina</button>
</div>
<label>
Cartella
<input @bind="ProjectFolder" placeholder="Es. Cantieri 2026" maxlength="60" />
</label>
<div class="button-row">
<button class="secondary-command" @onclick="UpdateProjectFolder" disabled="@(ProjectBusy || string.IsNullOrWhiteSpace(SelectedProjectId))">Sposta</button>
<button class="secondary-command" @onclick="DuplicateProject" disabled="@(ProjectBusy || string.IsNullOrWhiteSpace(SelectedProjectId))">Duplica</button>
</div>
<div class="button-row">
<button class="secondary-command" @onclick="ToggleProjectArchive" disabled="@(ProjectBusy || string.IsNullOrWhiteSpace(SelectedProjectId))">@(SelectedProject?.IsArchived == true ? "Ripristina" : "Archivia")</button>
<button class="secondary-command" @onclick="ShareProject" disabled="@(ProjectBusy || string.IsNullOrWhiteSpace(SelectedProjectId))">Condividi</button>
</div>
<label>
Revisione precedente
<select @bind="SelectedVersionId" disabled="@(ProjectVersions.Count == 0)">
<option value="">Seleziona revisione</option>
@foreach (var version in ProjectVersions)
{
<option value="@version.Id">@version.UpdatedAt.ToLocalTime().ToString("dd/MM/yyyy HH:mm:ss")</option>
}
</select>
</label>
<div class="button-row">
<button class="secondary-command" @onclick="LoadProjectVersion" disabled="@(string.IsNullOrWhiteSpace(SelectedVersionId))">Apri revisione</button>
<button class="secondary-command" @onclick="CompareProjectVersion" disabled="@(string.IsNullOrWhiteSpace(SelectedVersionId))">Confronta</button>
</div>
<div class="project-archive-state">@ProjectArchiveMessage</div>
</section>
<section class="panel-section">
<h2>Backup e trasferimento</h2>
<div class="button-row">
<button class="secondary-command" @onclick="ShareFullBackup">Crea backup</button>
<button class="secondary-command" @onclick="RestoreBackup">Ripristina backup</button>
</div>
<button class="secondary-command" @onclick="ImportProject">Importa progetto condiviso</button>
</section>
<section class="panel-section">
<h2>Visualizzazione progetto</h2>
<label class="toggle-line project-save-option">
@@ -353,6 +399,7 @@
<option value="topographic">Topografica - Esri World Topographic</option>
<option value="terrain">Terreno - OpenTopoMap</option>
<option value="carto">Strade chiare - CARTO Voyager</option>
<option value="offline">Offline - solo dati vettoriali</option>
<option value="azure-road">Strade - Azure Maps</option>
<option value="azure-satellite">Satellite - Azure Maps</option>
<option value="google-satellite">Satellite - Google Maps</option>
@@ -383,6 +430,22 @@
</div>
</section>
<section class="panel-section">
<h2>Modifica e sicurezza dati</h2>
<label class="toggle-line project-save-option">
<input type="checkbox" @bind="AutoSaveEnabled" @bind:after="ApplyAdvancedOptions" />
<span>Autosalvataggio locale dopo ogni modifica</span>
</label>
<label class="toggle-line project-save-option">
<input type="checkbox" @bind="SnapEnabled" @bind:after="ApplyAdvancedOptions" />
<span>Aggancia i vertici agli oggetti vicini</span>
</label>
<label>
Distanza aggancio in pixel
<input type="range" min="4" max="40" step="1" @bind="SnapDistancePixels" @bind:after="ApplyAdvancedOptions" />
</label>
</section>
<section class="panel-section">
<h2>GPS e percorso</h2>
<label class="toggle-line project-save-option">
@@ -393,6 +456,15 @@
<input type="checkbox" @bind="TrailEnabled" @bind:after="ApplyTrailOption" />
<span>Registra la traccia mentre cammini</span>
</label>
<label class="toggle-line project-save-option">
<input type="checkbox" @bind="GpsProximityEnabled" @bind:after="ApplyAdvancedOptions" />
<span>Distanza e direzione dal bordo selezionato</span>
</label>
<label class="toggle-line project-save-option">
<input type="checkbox" @bind="GpsBoundaryAlertEnabled" @bind:after="ApplyAdvancedOptions" />
<span>Avvisa quando entro o esco dall'area selezionata</span>
</label>
<button class="secondary-command" @onclick="SaveGpsMeasurementPoint">Salva punto GPS con precisione e ora</button>
</section>
<section class="panel-section">
@@ -453,6 +525,8 @@
private string PoiName { get; set; } = string.Empty;
private string SelectedProjectId { get; set; } = string.Empty;
private string ProjectArchiveMessage { get; set; } = "Nessun progetto salvato.";
private string ProjectFolder { get; set; } = string.Empty;
private string SelectedVersionId { get; set; } = string.Empty;
private string Status { get; set; } = "online";
private string SelectedBaseLayer { get; set; } = "satellite";
private string GoogleMapsApiKey { get; set; } = string.Empty;
@@ -486,6 +560,12 @@
private bool ExportDrawings { get; set; } = true;
private bool ExportPoi { get; set; } = true;
private bool ExportTrail { get; set; } = true;
private bool ExportSelectedOnly { get; set; }
private bool AutoSaveEnabled { get; set; } = true;
private bool SnapEnabled { get; set; } = true;
private int SnapDistancePixels { get; set; } = 14;
private bool GpsProximityEnabled { get; set; } = true;
private bool GpsBoundaryAlertEnabled { get; set; }
private CancellationTokenSource? CatalogCancellation { get; set; }
private CancellationTokenSource? GpsLookupCancellation { get; set; }
private long LastGpsLookupAt;
@@ -496,6 +576,8 @@
private IReadOnlyList<string> Sheets { get; set; } = [];
private IReadOnlyList<ParcelOption> Parcels { get; set; } = [];
private IReadOnlyList<MapProjectSummary> Projects { get; set; } = [];
private IReadOnlyList<MapProjectVersion> ProjectVersions { get; set; } = [];
private MapProjectSummary? SelectedProject => Projects.FirstOrDefault(item => item.Id == SelectedProjectId);
private MunicipalityOption? SelectedMunicipality =>
Municipalities.FirstOrDefault(item => item.Code == SelectedMunicipalityCode);
@@ -574,6 +656,7 @@
MapCallbackReference = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("lmeMap.init", MapCallbackReference);
await ApplyFlagPreferencesToMapAsync();
await ApplyAdvancedOptions();
await CenterOnInitialLocationAsync();
await MigrateLegacyProjectAsync();
}
@@ -1097,10 +1180,105 @@
}
}
private Task SelectProject(ChangeEventArgs args)
private async Task SelectProject(ChangeEventArgs args)
{
SelectedProjectId = args.Value?.ToString() ?? string.Empty;
return Task.CompletedTask;
ProjectFolder = SelectedProject?.Folder ?? string.Empty;
SelectedVersionId = string.Empty;
ProjectVersions = string.IsNullOrWhiteSpace(SelectedProjectId)
? []
: await ProjectStorageService.GetVersionsAsync(SelectedProjectId);
}
private Task UndoMapChange() => JS.InvokeVoidAsync("lmeMap.undoMapChange").AsTask();
private Task RedoMapChange() => JS.InvokeVoidAsync("lmeMap.redoMapChange").AsTask();
private Task RestoreAutosave() => JS.InvokeVoidAsync("lmeMap.restoreAutosave").AsTask();
private Task SaveGpsMeasurementPoint() => JS.InvokeVoidAsync("lmeMap.saveGpsMeasurementPoint").AsTask();
private async Task DuplicateProject()
{
if (SelectedProject is null) return;
var copy = await ProjectStorageService.DuplicateAsync(SelectedProject.Id);
await RefreshProjectsAsync();
SelectedProjectId = copy.Id;
ProjectFolder = copy.Folder;
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Progetto duplicato: {copy.Name}.");
}
private async Task UpdateProjectFolder()
{
if (SelectedProject is null) return;
await ProjectStorageService.UpdateOrganizationAsync(SelectedProject.Id, ProjectFolder);
await RefreshProjectsAsync();
await JS.InvokeVoidAsync("lmeMap.showInfo", "Cartella del progetto aggiornata.");
}
private async Task ToggleProjectArchive()
{
if (SelectedProject is not { } project) return;
await ProjectStorageService.UpdateOrganizationAsync(project.Id, isArchived: !project.IsArchived);
await RefreshProjectsAsync();
await JS.InvokeVoidAsync("lmeMap.showInfo", project.IsArchived ? "Progetto ripristinato." : "Progetto archiviato.");
}
private async Task ShareProject()
{
if (SelectedProject is null) return;
var path = await ProjectStorageService.CreateProjectPackageAsync(SelectedProject.Id);
await Share.Default.RequestAsync(new ShareFileRequest { Title = "Condividi progetto LME", File = new ShareFile(path, "application/json") });
}
private async Task ShareFullBackup()
{
var path = await ProjectStorageService.CreateBackupAsync();
await Share.Default.RequestAsync(new ShareFileRequest { Title = "Backup completo LME Mappe Catastali", File = new ShareFile(path, "application/zip") });
}
private async Task RestoreBackup()
{
var file = await FilePicker.Default.PickAsync(new PickOptions { PickerTitle = "Scegli backup LME" });
if (file is null) return;
var path = await CopyPickedFileToCacheAsync(file);
var count = await ProjectStorageService.RestoreBackupAsync(path);
await RefreshProjectsAsync();
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Backup ripristinato: {count} progetti disponibili.");
}
private async Task ImportProject()
{
var file = await FilePicker.Default.PickAsync(new PickOptions { PickerTitle = "Scegli progetto LME" });
if (file is null) return;
var path = await CopyPickedFileToCacheAsync(file);
var imported = await ProjectStorageService.ImportProjectAsync(path);
await RefreshProjectsAsync();
SelectedProjectId = imported.Id;
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Progetto importato: {imported.Name}.");
}
private static async Task<string> CopyPickedFileToCacheAsync(FileResult file)
{
var path = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid():N}-{file.FileName}");
await using var input = await file.OpenReadAsync();
await using var output = File.Create(path);
await input.CopyToAsync(output);
return path;
}
private async Task LoadProjectVersion()
{
if (SelectedProject is null || string.IsNullOrWhiteSpace(SelectedVersionId)) return;
var snapshot = await ProjectStorageService.LoadVersionAsync(SelectedProject.Id, SelectedVersionId);
var options = await JS.InvokeAsync<ProjectDisplayOptions>("lmeMap.loadProjectSnapshot", snapshot, $"{SelectedProject.Name} - revisione");
ApplyProjectDisplayOptions(options);
ActiveTab = "map";
}
private async Task CompareProjectVersion()
{
if (SelectedProject is null || string.IsNullOrWhiteSpace(SelectedVersionId)) return;
var current = await ProjectStorageService.LoadAsync(SelectedProject.Id);
var previous = await ProjectStorageService.LoadVersionAsync(SelectedProject.Id, SelectedVersionId);
ProjectArchiveMessage = await JS.InvokeAsync<string>("lmeMap.compareProjectSnapshots", current, previous);
}
private async Task ExportMapFile()
@@ -1122,7 +1300,8 @@
includeCadastral = ExportCadastral,
includeDrawings = ExportDrawings,
includePoi = ExportPoi,
includeTrail = ExportTrail
includeTrail = ExportTrail,
selectedOnly = ExportSelectedOnly
});
var exported = await MapExportService.CreateAsync(ExportFileName, ExportFormat, geoJson);
await Share.Default.RequestAsync(new ShareFileRequest
@@ -1403,6 +1582,12 @@
ExportDrawings = GetFlagPreference("export_drawings", true);
ExportPoi = GetFlagPreference("export_poi", true);
ExportTrail = GetFlagPreference("export_trail", true);
ExportSelectedOnly = GetFlagPreference("export_selected_only", false);
AutoSaveEnabled = GetFlagPreference("auto_save", true);
SnapEnabled = GetFlagPreference("snap_enabled", true);
SnapDistancePixels = Preferences.Default.Get($"{FlagPreferencePrefix}snap_distance", 14);
GpsProximityEnabled = GetFlagPreference("gps_proximity", true);
GpsBoundaryAlertEnabled = GetFlagPreference("gps_boundary_alert", false);
foreach (var layer in CadastralLayers)
{
@@ -1428,6 +1613,12 @@
SetFlagPreference("export_drawings", ExportDrawings);
SetFlagPreference("export_poi", ExportPoi);
SetFlagPreference("export_trail", ExportTrail);
SetFlagPreference("export_selected_only", ExportSelectedOnly);
SetFlagPreference("auto_save", AutoSaveEnabled);
SetFlagPreference("snap_enabled", SnapEnabled);
Preferences.Default.Set($"{FlagPreferencePrefix}snap_distance", SnapDistancePixels);
SetFlagPreference("gps_proximity", GpsProximityEnabled);
SetFlagPreference("gps_boundary_alert", GpsBoundaryAlertEnabled);
foreach (var layer in CadastralLayers)
{
@@ -1453,6 +1644,19 @@
}
}
private async Task ApplyAdvancedOptions()
{
SaveFlagPreferences();
await JS.InvokeVoidAsync("lmeMap.setAdvancedOptions", new
{
autoSaveEnabled = AutoSaveEnabled,
snapEnabled = SnapEnabled,
snapDistancePixels = SnapDistancePixels,
gpsProximityEnabled = GpsProximityEnabled,
gpsBoundaryAlertEnabled = GpsBoundaryAlertEnabled
});
}
private static bool GetFlagPreference(string name, bool defaultValue) =>
Preferences.Default.Get($"{FlagPreferencePrefix}{name}", defaultValue);