Compare commits

..
1 Commits
Author SHA1 Message Date
livio 0344227a1b Center map on startup location 2026-09-21 11:24:22 +02:00
6 changed files with 155 additions and 19 deletions

No files matched your search

+37
View File
@@ -291,6 +291,10 @@
<section class="panel-section"> <section class="panel-section">
<h2>GPS e percorso</h2> <h2>GPS e percorso</h2>
<label class="toggle-line project-save-option">
<input type="checkbox" checked="@ShowGpsPosition" @onchange="ToggleGpsPosition" />
<span>Mostra punto e cerchio GPS</span>
</label>
<label class="toggle-line project-save-option"> <label class="toggle-line project-save-option">
<input type="checkbox" checked="@TrailEnabled" @onchange="ToggleTrail" /> <input type="checkbox" checked="@TrailEnabled" @onchange="ToggleTrail" />
<span>Registra la traccia mentre cammini</span> <span>Registra la traccia mentre cammini</span>
@@ -363,6 +367,7 @@
private double? CatalogFraction { get; set; } private double? CatalogFraction { get; set; }
private bool CatalogBusy { get; set; } private bool CatalogBusy { get; set; }
private bool GpsActive { get; set; } private bool GpsActive { get; set; }
private bool ShowGpsPosition { get; set; } = true;
private bool DrawToolsVisible { get; set; } private bool DrawToolsVisible { get; set; }
private bool TrailEnabled { get; set; } private bool TrailEnabled { get; set; }
private bool LayerPanelOpen { get; set; } private bool LayerPanelOpen { get; set; }
@@ -453,10 +458,36 @@
if (firstRender) if (firstRender)
{ {
await JS.InvokeVoidAsync("lmeMap.init"); await JS.InvokeVoidAsync("lmeMap.init");
await CenterOnInitialLocationAsync();
await MigrateLegacyProjectAsync(); await MigrateLegacyProjectAsync();
} }
} }
private async Task CenterOnInitialLocationAsync()
{
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(12));
try
{
var location = await LocationService.GetCurrentLocationAsync(timeout.Token);
if (location is not null)
{
await JS.InvokeVoidAsync("lmeMap.showInitialPosition", location);
}
else
{
await JS.InvokeVoidAsync("lmeMap.showInfo", "Posizione iniziale non disponibile. La mappa resta utilizzabile normalmente.");
}
}
catch (OperationCanceledException)
{
await JS.InvokeVoidAsync("lmeMap.showInfo", "Ricerca della posizione iniziale scaduta. Usa Avvia GPS per riprovare.");
}
catch (Exception error)
{
await JS.InvokeVoidAsync("lmeMap.showInfo", $"Posizione iniziale non disponibile: {error.GetBaseException().Message}");
}
}
private async Task MigrateLegacyProjectAsync() private async Task MigrateLegacyProjectAsync()
{ {
try try
@@ -680,6 +711,12 @@
} }
} }
private async Task ToggleGpsPosition(ChangeEventArgs args)
{
ShowGpsPosition = (bool?)args.Value == true;
await JS.InvokeVoidAsync("lmeMap.setGpsPositionVisible", ShowGpsPosition);
}
private async Task StartGps() private async Task StartGps()
{ {
GpsActive = true; GpsActive = true;
+2 -2
View File
@@ -28,8 +28,8 @@
<ApplicationId>com.companyname.lmemappecatastaliapp</ApplicationId> <ApplicationId>com.companyname.lmemappecatastaliapp</ApplicationId>
<!-- Versions --> <!-- Versions -->
<ApplicationDisplayVersion>0.2.9</ApplicationDisplayVersion> <ApplicationDisplayVersion>0.2.10</ApplicationDisplayVersion>
<ApplicationVersion>13</ApplicationVersion> <ApplicationVersion>14</ApplicationVersion>
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged --> <!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
<WindowsPackageType>None</WindowsPackageType> <WindowsPackageType>None</WindowsPackageType>
+2
View File
@@ -19,6 +19,8 @@ App .NET MAUI Blazor Hybrid per Android, iOS, Mac Catalyst e Windows, pensata pe
- Archivio multiprogetto nella cartella privata dell'app, con nome, selezione da elenco, sovrascrittura ed eliminazione. - Archivio multiprogetto nella cartella privata dell'app, con nome, selezione da elenco, sovrascrittura ed eliminazione.
- Ogni progetto salva file GIS, POI, disegni, traccia e inquadratura; l'inclusione delle particelle catastali rosse e' opzionale. - Ogni progetto salva file GIS, POI, disegni, traccia e inquadratura; l'inclusione delle particelle catastali rosse e' opzionale.
- GPS nativo MAUI ad alta precisione con permessi Android/iOS. - GPS nativo MAUI ad alta precisione con permessi Android/iOS.
- Centratura automatica una tantum sulla posizione attuale all'apertura, senza mostrare il punto o il cerchio e senza avviare il tracking GPS continuo.
- Punto e cerchio di precisione del GPS live possono essere mostrati o nascosti dalle impostazioni senza fermare il rilevamento.
- Marker posizione, cerchio accuratezza in metri, tracciamento percorso. - Marker posizione, cerchio accuratezza in metri, tracciamento percorso.
- Controllo se la posizione GPS cade dentro un poligono importato o disegnato. - Controllo se la posizione GPS cade dentro un poligono importato o disegnato.
- Interrogazione WFS ufficiale Agenzia delle Entrate attorno alla posizione GPS per evidenziare la particella catastale corrente quando il servizio restituisce la geometria. - Interrogazione WFS ufficiale Agenzia delle Entrate attorno alla posizione GPS per evidenziare la particella catastale corrente quando il servizio restituisce la geometria.
+1 -1
View File
@@ -13,7 +13,7 @@ public sealed class AzureMapsService
public AzureMapsService() public AzureMapsService()
{ {
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/0.2.9"); _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("LMEMappeCatastali/0.2.10");
_httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json"); _httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json");
} }
+41 -8
View File
@@ -20,6 +20,37 @@ public sealed class PreciseLocationService : IAsyncDisposable
return status; return status;
} }
public async Task<LocationSnapshot?> GetCurrentLocationAsync(CancellationToken cancellationToken = default)
{
var permission = await EnsurePermissionAsync();
if (permission != PermissionStatus.Granted)
{
return null;
}
Location? location = null;
try
{
var request = new GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(8));
location = await Geolocation.Default.GetLocationAsync(request, cancellationToken);
}
catch (FeatureNotSupportedException)
{
return null;
}
if (location is null)
{
var lastKnown = await Geolocation.Default.GetLastKnownLocationAsync();
if (lastKnown is not null && lastKnown.Timestamp >= DateTimeOffset.UtcNow.AddMinutes(-15))
{
location = lastKnown;
}
}
return location is null ? null : ToSnapshot(location);
}
public async Task StartTrackingAsync(Func<LocationSnapshot, Task> onLocation, Func<string, Task> onError) public async Task StartTrackingAsync(Func<LocationSnapshot, Task> onLocation, Func<string, Task> onError)
{ {
if (trackingCancellation is not null) if (trackingCancellation is not null)
@@ -47,14 +78,7 @@ public sealed class PreciseLocationService : IAsyncDisposable
var location = await Geolocation.Default.GetLocationAsync(request, token); var location = await Geolocation.Default.GetLocationAsync(request, token);
if (location is not null) if (location is not null)
{ {
await onLocation(new LocationSnapshot( await onLocation(ToSnapshot(location));
location.Latitude,
location.Longitude,
location.Accuracy,
location.Altitude,
location.Course,
location.Speed,
location.Timestamp));
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@@ -84,6 +108,15 @@ public sealed class PreciseLocationService : IAsyncDisposable
StopTracking(); StopTracking();
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
private static LocationSnapshot ToSnapshot(Location location) => new(
location.Latitude,
location.Longitude,
location.Accuracy,
location.Altitude,
location.Course,
location.Speed,
location.Timestamp);
} }
public sealed record LocationSnapshot( public sealed record LocationSnapshot(
+72 -8
View File
@@ -19,6 +19,9 @@ const lmeState = {
measurementRefreshTimer: null, measurementRefreshTimer: null,
gpsMarker: null, gpsMarker: null,
gpsAccuracy: null, gpsAccuracy: null,
gpsPositionVisible: true,
hasLiveGpsFix: false,
lastAccuracy: 0,
gpsTrail: null, gpsTrail: null,
selectedParcelsLayer: null, selectedParcelsLayer: null,
gpsParcelLayer: null, gpsParcelLayer: null,
@@ -40,6 +43,7 @@ const cadastreCrs = L.extend({}, L.CRS.EPSG4326, { code: "EPSG:4258" });
window.lmeMap = { window.lmeMap = {
init, init,
showSearchResult, showSearchResult,
showInitialPosition,
displayOfficialParcel, displayOfficialParcel,
exportCadastreView, exportCadastreView,
closeCadastrePreview, closeCadastrePreview,
@@ -69,6 +73,7 @@ window.lmeMap = {
centerOnGps, centerOnGps,
toggleTrail, toggleTrail,
setNativeGpsPosition, setNativeGpsPosition,
setGpsPositionVisible,
setGpsError, setGpsError,
resize, resize,
showInfo: setInfo showInfo: setInfo
@@ -178,6 +183,23 @@ function showSearchResult(latitude, longitude, label) {
setInfo(label || `${lat.toFixed(6)}, ${lon.toFixed(6)}`); setInfo(label || `${lat.toFixed(6)}, ${lon.toFixed(6)}`);
} }
function showInitialPosition(location) {
const latitude = Number(location?.latitude);
const longitude = Number(location?.longitude);
const accuracy = Math.max(0, Number(location?.accuracy) || 0);
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) {
return;
}
const latlng = [latitude, longitude];
lmeState.lastPosition = latlng;
lmeState.lastAccuracy = accuracy;
lmeState.map.setView(latlng, accuracy > 80 ? 17 : 19);
setText("gpsState", "Posizione iniziale acquisita - GPS non attivo");
setInfo("Mappa centrata sulla posizione attuale. GPS continuo non attivo.");
updateGpsInsideState();
}
function displayOfficialParcel(xmlText, latitude, longitude, parcel, label, shouldFit) { function displayOfficialParcel(xmlText, latitude, longitude, parcel, label, shouldFit) {
const lat = Number(latitude); const lat = Number(latitude);
const lon = Number(longitude); const lon = Number(longitude);
@@ -1705,6 +1727,25 @@ function setNativeGpsPosition(location) {
}); });
} }
function setGpsPositionVisible(enabled) {
lmeState.gpsPositionVisible = Boolean(enabled);
if (!lmeState.gpsPositionVisible) {
if (lmeState.gpsMarker && lmeState.map.hasLayer(lmeState.gpsMarker)) {
lmeState.map.removeLayer(lmeState.gpsMarker);
}
if (lmeState.gpsAccuracy && lmeState.map.hasLayer(lmeState.gpsAccuracy)) {
lmeState.map.removeLayer(lmeState.gpsAccuracy);
}
setInfo("Punto e cerchio GPS nascosti. Il rilevamento continua.");
return;
}
if (lmeState.hasLiveGpsFix && lmeState.lastPosition) {
updateGpsPositionLayers(lmeState.lastPosition, lmeState.lastAccuracy);
}
setInfo("Punto e cerchio GPS visibili.");
}
function setGpsError(error) { function setGpsError(error) {
setText("gpsState", "GPS non disponibile"); setText("gpsState", "GPS non disponibile");
setInfo(error || "Errore GPS."); setInfo(error || "Errore GPS.");
@@ -1729,14 +1770,12 @@ function onGpsPosition(position) {
const { latitude, longitude, accuracy } = position.coords; const { latitude, longitude, accuracy } = position.coords;
const latlng = [latitude, longitude]; const latlng = [latitude, longitude];
lmeState.lastPosition = latlng; lmeState.lastPosition = latlng;
lmeState.lastAccuracy = Math.max(0, Number(accuracy) || 0);
if (!lmeState.gpsMarker) { const firstLiveFix = !lmeState.hasLiveGpsFix;
lmeState.gpsMarker = L.marker(latlng, { title: "La tua posizione" }).addTo(lmeState.map); lmeState.hasLiveGpsFix = true;
lmeState.gpsAccuracy = L.circle(latlng, { radius: accuracy, color: "#2563eb", weight: 2, fillColor: "#60a5fa", fillOpacity: 0.16 }).addTo(lmeState.map); updateGpsPositionLayers(latlng, lmeState.lastAccuracy);
if (firstLiveFix) {
lmeState.map.setView(latlng, 19); lmeState.map.setView(latlng, 19);
} else {
lmeState.gpsMarker.setLatLng(latlng);
lmeState.gpsAccuracy.setLatLng(latlng).setRadius(accuracy);
} }
if (lmeState.gpsTrailEnabled) { if (lmeState.gpsTrailEnabled) {
@@ -1744,10 +1783,35 @@ function onGpsPosition(position) {
lmeState.gpsTrail.setLatLngs(lmeState.gpsTrailPoints); lmeState.gpsTrail.setLatLngs(lmeState.gpsTrailPoints);
} }
setText("gpsState", `${latitude.toFixed(6)}, ${longitude.toFixed(6)} - precisione ${Math.round(accuracy)} m`); setText("gpsState", `${latitude.toFixed(6)}, ${longitude.toFixed(6)} - precisione ${Math.round(lmeState.lastAccuracy)} m`);
updateGpsInsideState(); updateGpsInsideState();
} }
function updateGpsPositionLayers(latlng, accuracy) {
if (!lmeState.gpsPositionVisible) {
return;
}
if (!lmeState.gpsMarker) {
lmeState.gpsMarker = L.marker(latlng, { title: "La tua posizione" });
lmeState.gpsAccuracy = L.circle(latlng, {
radius: accuracy,
color: "#2563eb",
weight: 2,
fillColor: "#60a5fa",
fillOpacity: 0.16
});
} else {
lmeState.gpsMarker.setLatLng(latlng);
lmeState.gpsAccuracy.setLatLng(latlng).setRadius(accuracy);
}
if (!lmeState.map.hasLayer(lmeState.gpsMarker)) {
lmeState.gpsMarker.addTo(lmeState.map);
}
if (!lmeState.map.hasLayer(lmeState.gpsAccuracy)) {
lmeState.gpsAccuracy.addTo(lmeState.map);
}
}
function onGpsError(error) { function onGpsError(error) {
setText("gpsState", "GPS non autorizzato o non disponibile"); setText("gpsState", "GPS non autorizzato o non disponibile");
setInfo(error.message || "Errore GPS."); setInfo(error.message || "Errore GPS.");