Center map on startup location
This commit is contained in:
1 parent
7933b2e86a
commit
3327b51dcb
6 files changed
+155
-19
No files matched your search
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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.");
|
||||||
|
|||||||
Reference in new issue
Block a user