Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85e1c93d8a | ||
|
|
7b642afe2d |
No files matched your search
@@ -3,6 +3,7 @@
|
||||
@inject PreciseLocationService LocationService
|
||||
@inject CadastralCatalogService CatalogService
|
||||
@inject OfficialCadastreService OfficialCadastreService
|
||||
@inject FiducialPointService FiducialPointService
|
||||
@inject LocationSearchService LocationSearchService
|
||||
@inject NetworkLocationService NetworkLocationService
|
||||
@inject AzureMapsService AzureMapsService
|
||||
@@ -46,6 +47,7 @@
|
||||
<button @onclick='() => SelectTab("map")'>@L("menu.map")</button>
|
||||
<button @onclick='() => SelectTab("search")'>@L("menu.search")</button>
|
||||
<button @onclick='() => SelectTab("cadastre")'>@L("menu.cadastre")</button>
|
||||
<button @onclick='() => SelectTab("fiducials")'>Punti fiduciali</button>
|
||||
<button @onclick='() => SelectTab("data")'>@L("menu.data")</button>
|
||||
<button @onclick='() => SelectTab("projects")'>@L("menu.projects")</button>
|
||||
<button @onclick='() => SelectTab("save")'>@L("menu.export")</button>
|
||||
@@ -94,6 +96,16 @@
|
||||
<small>Particelle e geometrie verdi di file e progetti</small>
|
||||
</span>
|
||||
</label>
|
||||
@if (CanUse(AppFeatures.FiducialPoints))
|
||||
{
|
||||
<label class="map-layer-option">
|
||||
<input type="checkbox" @bind="ShowFiducialPoints" @bind:after="ApplyFiducialVisibility" />
|
||||
<span>
|
||||
<strong>Punti fiduciali</strong>
|
||||
<small>PF georiferiti caricati nella ricerca Pro</small>
|
||||
</span>
|
||||
</label>
|
||||
}
|
||||
<section class="layer-tree-section">
|
||||
<header>
|
||||
<strong>Ordine livelli e oggetti</strong>
|
||||
@@ -201,6 +213,98 @@
|
||||
<MenuHelp Topic="cadastre" />
|
||||
</section>
|
||||
}
|
||||
else if (ActiveTab == "fiducials")
|
||||
{
|
||||
<section class="panel-content">
|
||||
@if (!CanUse(AppFeatures.FiducialPoints))
|
||||
{
|
||||
<div class="service-note protected-service">
|
||||
<strong>Funzione LME Pro</strong>
|
||||
<p>La ricerca dei Punti Fiduciali e delle monografie ufficiali è disponibile con abbonamento Pro o chiave manuale valida.</p>
|
||||
<button class="primary-command" @onclick='() => SelectTab("settings")'>Apri abbonamento Pro</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<label>
|
||||
Provincia
|
||||
<select value="@SelectedFiducialProvinceCode" @onchange="OnFiducialProvinceChanged" disabled="@FiducialBusy">
|
||||
<option value="">Seleziona provincia</option>
|
||||
@foreach (var province in FiducialProvinces)
|
||||
{
|
||||
<option value="@province.Code">@province.Name</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Comune catastale
|
||||
<select value="@SelectedFiducialMunicipalityCode" @onchange="OnFiducialMunicipalityChanged" disabled="@(FiducialMunicipalities.Count == 0 || FiducialBusy)">
|
||||
<option value="">Seleziona comune</option>
|
||||
@foreach (var municipality in FiducialMunicipalities)
|
||||
{
|
||||
<option value="@municipality.Code">@municipality.Name (@municipality.Code)</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Foglio
|
||||
<select @bind="SelectedFiducialSheet" disabled="@(FiducialSheets.Count == 0 || FiducialBusy)">
|
||||
<option value="">Seleziona foglio</option>
|
||||
@foreach (var sheet in FiducialSheets)
|
||||
{
|
||||
<option value="@sheet.Code">@sheet.Name</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<button class="primary-command" @onclick="SearchFiducials" disabled="@(string.IsNullOrWhiteSpace(SelectedFiducialSheet) || FiducialBusy)">
|
||||
@(FiducialBusy ? "Ricerca..." : "Cerca Punti Fiduciali")
|
||||
</button>
|
||||
|
||||
<label class="toggle-line">
|
||||
<input type="checkbox" @bind="ShowFiducialPoints" @bind:after="ApplyFiducialVisibility" />
|
||||
<span>Mostra i PF sulla mappa</span>
|
||||
</label>
|
||||
<label>
|
||||
Colore PF
|
||||
<input type="color" @bind="FiducialColor" @bind:after="ApplyFiducialColor" />
|
||||
</label>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(FiducialMessage))
|
||||
{
|
||||
<div class="catalog-progress"><span>@FiducialMessage</span></div>
|
||||
}
|
||||
|
||||
@if (MappedFiducialPoints.Count > 0)
|
||||
{
|
||||
<section class="panel-section">
|
||||
<h2>Punti cartografici (@MappedFiducialPoints.Count)</h2>
|
||||
<button class="secondary-command" @onclick="FitFiducialPoints">Inquadra tutti i PF</button>
|
||||
<div class="fiducial-list">
|
||||
@foreach (var point in MappedFiducialPoints)
|
||||
{
|
||||
<article class="fiducial-item">
|
||||
<strong>@point.Identifier</strong>
|
||||
<small>Nord/X: @FormatCoordinate(point.CadastralNorth) · Est/Y: @FormatCoordinate(point.CadastralEast)</small>
|
||||
@if (!string.IsNullOrWhiteSpace(point.MonographUrl))
|
||||
{
|
||||
<button class="secondary-command" @onclick="() => DownloadFiducialMonograph(point.MonographUrl)">Scarica PDF monografia</button>
|
||||
}
|
||||
</article>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
<div class="service-note">
|
||||
<strong>Monografie ufficiali, posizione sperimentale</strong>
|
||||
<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>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
else if (ActiveTab == "data")
|
||||
{
|
||||
<section class="panel-content">
|
||||
@@ -745,6 +849,12 @@
|
||||
private string MonthlyPrice { get; set; } = string.Empty;
|
||||
private string YearlyPrice { get; set; } = string.Empty;
|
||||
private bool BillingBusy { get; set; }
|
||||
private bool FiducialBusy { get; set; }
|
||||
private string SelectedFiducialProvinceCode { get; set; } = string.Empty;
|
||||
private string SelectedFiducialMunicipalityCode { get; set; } = string.Empty;
|
||||
private string SelectedFiducialSheet { get; set; } = string.Empty;
|
||||
private string FiducialMessage { get; set; } = string.Empty;
|
||||
private string FiducialColor { get; set; } = "#dc2626";
|
||||
private string Status { get; set; } = "online";
|
||||
private string SelectedBaseLayer { get; set; } = "satellite";
|
||||
private string GoogleMapsApiKey { get; set; } = string.Empty;
|
||||
@@ -762,6 +872,7 @@
|
||||
private bool GpsOverlayCollapsed { get; set; }
|
||||
private bool ShowGpsPosition { get; set; } = true;
|
||||
private bool ShowSelectedParcels { get; set; } = true;
|
||||
private bool ShowFiducialPoints { get; set; } = true;
|
||||
private bool ShowImportedData { get; set; } = true;
|
||||
private bool DrawToolsVisible { get; set; }
|
||||
private bool TrailEnabled { get; set; }
|
||||
@@ -821,6 +932,11 @@
|
||||
private IReadOnlyList<MunicipalityOption> Municipalities { get; set; } = [];
|
||||
private IReadOnlyList<string> Sheets { get; set; } = [];
|
||||
private IReadOnlyList<ParcelOption> Parcels { get; set; } = [];
|
||||
private IReadOnlyList<FiducialAreaOption> FiducialProvinces { get; set; } = [];
|
||||
private IReadOnlyList<FiducialAreaOption> FiducialMunicipalities { get; set; } = [];
|
||||
private IReadOnlyList<FiducialAreaOption> FiducialSheets { get; set; } = [];
|
||||
private IReadOnlyList<FiducialPointEntry> FiducialPoints { get; set; } = [];
|
||||
private IReadOnlyList<MappedFiducialPoint> MappedFiducialPoints { get; set; } = [];
|
||||
private IReadOnlyList<MapProjectSummary> Projects { get; set; } = [];
|
||||
private IReadOnlyList<MapProjectVersion> ProjectVersions { get; set; } = [];
|
||||
private MapProjectSummary? SelectedProject => Projects.FirstOrDefault(item => item.Id == SelectedProjectId);
|
||||
@@ -834,6 +950,7 @@
|
||||
private string PanelTitle => ActiveTab switch
|
||||
{
|
||||
"cadastre" => "Ricerca catastale",
|
||||
"fiducials" => "Punti fiduciali",
|
||||
"data" => "Dati e disegni",
|
||||
"projects" => "Archivio progetti",
|
||||
"save" => "Salvataggio ed esportazione",
|
||||
@@ -845,6 +962,7 @@
|
||||
private string PanelEyebrow => ActiveTab switch
|
||||
{
|
||||
"cadastre" => "Regione, Comune, Foglio, Particella",
|
||||
"fiducials" => "Monografie ufficiali AdE",
|
||||
"data" => "File GIS, misure e POI",
|
||||
"projects" => "Salva, apri e visualizza i tuoi lavori",
|
||||
"save" => "Esporta file GIS",
|
||||
@@ -1056,6 +1174,10 @@
|
||||
{
|
||||
await RefreshProjectsAsync();
|
||||
}
|
||||
if (tab == "fiducials" && CanUse(AppFeatures.FiducialPoints) && FiducialProvinces.Count == 0)
|
||||
{
|
||||
await LoadFiducialProvincesAsync();
|
||||
}
|
||||
await Task.Delay(30);
|
||||
await JS.InvokeVoidAsync("lmeMap.resize");
|
||||
if (LayerPanelOpen)
|
||||
@@ -1135,6 +1257,185 @@
|
||||
: await CatalogService.GetMunicipalitiesAsync(SelectedRegionFile);
|
||||
}
|
||||
|
||||
private async Task LoadFiducialProvincesAsync()
|
||||
{
|
||||
FiducialBusy = true;
|
||||
FiducialMessage = "Carico l'elenco delle province...";
|
||||
try
|
||||
{
|
||||
FiducialProvinces = await FiducialPointService.GetProvincesAsync();
|
||||
FiducialMessage = string.Empty;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
FiducialMessage = $"Servizio monografie non disponibile: {error.GetBaseException().Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
FiducialBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnFiducialProvinceChanged(ChangeEventArgs args)
|
||||
{
|
||||
SelectedFiducialProvinceCode = args.Value?.ToString() ?? string.Empty;
|
||||
SelectedFiducialMunicipalityCode = string.Empty;
|
||||
SelectedFiducialSheet = string.Empty;
|
||||
FiducialMunicipalities = [];
|
||||
FiducialSheets = [];
|
||||
FiducialPoints = [];
|
||||
MappedFiducialPoints = [];
|
||||
if (string.IsNullOrWhiteSpace(SelectedFiducialProvinceCode))
|
||||
{
|
||||
FiducialMessage = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
FiducialBusy = true;
|
||||
FiducialMessage = "Carico i Comuni catastali...";
|
||||
try
|
||||
{
|
||||
FiducialMunicipalities = await FiducialPointService.GetMunicipalitiesAsync(SelectedFiducialProvinceCode);
|
||||
FiducialMessage = string.Empty;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
FiducialMessage = $"Comuni non disponibili: {error.GetBaseException().Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
FiducialBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnFiducialMunicipalityChanged(ChangeEventArgs args)
|
||||
{
|
||||
SelectedFiducialMunicipalityCode = args.Value?.ToString() ?? string.Empty;
|
||||
SelectedFiducialSheet = string.Empty;
|
||||
FiducialSheets = [];
|
||||
FiducialPoints = [];
|
||||
MappedFiducialPoints = [];
|
||||
if (string.IsNullOrWhiteSpace(SelectedFiducialMunicipalityCode))
|
||||
{
|
||||
FiducialMessage = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
FiducialBusy = true;
|
||||
FiducialMessage = "Carico i fogli disponibili...";
|
||||
try
|
||||
{
|
||||
FiducialSheets = await FiducialPointService.GetSheetsAsync(
|
||||
SelectedFiducialProvinceCode,
|
||||
SelectedFiducialMunicipalityCode);
|
||||
FiducialMessage = string.Empty;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
FiducialMessage = $"Fogli non disponibili: {error.GetBaseException().Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
FiducialBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SearchFiducials()
|
||||
{
|
||||
var province = FiducialProvinces.FirstOrDefault(item => item.Code == SelectedFiducialProvinceCode);
|
||||
var municipality = FiducialMunicipalities.FirstOrDefault(item => item.Code == SelectedFiducialMunicipalityCode);
|
||||
if (province is null || municipality is null || string.IsNullOrWhiteSpace(SelectedFiducialSheet))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FiducialBusy = true;
|
||||
FiducialPoints = [];
|
||||
MappedFiducialPoints = [];
|
||||
FiducialMessage = "Recupero monografie e coordinate dei PF...";
|
||||
try
|
||||
{
|
||||
string? monographError = null;
|
||||
try
|
||||
{
|
||||
FiducialPoints = await FiducialPointService.GetFiducialsAsync(
|
||||
province.Name,
|
||||
municipality.Name,
|
||||
municipality.Code,
|
||||
SelectedFiducialSheet);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
monographError = error.GetBaseException().Message;
|
||||
}
|
||||
|
||||
var mapped = await FiducialPointService.GetMappedFiducialsAsync(
|
||||
municipality.Code,
|
||||
SelectedFiducialSheet);
|
||||
var monographs = FiducialPoints.ToDictionary(
|
||||
item => item.Identifier,
|
||||
item => item.MonographUrl,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
var currentMappedPoints = monographs.Count == 0
|
||||
? mapped
|
||||
: mapped.Where(point => monographs.ContainsKey(point.Identifier)).ToArray();
|
||||
MappedFiducialPoints = currentMappedPoints
|
||||
.Select(point => point with
|
||||
{
|
||||
MonographUrl = monographs.GetValueOrDefault(point.Identifier, string.Empty)
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
await JS.InvokeVoidAsync(
|
||||
"lmeMap.setFiducialPoints",
|
||||
MappedFiducialPoints,
|
||||
FiducialColor,
|
||||
ShowFiducialPoints);
|
||||
FiducialMessage = MappedFiducialPoints.Count == 0
|
||||
? "Nessun Punto Fiduciale georiferito per il foglio selezionato."
|
||||
: $"Trovati {MappedFiducialPoints.Count} PF cartografici e {FiducialPoints.Count} monografie ufficiali."
|
||||
+ (monographError is null ? string.Empty : " Le monografie non sono al momento raggiungibili.");
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
FiducialMessage = $"Ricerca PF non riuscita: {error.GetBaseException().Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
FiducialBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadFiducialMonograph(string monographUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Launcher.Default.OpenAsync(new Uri(monographUrl));
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
FiducialMessage = $"Impossibile aprire la monografia: {error.GetBaseException().Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyFiducialVisibility()
|
||||
{
|
||||
SaveFlagPreferences();
|
||||
await JS.InvokeVoidAsync("lmeMap.setFiducialPointsVisible", ShowFiducialPoints);
|
||||
}
|
||||
|
||||
private async Task ApplyFiducialColor()
|
||||
{
|
||||
SaveFlagPreferences();
|
||||
await JS.InvokeVoidAsync("lmeMap.setFiducialColor", FiducialColor);
|
||||
}
|
||||
|
||||
private async Task FitFiducialPoints() =>
|
||||
await JS.InvokeVoidAsync("lmeMap.fitFiducialPoints");
|
||||
|
||||
private static string FormatCoordinate(double? value) =>
|
||||
value?.ToString("0.000", System.Globalization.CultureInfo.InvariantCulture) ?? "-";
|
||||
|
||||
private async Task OnMunicipalityChanged(ChangeEventArgs args)
|
||||
{
|
||||
SelectedMunicipalityCode = args.Value?.ToString() ?? string.Empty;
|
||||
@@ -2360,6 +2661,8 @@
|
||||
DrawToolsVisible = GetFlagPreference("draw_tools_visible", false);
|
||||
ShowGpsPosition = GetFlagPreference("show_gps_position", true);
|
||||
ShowSelectedParcels = GetFlagPreference("show_selected_parcels", true);
|
||||
ShowFiducialPoints = GetFlagPreference("show_fiducial_points", true);
|
||||
FiducialColor = Preferences.Default.Get($"{FlagPreferencePrefix}fiducial_color", "#dc2626");
|
||||
TrailEnabled = GetFlagPreference("trail_enabled", false);
|
||||
ExportImported = GetFlagPreference("export_imported", true);
|
||||
ExportImportedSheets = GetFlagPreference("export_imported_sheets", true);
|
||||
@@ -2400,6 +2703,8 @@
|
||||
SetFlagPreference("draw_tools_visible", DrawToolsVisible);
|
||||
SetFlagPreference("show_gps_position", ShowGpsPosition);
|
||||
SetFlagPreference("show_selected_parcels", ShowSelectedParcels);
|
||||
SetFlagPreference("show_fiducial_points", ShowFiducialPoints);
|
||||
Preferences.Default.Set($"{FlagPreferencePrefix}fiducial_color", FiducialColor);
|
||||
SetFlagPreference("trail_enabled", TrailEnabled);
|
||||
SetFlagPreference("export_imported", ExportImported);
|
||||
SetFlagPreference("export_imported_sheets", ExportImportedSheets);
|
||||
@@ -2437,6 +2742,8 @@
|
||||
await JS.InvokeVoidAsync("lmeMap.setDrawToolsVisible", DrawToolsVisible);
|
||||
await JS.InvokeVoidAsync("lmeMap.setGpsPositionVisible", ShowGpsPosition);
|
||||
await JS.InvokeVoidAsync("lmeMap.setSelectedParcelsVisible", ShowSelectedParcels);
|
||||
await JS.InvokeVoidAsync("lmeMap.setFiducialColor", FiducialColor);
|
||||
await JS.InvokeVoidAsync("lmeMap.setFiducialPointsVisible", ShowFiducialPoints);
|
||||
await JS.InvokeVoidAsync("lmeMap.toggleTrail", TrailEnabled);
|
||||
|
||||
foreach (var layer in CadastralLayers)
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
<ApplicationId>it.liviomerola.lmemappecatastali.pro</ApplicationId>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>1.0.11</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>111</ApplicationVersion>
|
||||
<ApplicationDisplayVersion>1.0.13</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>113</ApplicationVersion>
|
||||
<Version>$(ApplicationDisplayVersion)</Version>
|
||||
|
||||
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||
|
||||
@@ -19,6 +19,7 @@ public static class MauiProgram
|
||||
builder.Services.AddSingleton<PreciseLocationService>();
|
||||
builder.Services.AddSingleton<CadastralCatalogService>();
|
||||
builder.Services.AddSingleton<OfficialCadastreService>();
|
||||
builder.Services.AddSingleton<FiducialPointService>();
|
||||
builder.Services.AddSingleton<LocationSearchService>();
|
||||
builder.Services.AddSingleton<NetworkLocationService>();
|
||||
builder.Services.AddSingleton<AzureMapsService>();
|
||||
|
||||
@@ -21,7 +21,8 @@ public sealed class EntitlementService(
|
||||
AppFeatures.CloudSync,
|
||||
AppFeatures.OfflineMaps,
|
||||
AppFeatures.AdvancedGeometry,
|
||||
AppFeatures.GpsTrail
|
||||
AppFeatures.GpsTrail,
|
||||
AppFeatures.FiducialPoints
|
||||
};
|
||||
|
||||
public bool IsGoogleProActive { get; private set; }
|
||||
@@ -185,4 +186,5 @@ public static class AppFeatures
|
||||
public const string OfflineMaps = "offline-maps";
|
||||
public const string AdvancedGeometry = "advanced-geometry";
|
||||
public const string GpsTrail = "gps-trail";
|
||||
public const string FiducialPoints = "fiducial-points";
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace LMEMappeCatastaliApp.Services;
|
||||
|
||||
public sealed partial class FiducialPointService
|
||||
{
|
||||
private const string ServiceBaseUrl = "https://www1.agenziaentrate.gov.it/servizi/Monografie/";
|
||||
private const string MappedPointsUrl = "https://cartografia.globogis.it/arcgis/rest/services/fiduciali/Punti_fiduciali/MapServer/2/query";
|
||||
private readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(30) };
|
||||
|
||||
public FiducialPointService()
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/1.0 (Android)");
|
||||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<FiducialAreaOption>> GetProvincesAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var html = await GetHtmlAsync("ricerca.php", cancellationToken);
|
||||
return ParseOptions(html, "provincia");
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<FiducialAreaOption>> GetMunicipalitiesAsync(
|
||||
string provinceCode,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var code = NormalizeCode(provinceCode);
|
||||
var html = await GetHtmlAsync($"ricerca.php?level=1&prov={Uri.EscapeDataString(code)}", cancellationToken);
|
||||
return ParseOptions(html, "co");
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<FiducialAreaOption>> GetSheetsAsync(
|
||||
string provinceCode,
|
||||
string municipalityCode,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var province = NormalizeCode(provinceCode);
|
||||
var municipality = NormalizeCode(municipalityCode);
|
||||
var path = $"ricerca.php?level=2&prov={Uri.EscapeDataString(province)}";
|
||||
var html = await PostFormAsync(path, new Dictionary<string, string> { ["co"] = municipality }, cancellationToken);
|
||||
return ParseOptions(html, "foglio");
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<FiducialPointEntry>> GetFiducialsAsync(
|
||||
string provinceName,
|
||||
string municipalityName,
|
||||
string municipalityCode,
|
||||
string sheet,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var code = NormalizeCode(municipalityCode);
|
||||
var normalizedSheet = sheet.Trim();
|
||||
var query = string.Join("&",
|
||||
[
|
||||
$"provincia={Uri.EscapeDataString(provinceName.Trim())}",
|
||||
$"comune={Uri.EscapeDataString(municipalityName.Trim())}",
|
||||
$"co={Uri.EscapeDataString(code)}"
|
||||
]);
|
||||
var html = await PostFormAsync(
|
||||
$"risultato.php?{query}",
|
||||
new Dictionary<string, string> { ["foglio"] = normalizedSheet },
|
||||
cancellationToken);
|
||||
|
||||
return DownloadLinkRegex().Matches(html)
|
||||
.Select(match =>
|
||||
{
|
||||
var relativeUrl = WebUtility.HtmlDecode(match.Groups["url"].Value);
|
||||
var fileName = WebUtility.HtmlDecode(match.Groups["name"].Value);
|
||||
return new FiducialPointEntry(
|
||||
FormatIdentifier(fileName),
|
||||
new Uri(new Uri(ServiceBaseUrl), relativeUrl).AbsoluteUri);
|
||||
})
|
||||
.DistinctBy(item => item.Identifier, StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(item => item.Identifier, StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<MappedFiducialPoint>> GetMappedFiducialsAsync(
|
||||
string municipalityCode,
|
||||
string sheet,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var municipality = NormalizeCode(municipalityCode);
|
||||
if (!int.TryParse(new string(sheet.Where(char.IsDigit).ToArray()), out var sheetNumber))
|
||||
{
|
||||
throw new ArgumentException("Numero di foglio non valido.", nameof(sheet));
|
||||
}
|
||||
|
||||
var query = new Dictionary<string, string>
|
||||
{
|
||||
["f"] = "json",
|
||||
["where"] = $"COMUNENAZI='{municipality}' AND FOGLIO={sheetNumber}",
|
||||
["outFields"] = "ETICHETTA,PARTICELLA,NORD,EST,ATTENDIBIL,QUOTA,DATAAGGIOR,IDORIGINE,URL_AGENZI,FOGLIO,ALLEGATO,FIDUCIALE",
|
||||
["returnGeometry"] = "true",
|
||||
["outSR"] = "3857",
|
||||
["resultRecordCount"] = "500"
|
||||
};
|
||||
var url = $"{MappedPointsUrl}?{string.Join('&', query.Select(item => $"{item.Key}={Uri.EscapeDataString(item.Value)}"))}";
|
||||
using var response = await _httpClient.GetAsync(url, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
using var document = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
|
||||
if (document.RootElement.TryGetProperty("error", out var error))
|
||||
{
|
||||
throw new InvalidOperationException(error.TryGetProperty("message", out var message)
|
||||
? message.GetString()
|
||||
: "Servizio cartografico PF non disponibile.");
|
||||
}
|
||||
|
||||
if (!document.RootElement.TryGetProperty("features", out var features))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return features.EnumerateArray()
|
||||
.Select(feature => ParseMappedPoint(feature, municipality))
|
||||
.Where(point => point is not null)
|
||||
.Cast<MappedFiducialPoint>()
|
||||
.DistinctBy(point => point.Identifier, StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(point => point.Identifier, StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static MappedFiducialPoint? ParseMappedPoint(JsonElement feature, string municipalityCode)
|
||||
{
|
||||
if (!feature.TryGetProperty("geometry", out var geometry)
|
||||
|| !TryGetDouble(geometry, "x", out var x)
|
||||
|| !TryGetDouble(geometry, "y", out var y))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var attributes = feature.GetProperty("attributes");
|
||||
var label = GetString(attributes, "ETICHETTA");
|
||||
var identifier = FormatIdentifier(label);
|
||||
if (identifier == label)
|
||||
{
|
||||
var sheet = GetString(attributes, "FOGLIO").PadLeft(4, '0');
|
||||
var number = GetString(attributes, "FIDUCIALE").PadLeft(2, '0');
|
||||
identifier = $"PF{number}/{sheet}/{municipalityCode}";
|
||||
}
|
||||
|
||||
const double earthRadius = 6378137d;
|
||||
var longitude = x / earthRadius * 180d / Math.PI;
|
||||
var latitude = (2d * Math.Atan(Math.Exp(y / earthRadius)) - Math.PI / 2d) * 180d / Math.PI;
|
||||
return new MappedFiducialPoint(
|
||||
identifier,
|
||||
latitude,
|
||||
longitude,
|
||||
GetNullableDouble(attributes, "NORD"),
|
||||
GetNullableDouble(attributes, "EST"),
|
||||
GetString(attributes, "ATTENDIBIL"),
|
||||
GetNullableDouble(attributes, "QUOTA"),
|
||||
GetString(attributes, "PARTICELLA"),
|
||||
GetString(attributes, "DATAAGGIOR"),
|
||||
GetString(attributes, "IDORIGINE"));
|
||||
}
|
||||
|
||||
private static bool TryGetDouble(JsonElement element, string name, out double value)
|
||||
{
|
||||
value = 0;
|
||||
return element.TryGetProperty(name, out var property)
|
||||
&& (property.ValueKind == JsonValueKind.Number
|
||||
? property.TryGetDouble(out value)
|
||||
: double.TryParse(property.ToString(), System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out value));
|
||||
}
|
||||
|
||||
private static double? GetNullableDouble(JsonElement element, string name) =>
|
||||
TryGetDouble(element, name, out var value) ? value : null;
|
||||
|
||||
private static string GetString(JsonElement element, string name) =>
|
||||
element.TryGetProperty(name, out var property) && property.ValueKind != JsonValueKind.Null
|
||||
? property.ToString().Trim()
|
||||
: string.Empty;
|
||||
|
||||
private async Task<string> GetHtmlAsync(string relativeUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
using var response = await _httpClient.GetAsync(ServiceBaseUrl + relativeUrl, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<string> PostFormAsync(
|
||||
string relativeUrl,
|
||||
IReadOnlyDictionary<string, string> values,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
using var content = new FormUrlEncodedContent(values);
|
||||
using var response = await _httpClient.PostAsync(ServiceBaseUrl + relativeUrl, content, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<FiducialAreaOption> ParseOptions(string html, string selectId)
|
||||
{
|
||||
var select = SelectRegex().Matches(html)
|
||||
.LastOrDefault(match => string.Equals(match.Groups["id"].Value, selectId, StringComparison.OrdinalIgnoreCase));
|
||||
if (select is null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return OptionRegex().Matches(select.Groups["body"].Value)
|
||||
.Select(match => new FiducialAreaOption(
|
||||
WebUtility.HtmlDecode(match.Groups["value"].Value).Trim(),
|
||||
WebUtility.HtmlDecode(StripTagsRegex().Replace(match.Groups["label"].Value, string.Empty)).Trim()))
|
||||
.Where(item => !string.IsNullOrWhiteSpace(item.Code) && !string.IsNullOrWhiteSpace(item.Name))
|
||||
.DistinctBy(item => item.Code, StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static string NormalizeCode(string value)
|
||||
{
|
||||
var normalized = value.Trim().ToUpperInvariant();
|
||||
if (normalized.Length is < 2 or > 4 || normalized.Any(character => !char.IsLetterOrDigit(character)))
|
||||
{
|
||||
throw new ArgumentException("Codice territoriale non valido.", nameof(value));
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static string FormatIdentifier(string fileName)
|
||||
{
|
||||
var parts = fileName.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
return parts.Length == 3 ? $"PF{parts[2]}/{parts[1]}/{parts[0]}" : fileName;
|
||||
}
|
||||
|
||||
[GeneratedRegex("<select\\b[^>]*(?:id|name)=[\\\"'](?<id>[^\\\"']+)[\\\"'][^>]*>(?<body>[\\s\\S]*?)</select>", RegexOptions.IgnoreCase, matchTimeoutMilliseconds: 2000)]
|
||||
private static partial Regex SelectRegex();
|
||||
|
||||
[GeneratedRegex("<option\\b[^>]*value=[\\\"'](?<value>[^\\\"']*)[\\\"'][^>]*>(?<label>[\\s\\S]*?)</option>", RegexOptions.IgnoreCase, matchTimeoutMilliseconds: 2000)]
|
||||
private static partial Regex OptionRegex();
|
||||
|
||||
[GeneratedRegex("href=[\\\"'](?<url>download\\.php\\?[^\\\"']*namefile=(?<name>[A-Z0-9-]+))[^\\\"']*[\\\"']", RegexOptions.IgnoreCase, matchTimeoutMilliseconds: 2000)]
|
||||
private static partial Regex DownloadLinkRegex();
|
||||
|
||||
[GeneratedRegex("<[^>]+>", RegexOptions.None, 2000)]
|
||||
private static partial Regex StripTagsRegex();
|
||||
}
|
||||
|
||||
public sealed record FiducialAreaOption(string Code, string Name);
|
||||
|
||||
public sealed record FiducialPointEntry(string Identifier, string MonographUrl);
|
||||
|
||||
public sealed record MappedFiducialPoint(
|
||||
string Identifier,
|
||||
double Latitude,
|
||||
double Longitude,
|
||||
double? CadastralNorth,
|
||||
double? CadastralEast,
|
||||
string Reliability,
|
||||
double? Elevation,
|
||||
string Parcel,
|
||||
string UpdatedAt,
|
||||
string OriginId,
|
||||
string MonographUrl = "");
|
||||
@@ -1247,6 +1247,9 @@ input[type="checkbox"] {
|
||||
|
||||
.gps-overlay.is-collapsed {
|
||||
grid-template-columns: auto;
|
||||
right: auto;
|
||||
width: 48px;
|
||||
max-width: 48px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
@@ -1255,6 +1258,28 @@ input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fiducial-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.fiducial-item {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #d9e0e3;
|
||||
}
|
||||
|
||||
.fiducial-item strong {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.fiducial-item .secondary-command {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gps-overlay.search-open {
|
||||
top: 140px;
|
||||
}
|
||||
@@ -1881,6 +1906,12 @@ footer {
|
||||
top: 136px;
|
||||
}
|
||||
|
||||
.gps-overlay.is-collapsed {
|
||||
right: auto;
|
||||
width: 48px;
|
||||
max-width: 48px;
|
||||
}
|
||||
|
||||
.leaflet-top.leaflet-right {
|
||||
top: 174px;
|
||||
right: 0;
|
||||
@@ -1936,6 +1967,17 @@ footer {
|
||||
grid-template-columns: minmax(0, 1fr) auto auto;
|
||||
}
|
||||
|
||||
.gps-overlay.is-collapsed {
|
||||
grid-template-columns: auto;
|
||||
right: auto;
|
||||
width: 48px;
|
||||
max-width: 48px;
|
||||
}
|
||||
|
||||
.fiducial-item {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.gps-main,
|
||||
.gps-center {
|
||||
min-width: 86px;
|
||||
|
||||
+143
-2
@@ -9,6 +9,7 @@ const lmeState = {
|
||||
"gpsTrail",
|
||||
"gpsParcels",
|
||||
"selectedParcels",
|
||||
"fiducials",
|
||||
"importedSheets",
|
||||
"imported",
|
||||
"cadastre"
|
||||
@@ -50,6 +51,9 @@ const lmeState = {
|
||||
gpsTrail: null,
|
||||
selectedParcelsLayer: null,
|
||||
selectedParcelsVisible: true,
|
||||
fiducials: null,
|
||||
fiducialsVisible: true,
|
||||
fiducialColor: "#dc2626",
|
||||
gpsParcelLayer: null,
|
||||
gpsTrailEnabled: false,
|
||||
gpsTrailPoints: [],
|
||||
@@ -131,6 +135,10 @@ window.lmeMap = {
|
||||
closeCadastrePreview,
|
||||
clearSelectedParcels,
|
||||
setSelectedParcelsVisible,
|
||||
setFiducialPoints,
|
||||
setFiducialPointsVisible,
|
||||
setFiducialColor,
|
||||
fitFiducialPoints,
|
||||
clearMapElements,
|
||||
fitImported,
|
||||
clearImported,
|
||||
@@ -310,6 +318,7 @@ function init(dotNetReference) {
|
||||
lmeState.importedMeasurements = L.layerGroup().addTo(lmeState.map);
|
||||
lmeState.drawingMeasurements = L.layerGroup().addTo(lmeState.map);
|
||||
lmeState.selectedParcelsLayer = L.featureGroup().addTo(lmeState.map);
|
||||
lmeState.fiducials = L.featureGroup().addTo(lmeState.map);
|
||||
lmeState.gpsParcelLayer = L.featureGroup().addTo(lmeState.map);
|
||||
const navigationSheetPane = lmeState.map.createPane("navigationSheetPane");
|
||||
navigationSheetPane.style.zIndex = "465";
|
||||
@@ -1339,6 +1348,7 @@ function managedLayerPanes() {
|
||||
gpsTrail: "gpsTrailPane",
|
||||
gpsParcels: "gpsParcelsPane",
|
||||
selectedParcels: "selectedParcelsPane",
|
||||
fiducials: "fiducialsPane",
|
||||
importedSheets: "importedSheetPane",
|
||||
imported: "importedPane",
|
||||
cadastre: "cadastrePane"
|
||||
@@ -1427,6 +1437,7 @@ function layerManagerCategories() {
|
||||
{ key: "gpsTrail", label: "Traccia GPS", roots: [lmeState.gpsTrail].filter(Boolean) },
|
||||
{ key: "gpsParcels", label: "Particella rilevata dal GPS", roots: [lmeState.gpsParcelLayer].filter(Boolean) },
|
||||
{ key: "selectedParcels", label: "Particelle catastali evidenziate", roots: [lmeState.selectedParcelsLayer].filter(Boolean) },
|
||||
{ key: "fiducials", label: "Punti fiduciali", roots: [lmeState.fiducials].filter(Boolean) },
|
||||
{ key: "importedSheets", label: "Cornici e fogli importati", roots: [lmeState.importedSheets].filter(Boolean), blocks: true },
|
||||
{ key: "imported", label: "Progetti e dati importati", roots: [lmeState.imported].filter(Boolean), blocks: true },
|
||||
{ key: "cadastre", label: "Layer catastali AdE", roots: [lmeState.cadastre].filter(Boolean) }
|
||||
@@ -3271,6 +3282,8 @@ function setManagedCategoryVisible(category, visible) {
|
||||
lmeState.importedSheetsVisible = visible;
|
||||
} else if (category.key === "selectedParcels") {
|
||||
lmeState.selectedParcelsVisible = visible;
|
||||
} else if (category.key === "fiducials") {
|
||||
lmeState.fiducialsVisible = visible;
|
||||
} else if (category.key === "gpsPosition") {
|
||||
lmeState.gpsPositionVisible = visible;
|
||||
}
|
||||
@@ -3344,6 +3357,9 @@ function managedCategoryEnabled(categoryKey) {
|
||||
if (categoryKey === "selectedParcels") {
|
||||
return lmeState.selectedParcelsVisible;
|
||||
}
|
||||
if (categoryKey === "fiducials") {
|
||||
return lmeState.fiducialsVisible;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4721,7 +4737,7 @@ function circleToPolygon(center, radiusMeters) {
|
||||
function createProjectSnapshot(includeCadastralParcels) {
|
||||
const center = lmeState.map.getCenter();
|
||||
return JSON.stringify({
|
||||
schemaVersion: 5,
|
||||
schemaVersion: 6,
|
||||
view: {
|
||||
latitude: center.lat,
|
||||
longitude: center.lng,
|
||||
@@ -4731,6 +4747,7 @@ function createProjectSnapshot(includeCadastralParcels) {
|
||||
imported: managedGroupGeoJson(lmeState.imported, "imported"),
|
||||
importedSheets: managedGroupGeoJson(lmeState.importedSheets, "importedSheets"),
|
||||
importedSheetsVisible: lmeState.importedSheetsVisible,
|
||||
fiducials: managedGroupGeoJson(lmeState.fiducials, "fiducials"),
|
||||
displayOptions: captureDisplayOptions(),
|
||||
selectedParcels: includeCadastralParcels
|
||||
? managedGroupGeoJson(lmeState.selectedParcelsLayer, "selectedParcels")
|
||||
@@ -4766,6 +4783,7 @@ function loadProjectSnapshot(raw, projectName) {
|
||||
lmeState.drawingMeasurements.clearLayers();
|
||||
lmeState.importedMeasurements.clearLayers();
|
||||
lmeState.selectedParcelsLayer.clearLayers();
|
||||
lmeState.fiducials.clearLayers();
|
||||
lmeState.gpsParcelLayer.clearLayers();
|
||||
|
||||
const restoredDrawings = L.geoJSON(data.drawings || emptyFeatureCollection(), {
|
||||
@@ -4826,6 +4844,10 @@ function loadProjectSnapshot(raw, projectName) {
|
||||
applyStyleFromProperties(layer);
|
||||
}
|
||||
}).addTo(lmeState.selectedParcelsLayer);
|
||||
L.geoJSON(data.fiducials || emptyFeatureCollection(), {
|
||||
pointToLayer: (feature, latlng) => createFiducialMarker(feature, latlng),
|
||||
onEachFeature: (feature, layer) => bindFiducialPopup(feature, layer)
|
||||
}).addTo(lmeState.fiducials);
|
||||
if (lmeState.importedSheetsVisible) {
|
||||
void ensureImportedSheetOutlines().then(() => {
|
||||
applyImportedSheetVisibility();
|
||||
@@ -4848,6 +4870,7 @@ function loadProjectSnapshot(raw, projectName) {
|
||||
lmeState.drawings,
|
||||
lmeState.imported,
|
||||
lmeState.selectedParcelsLayer,
|
||||
lmeState.fiducials,
|
||||
lmeState.gpsTrail
|
||||
];
|
||||
if (lmeState.importedSheetsVisible) {
|
||||
@@ -4898,6 +4921,8 @@ function captureDisplayOptions() {
|
||||
drawToolsVisible: Boolean(lmeState.drawControl),
|
||||
gpsPositionVisible: lmeState.gpsPositionVisible,
|
||||
selectedParcelsVisible: lmeState.selectedParcelsVisible,
|
||||
fiducialsVisible: lmeState.fiducialsVisible,
|
||||
fiducialColor: lmeState.fiducialColor,
|
||||
layerOrder: [...lmeState.layerOrder],
|
||||
layerObjectOrder: { ...lmeState.managerObjectOrder },
|
||||
layerCategoryNames: { ...lmeState.managerCategoryNames },
|
||||
@@ -4965,6 +4990,10 @@ function applyProjectDisplayOptions(options) {
|
||||
}
|
||||
setGpsPositionVisible(options.gpsPositionVisible !== false);
|
||||
setSelectedParcelsVisible(options.selectedParcelsVisible !== false);
|
||||
if (typeof options.fiducialColor === "string") {
|
||||
setFiducialColor(options.fiducialColor);
|
||||
}
|
||||
setFiducialPointsVisible(options.fiducialsVisible !== false);
|
||||
toggleTrail(Boolean(options.trailEnabled));
|
||||
setDrawToolsVisible(Boolean(options.drawToolsVisible));
|
||||
applyStoredLayerManagerVisibility();
|
||||
@@ -5634,6 +5663,116 @@ function activeCadastreLayers() {
|
||||
return drawOrder.filter(layer => lmeState.cadastreLayers.includes(layer));
|
||||
}
|
||||
|
||||
function setFiducialPoints(points, color, visible = true) {
|
||||
if (!lmeState.fiducials) return;
|
||||
const source = Array.isArray(points) ? points : [];
|
||||
if (source.length || lmeState.fiducials.getLayers().length) rememberMapChange();
|
||||
lmeState.fiducials.clearLayers();
|
||||
clearManagedCategoryState("fiducials");
|
||||
if (typeof color === "string" && /^#[0-9a-f]{6}$/i.test(color)) {
|
||||
lmeState.fiducialColor = color;
|
||||
}
|
||||
source.forEach((point, index) => {
|
||||
const latitude = Number(point.latitude ?? point.Latitude);
|
||||
const longitude = Number(point.longitude ?? point.Longitude);
|
||||
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) return;
|
||||
const feature = {
|
||||
type: "Feature",
|
||||
geometry: { type: "Point", coordinates: [longitude, latitude] },
|
||||
properties: {
|
||||
name: String(point.identifier ?? point.Identifier ?? `PF ${index + 1}`),
|
||||
_lmeDisplayName: String(point.identifier ?? point.Identifier ?? `PF ${index + 1}`),
|
||||
_lmeSource: "Punti fiduciali georiferiti",
|
||||
_lmeColor: lmeState.fiducialColor,
|
||||
_lmeFillColor: lmeState.fiducialColor,
|
||||
_lmeWeight: 3,
|
||||
_lmeOpacity: 1,
|
||||
_lmeFillOpacity: 0.72,
|
||||
cadastralNorth: point.cadastralNorth ?? point.CadastralNorth ?? null,
|
||||
cadastralEast: point.cadastralEast ?? point.CadastralEast ?? null,
|
||||
reliability: String(point.reliability ?? point.Reliability ?? ""),
|
||||
elevation: point.elevation ?? point.Elevation ?? null,
|
||||
parcel: String(point.parcel ?? point.Parcel ?? ""),
|
||||
updatedAt: String(point.updatedAt ?? point.UpdatedAt ?? ""),
|
||||
originId: String(point.originId ?? point.OriginId ?? ""),
|
||||
monographUrl: String(point.monographUrl ?? point.MonographUrl ?? "")
|
||||
}
|
||||
};
|
||||
const marker = createFiducialMarker(feature, L.latLng(latitude, longitude));
|
||||
marker.feature = feature;
|
||||
bindFiducialPopup(feature, marker);
|
||||
lmeState.fiducials.addLayer(marker);
|
||||
});
|
||||
setFiducialPointsVisible(visible);
|
||||
refreshLayerManager();
|
||||
if (source.length) finishMapChange();
|
||||
}
|
||||
|
||||
function createFiducialMarker(feature, latlng) {
|
||||
const properties = feature?.properties || {};
|
||||
const color = properties._lmeColor || lmeState.fiducialColor;
|
||||
return L.circleMarker(latlng, {
|
||||
pane: "fiducialsPane",
|
||||
radius: 8,
|
||||
color,
|
||||
fillColor: properties._lmeFillColor || color,
|
||||
weight: Number(properties._lmeWeight) || 3,
|
||||
opacity: Number.isFinite(Number(properties._lmeOpacity)) ? Number(properties._lmeOpacity) : 1,
|
||||
fillOpacity: Number.isFinite(Number(properties._lmeFillOpacity)) ? Number(properties._lmeFillOpacity) : 0.72
|
||||
});
|
||||
}
|
||||
|
||||
function bindFiducialPopup(feature, layer) {
|
||||
const properties = feature?.properties || {};
|
||||
const rows = [];
|
||||
if (properties.cadastralNorth != null) rows.push(`Nord/X catastale: ${escapeHtml(properties.cadastralNorth)}`);
|
||||
if (properties.cadastralEast != null) rows.push(`Est/Y catastale: ${escapeHtml(properties.cadastralEast)}`);
|
||||
if (properties.reliability) rows.push(`Attendibilita: ${escapeHtml(properties.reliability)}`);
|
||||
if (properties.elevation != null) rows.push(`Quota: ${escapeHtml(properties.elevation)}`);
|
||||
if (properties.parcel) rows.push(`Particella: ${escapeHtml(properties.parcel)}`);
|
||||
const url = String(properties.monographUrl || "");
|
||||
const monograph = /^https:\/\//i.test(url)
|
||||
? `<p><a href="${escapeHtml(url)}" target="_blank" rel="noopener">Apri monografia ufficiale</a></p>`
|
||||
: "";
|
||||
layer.bindPopup(`<strong>${escapeHtml(properties._lmeDisplayName || properties.name || "Punto fiduciale")}</strong><p>${rows.join("<br>")}</p>${monograph}<small>Posizione cartografica sperimentale GloboGIS: verificare prima dell'uso topografico.</small>`);
|
||||
applyStyleFromProperties(layer);
|
||||
}
|
||||
|
||||
function setFiducialPointsVisible(enabled) {
|
||||
lmeState.fiducialsVisible = Boolean(enabled);
|
||||
if (!lmeState.map || !lmeState.fiducials) return;
|
||||
if (lmeState.fiducialsVisible) {
|
||||
lmeState.managerHiddenCategories.delete("fiducials");
|
||||
if (!lmeState.map.hasLayer(lmeState.fiducials)) lmeState.fiducials.addTo(lmeState.map);
|
||||
lmeState.fiducials.bringToFront?.();
|
||||
} else {
|
||||
lmeState.managerHiddenCategories.add("fiducials");
|
||||
if (lmeState.map.hasLayer(lmeState.fiducials)) lmeState.map.removeLayer(lmeState.fiducials);
|
||||
}
|
||||
refreshLayerManager();
|
||||
}
|
||||
|
||||
function setFiducialColor(color) {
|
||||
if (!/^#[0-9a-f]{6}$/i.test(String(color))) return;
|
||||
lmeState.fiducialColor = color;
|
||||
lmeState.fiducials?.eachLayer(layer => {
|
||||
if (layer.feature?.properties) {
|
||||
layer.feature.properties._lmeColor = color;
|
||||
layer.feature.properties._lmeFillColor = color;
|
||||
}
|
||||
applyStyleFromProperties(layer);
|
||||
});
|
||||
refreshLayerManager();
|
||||
}
|
||||
|
||||
function fitFiducialPoints() {
|
||||
if (!lmeState.fiducials?.getLayers().length) {
|
||||
setInfo("Nessun Punto Fiduciale da inquadrare.");
|
||||
return;
|
||||
}
|
||||
fitLayer(lmeState.fiducials);
|
||||
}
|
||||
|
||||
function clearSelectedParcels() {
|
||||
const count = managedGroupGeoJson(lmeState.selectedParcelsLayer, "selectedParcels").features.length;
|
||||
if (count) rememberMapChange();
|
||||
@@ -5672,6 +5811,7 @@ function setSelectedParcelsVisible(enabled) {
|
||||
function clearMapElements() {
|
||||
const count = managedGroupGeoJson(lmeState.selectedParcelsLayer, "selectedParcels").features.length
|
||||
+ managedGroupGeoJson(lmeState.gpsParcelLayer, "gpsParcels").features.length
|
||||
+ managedGroupGeoJson(lmeState.fiducials, "fiducials").features.length
|
||||
+ managedGroupGeoJson(lmeState.imported, "imported").features.length
|
||||
+ managedGroupGeoJson(lmeState.importedSheets, "importedSheets").features.length
|
||||
+ managedGroupGeoJson(lmeState.drawings, "drawings").features.length
|
||||
@@ -5680,7 +5820,7 @@ function clearMapElements() {
|
||||
setInfo("La mappa non contiene elementi da pulire.");
|
||||
return;
|
||||
}
|
||||
if (!window.confirm("Rimuovere particelle evidenziate, file importati, disegni, POI e traccia GPS dalla mappa?")) {
|
||||
if (!window.confirm("Rimuovere particelle evidenziate, Punti Fiduciali, file importati, disegni, POI e traccia GPS dalla mappa?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5688,6 +5828,7 @@ function clearMapElements() {
|
||||
resetLayerManagerProjectState();
|
||||
lmeState.selectedParcelsLayer.clearLayers();
|
||||
lmeState.gpsParcelLayer.clearLayers();
|
||||
lmeState.fiducials.clearLayers();
|
||||
lmeState.imported.clearLayers();
|
||||
lmeState.importedSheets.clearLayers();
|
||||
lmeState.drawings.clearLayers();
|
||||
|
||||
Reference in new issue
Block a user