Geolocalización
En este artículo se describe cómo puedes usar la interfaz IGeolocation de .NET Multi-platform App UI (.NET MAUI). Esta interfaz proporciona las API para recuperar las coordenadas de geolocalización actuales del dispositivo.
La implementación predeterminada de la interfaz IGeolocation
está disponible a través de la propiedad Geolocation.Default. Tanto la interfaz IGeolocation
como la clase Geolocation
están contenidas en el espacio de nombres Microsoft.Maui.Devices.Sensors
.
Introducción
Para acceder a la funcionalidad de Geolocalización, se requiere la siguiente configuración específica para la plataforma:
En el proyecto de Android deben especificarse y configurarse el permiso Coarse Location o el permiso Fine Location o ambos.
Además, si la aplicación tiene como destino Android 5.0 (nivel de API 21) o versiones posteriores, debe declarar que la aplicación usa las características de hardware en el archivo de manifiesto. Se puede agregar de las siguientes maneras:
Agrega el permiso basado en ensamblados:
Abre el archivo Platforms/Android/MainApplication.cs y agrega los siguientes atributos de ensamblado después de las directivas
using
:[assembly: UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)] [assembly: UsesPermission(Android.Manifest.Permission.AccessFineLocation)] [assembly: UsesFeature("android.hardware.location", Required = false)] [assembly: UsesFeature("android.hardware.location.gps", Required = false)] [assembly: UsesFeature("android.hardware.location.network", Required = false)]
Si tu aplicación tiene como destino Android 10-Q (nivel de API 29 o superior) y solicita
LocationAlways
, debes agregar también la siguiente solicitud de permiso:[assembly: UsesPermission(Android.Manifest.Permission.AccessBackgroundLocation)]
- o -
Actualiza el manifiesto de Android:
Abre el archivo Platforms/Android/AndroidManifest.xml y agrega esto en el nodo
manifest
:<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-feature android:name="android.hardware.location" android:required="false" /> <uses-feature android:name="android.hardware.location.gps" android:required="false" /> <uses-feature android:name="android.hardware.location.network" android:required="false" />
Si tu aplicación tiene como destino Android 10-Q (nivel de API 29 o superior) y solicita
LocationAlways
, debes agregar también la siguiente solicitud de permiso:<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- o -
Actualiza el manifiesto de Android en el editor de manifiestos:
En Visual Studio, haz doble clic en el archivo Platforms/Android/AndroidManifest.xml para abrir el editor de manifiestos de Android. Luego, comprueba los permisos enumerados arriba en Permisos necesarios. Esto actualizará automáticamente el archivo AndroidManifest.xml.
Sugerencia
Se recomienda leer la documentación de Android sobre actualizaciones de ubicación en segundo plano, ya que hay muchas restricciones que deben tenerse en cuenta.
Obtención de la última ubicación conocida
Es posible que el dispositivo haya almacenado en caché la ubicación más reciente del dispositivo. Usa el método GetLastKnownLocationAsync() para acceder a la ubicación almacenada en caché, si está disponible. Esto suele ser más rápido que realizar una consulta completa, pero puede ser menos preciso. Si no existe ninguna ubicación almacenada en caché, este método devuelve null
.
Nota:
La API Geolocation también le pedirá permisos al usuario cuando sea necesario.
En el ejemplo de código siguiente se muestra la comprobación de una ubicación almacenada en caché:
public async Task<string> GetCachedLocation()
{
try
{
Location location = await Geolocation.Default.GetLastKnownLocationAsync();
if (location != null)
return $"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}";
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
}
catch (PermissionException pEx)
{
// Handle permission exception
}
catch (Exception ex)
{
// Unable to get location
}
return "None";
}
Dependiendo del dispositivo, puede que no todos los valores de ubicación estén disponibles. Por ejemplo, la propiedad Altitude podría ser null
o tener un valor de 0 o positivo indicando los metros por encima del nivel del mar. Otros valores que pueden no estar presentes son, entre otros, las propiedades Speed y Course.
Obtención de la ubicación actual
Aunque la comprobación de la última ubicación conocida del dispositivo puede ser más rápida, puede ser inexacta. Usa el método GetLocationAsync para consultar al dispositivo la ubicación actual. Puedes configurar la precisión y el tiempo de espera de la consulta. Es mejor utilizar la sobrecarga del método que usa los parámetros GeolocationRequest y CancellationToken, ya que puede llevar algún tiempo obtener la ubicación del dispositivo.
Nota:
La API Geolocation también le pedirá permisos al usuario cuando sea necesario.
En el ejemplo de código siguiente se muestra cómo solicitar la ubicación del dispositivo, a la vez que se admite la cancelación:
private CancellationTokenSource _cancelTokenSource;
private bool _isCheckingLocation;
public async Task GetCurrentLocation()
{
try
{
_isCheckingLocation = true;
GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
_cancelTokenSource = new CancellationTokenSource();
Location location = await Geolocation.Default.GetLocationAsync(request, _cancelTokenSource.Token);
if (location != null)
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
}
// Catch one of the following exceptions:
// FeatureNotSupportedException
// FeatureNotEnabledException
// PermissionException
catch (Exception ex)
{
// Unable to get location
}
finally
{
_isCheckingLocation = false;
}
}
public void CancelRequest()
{
if (_isCheckingLocation && _cancelTokenSource != null && _cancelTokenSource.IsCancellationRequested == false)
_cancelTokenSource.Cancel();
}
Es posible que no todos los valores de ubicación estén disponibles, dependiendo del dispositivo. Por ejemplo, la propiedad Altitude podría ser null
o tener un valor de 0 o positivo indicando los metros por encima del nivel del mar. Otros valores que pueden no estar presentes son, entre otros, Speed y Course.
Advertencia
GetLocationAsync puede devolver null
en algunos escenarios. Esto indica que la plataforma subyacente no puede obtener la ubicación actual.
Escuchar los cambios de ubicación
Además de consultar al dispositivo la ubicación actual, puedes escuchar los cambios de ubicación mientras una aplicación está en primer plano.
Para comprobar si la aplicación está escuchando los cambios de ubicación, hay una propiedad IsListeningForeground que puedes consultar. Cuando estés listo para empezar a escuchar los cambios de ubicación, debes llamar al método StartListeningForegroundAsync. Este método comienza a escuchar las actualizaciones de ubicación y genera el evento LocationChanged cuando cambia la ubicación, siempre que la aplicación esté en primer plano. El objeto GeolocationLocationChangedEventArgs que acompaña a este evento tiene una propiedad Location, de tipo Location, que representa la nueva ubicación que se ha detectado.
Nota:
La API Geolocation también le pedirá permisos al usuario cuando sea necesario.
En el ejemplo de código siguiente se muestra cómo escuchar un cambio de ubicación y cómo procesar la ubicación modificada:
async void OnStartListening()
{
try
{
Geolocation.LocationChanged += Geolocation_LocationChanged;
var request = new GeolocationListeningRequest((GeolocationAccuracy)Accuracy);
var success = await Geolocation.StartListeningForegroundAsync(request);
string status = success
? "Started listening for foreground location updates"
: "Couldn't start listening";
}
catch (Exception ex)
{
// Unable to start listening for location changes
}
}
void Geolocation_LocationChanged(object sender, GeolocationLocationChangedEventArgs e)
{
// Process e.Location to get the new location
}
El control de errores se puede implementar registrando un controlador de eventos para el evento ListeningFailed. El objeto GeolocationListeningFailedEventArgs que acompaña a este evento tiene una propiedad Error, de tipo GeolocationError, que indica por qué se produjo un error en la escucha. Cuando se genera el evento ListeningFailed, se detiene la escucha de cambios de ubicación y no se genera ningún evento LocationChanged más.
Para detener la escucha de cambios de ubicación, llama al método StopListeningForeground:
void OnStopListening()
{
try
{
Geolocation.LocationChanged -= Geolocation_LocationChanged;
Geolocation.StopListeningForeground();
string status = "Stopped listening for foreground location updates";
}
catch (Exception ex)
{
// Unable to stop listening for location changes
}
}
Nota:
El método StopListeningForeground no tiene ningún efecto cuando la aplicación no está escuchando los cambios de ubicación.
Precisión
En las secciones siguientes se describe la distancia de precisión de ubicación, por plataforma:
Importante
iOS tiene algunas limitaciones con respecto a la precisión. Para obtener más información, consulta la sección Diferencias entre plataformas.
Mínima
Plataforma | Distancia (en metros) |
---|---|
Android | 500 |
iOS | 3000 |
Windows | 1000 - 5000 |
Bajo
Plataforma | Distancia (en metros) |
---|---|
Android | 500 |
iOS | 1000 |
Windows | 300 - 3000 |
Media (valor predeterminado)
Plataforma | Distancia (en metros) |
---|---|
Android | 100 - 500 |
iOS | 100 |
Windows | 30-500 |
Alto
Plataforma | Distancia (en metros) |
---|---|
Android | 0 - 100 |
iOS | 10 |
Windows | <= 10 |
Óptima
Plataforma | Distancia (en metros) |
---|---|
Android | 0 - 100 |
iOS | ~0 |
Windows | <= 10 |
Detección de ubicaciones ficticias
Algunos dispositivos pueden devolver una ubicación ficticia desde el proveedor o desde una aplicación que proporciona ubicaciones ficticias. Puedes detectarlo usando IsFromMockProvider en cualquier Location:
public async Task CheckMock()
{
GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium);
Location location = await Geolocation.Default.GetLocationAsync(request);
if (location != null && location.IsFromMockProvider)
{
// location is from a mock provider
}
}
Distancia entre dos ubicaciones
El método CalculateDistance calcula la distancia entre dos ubicaciones geográficas. Esta distancia calculada no tiene en cuenta carreteras ni otros caminos, y es simplemente la distancia más corta entre los dos puntos a lo largo de la superficie de la Tierra. Este cálculo se conoce como cálculo de distancia ortodrómica.
El código siguiente calcula la distancia entre las ciudades estadounidenses de Boston y San Francisco:
Location boston = new Location(42.358056, -71.063611);
Location sanFrancisco = new Location(37.783333, -122.416667);
double miles = Location.CalculateDistance(boston, sanFrancisco, DistanceUnits.Miles);
El constructor Location(Double, Double, Double) acepta los argumentos de latitud y longitud, respectivamente. Los valores de latitud positivos están al norte del Ecuador, y los valores de longitud positivos están al este del meridiano de Greenwich. Use el argumento final CalculateDistance
para especificar millas o kilómetros. La clase UnitConverters también define los métodos KilometersToMiles y MilesToKilometers para la conversión entre las dos unidades.
Diferencias entre plataformas
En esta sección se describen las diferencias específicas de la plataforma con la API de geolocalización.
La altitud se calcula de forma diferente en cada plataforma.
En Android, la altitud, si está disponible, se devuelve en metros por encima del elipsoide de referencia WGS 84. Si esta ubicación no tiene altitud, se devuelve 0.0
.
La propiedad Location.ReducedAccuracy solo la usa iOS y devuelve false
en todas las demás plataformas.