Initial Android cadastral map app

This commit is contained in:
livio committed 2026-09-11 08:34:16 +02:00
commit bc4e82b518
43 files changed
+2137

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
@inherits LayoutComponentBase
<main class="app-shell">
@Body
</main>
+215
View File
@@ -0,0 +1,215 @@
@page "/"
@inject IJSRuntime JS
@inject PreciseLocationService LocationService
@implements IAsyncDisposable
<PageTitle>LME Mappe Catastali</PageTitle>
<div class="map-app">
<aside class="tool-panel">
<section class="brand">
<div>
<p class="eyebrow">Catasto, rilievi e progetti</p>
<h1>LME Mappe Catastali</h1>
</div>
<span class="status">@Status</span>
</section>
<section class="panel-section">
<h2>Ricerca libera</h2>
<div class="input-row">
<input @bind="SearchText" @bind:event="oninput" placeholder="Indirizzo, localita, coordinate..." />
<button @onclick="SearchLocation">Cerca</button>
</div>
</section>
<section class="panel-section">
<h2>Particella catastale</h2>
<div class="field-grid">
<label>
Regione
<input @bind="CadastralRegion" placeholder="Lombardia" />
</label>
<label>
Comune
<input @bind="CadastralMunicipality" placeholder="Milano" />
</label>
<label>
Codice catastale
<input @bind="CadastralMunicipalityCode" placeholder="F205" />
</label>
<label>
Foglio
<input @bind="CadastralSheet" placeholder="12" />
</label>
<label>
Particella
<input @bind="CadastralParcel" placeholder="345" />
</label>
</div>
<div class="button-row">
<button @onclick="LocateParcel">Vai alla zona</button>
<button @onclick="ExportCadastralImage">Scarica vista</button>
</div>
<p class="hint">Il layer ufficiale WMS dell'Agenzia delle Entrate e' attivo; la selezione geometrica precisa della particella sara' collegata al servizio WFS/API.</p>
</section>
<section class="panel-section">
<h2>File GIS</h2>
<label class="file-picker">
<span>Carica KML, KMZ o GeoJSON</span>
<input id="gisFileInput" type="file" accept=".kml,.kmz,.geojson,.json,application/vnd.google-earth.kml+xml,application/vnd.google-earth.kmz,application/geo+json" />
</label>
<div class="button-row">
<button @onclick="FitImportedLayers">Inquadra file</button>
<button @onclick="ClearImportedLayers">Pulisci file</button>
</div>
</section>
<section class="panel-section">
<h2>Progetti e POI</h2>
<div class="input-row">
<input @bind="ProjectName" @bind:event="oninput" placeholder="Nome progetto o POI" />
<button @onclick="AddProjectMarker">Aggiungi</button>
</div>
<div class="button-row">
<button @onclick="EnableDrawTools">Disegna</button>
<button @onclick="SaveWorkspace">Salva</button>
<button @onclick="LoadWorkspace">Carica</button>
</div>
</section>
<section class="panel-section live-section">
<h2>GPS dal vivo</h2>
<div class="gps-readout">
<span id="gpsState">GPS non attivo</span>
<strong id="parcelState">Fuori da poligoni caricati</strong>
</div>
<div class="button-row">
<button @onclick="StartGps">Segui posizione</button>
<button @onclick="StopGps">Stop</button>
<button @onclick="CenterOnGps">Centra</button>
</div>
<label class="toggle-line">
<input type="checkbox" checked @onchange="ToggleTrail" />
Registra percorso
</label>
</section>
<section class="panel-section">
<h2>Livelli</h2>
<div class="field-grid compact">
<label>
Sfondo
<select @onchange="ChangeBaseLayer">
<option value="satellite">Satellite</option>
<option value="osm">Strade</option>
</select>
</label>
<label>
Trasparenza catasto
<input type="range" min="0" max="1" step="0.05" value="0.75" @onchange="ChangeCadastreOpacity" />
</label>
</div>
<div class="layer-toggles">
@foreach (var layer in CadastralLayers)
{
<label>
<input type="checkbox" checked="@layer.Enabled" @onchange="(args) => ToggleCadastreLayer(layer.Key, args)" />
@layer.Name
</label>
}
</div>
</section>
<footer>
Dati catastali: Agenzia delle Entrate, licenza CC BY 4.0. App indipendente.
</footer>
</aside>
<section class="map-stage">
<div id="map"></div>
<div id="mapInfo" class="map-info">Pronto</div>
</section>
</div>
@code {
private string SearchText { get; set; } = "";
private string CadastralRegion { get; set; } = "";
private string CadastralMunicipality { get; set; } = "";
private string CadastralMunicipalityCode { get; set; } = "";
private string CadastralSheet { get; set; } = "";
private string CadastralParcel { get; set; } = "";
private string ProjectName { get; set; } = "";
private string Status { get; set; } = "online";
private readonly List<CadastralLayer> CadastralLayers =
[
new("CP.CadastralParcel", "Particelle", true),
new("codice_plla", "Numeri particelle", true),
new("CP.CadastralZoning", "Fogli", true),
new("fabbricati", "Fabbricati", true),
new("strade", "Strade", false),
new("acque", "Acque", false)
];
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("lmeMap.init");
}
}
private Task SearchLocation() => JS.InvokeVoidAsync("lmeMap.search", SearchText).AsTask();
private Task LocateParcel()
{
var label = $"{CadastralRegion} {CadastralMunicipality} foglio {CadastralSheet} particella {CadastralParcel}".Trim();
return JS.InvokeVoidAsync("lmeMap.locateParcelArea", CadastralRegion, CadastralMunicipality, CadastralMunicipalityCode, CadastralSheet, CadastralParcel, label).AsTask();
}
private Task ExportCadastralImage() => JS.InvokeVoidAsync("lmeMap.exportCadastreView").AsTask();
private Task FitImportedLayers() => JS.InvokeVoidAsync("lmeMap.fitImported").AsTask();
private Task ClearImportedLayers() => JS.InvokeVoidAsync("lmeMap.clearImported").AsTask();
private Task AddProjectMarker() => JS.InvokeVoidAsync("lmeMap.addProjectMarker", ProjectName).AsTask();
private async Task StartGps()
{
await LocationService.StartTrackingAsync(
async location => await InvokeAsync(() => JS.InvokeVoidAsync("lmeMap.setNativeGpsPosition", location)),
async error => await InvokeAsync(() => JS.InvokeVoidAsync("lmeMap.setGpsError", error)));
}
private async Task StopGps()
{
LocationService.StopTracking();
await JS.InvokeVoidAsync("lmeMap.stopGps");
}
private Task CenterOnGps() => JS.InvokeVoidAsync("lmeMap.centerOnGps").AsTask();
private Task ToggleTrail(ChangeEventArgs args) => JS.InvokeVoidAsync("lmeMap.toggleTrail", (bool?)args.Value == true).AsTask();
private Task EnableDrawTools() => JS.InvokeVoidAsync("lmeMap.enableDrawTools").AsTask();
private Task SaveWorkspace() => JS.InvokeVoidAsync("lmeMap.saveWorkspace").AsTask();
private Task LoadWorkspace() => JS.InvokeVoidAsync("lmeMap.loadWorkspace").AsTask();
private Task ChangeBaseLayer(ChangeEventArgs args) => JS.InvokeVoidAsync("lmeMap.setBaseLayer", args.Value?.ToString()).AsTask();
private Task ChangeCadastreOpacity(ChangeEventArgs args) => JS.InvokeVoidAsync("lmeMap.setCadastreOpacity", args.Value?.ToString()).AsTask();
private Task ToggleCadastreLayer(string layer, ChangeEventArgs args) => JS.InvokeVoidAsync("lmeMap.toggleCadastreLayer", layer, (bool?)args.Value == true).AsTask();
private sealed record CadastralLayer(string Key, string Name, bool Enabled);
public async ValueTask DisposeAsync()
{
await LocationService.DisposeAsync();
}
}
+6
View File
@@ -0,0 +1,6 @@
<Router AppAssembly="@typeof(MauiProgram).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
+10
View File
@@ -0,0 +1,10 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using LMEMappeCatastaliApp
@using LMEMappeCatastaliApp.Components
@using LMEMappeCatastaliApp.Services