Geolocation
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) IGeolocation interface. This interface provides APIs to retrieve the device's current geolocation coordinates.
The default implementation of the IGeolocation
interface is available through the Geolocation.Default property. Both the IGeolocation
interface and Geolocation
class are contained in the Microsoft.Maui.Devices.Sensors
namespace.
Get started
To access the Geolocation functionality, the following platform-specific setup is required:
Coarse or fine location permissions, or both, must be specified and should be configured in the Android project.
Additionally, if your app targets Android 5.0 (API level 21) or higher, you must declare that your app uses the hardware features in the manifest file. This can be added in the following ways:
Add the assembly-based permission:
Open the Platforms/Android/MainApplication.cs file and add the following assembly attributes after
using
directives:[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)]
If your application is targeting Android 10 - Q (API Level 29 or higher) and is requesting
LocationAlways
, you must also add this permission request:[assembly: UsesPermission(Android.Manifest.Permission.AccessBackgroundLocation)]
- or -
Update the Android Manifest:
Open the Platforms/Android/AndroidManifest.xml file and add the following in the
manifest
node:<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" />
If your application is targeting Android 10 - Q (API Level 29 or higher) and is requesting
LocationAlways
, you must also add this permission request:<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- or -
Update the Android Manifest in the manifest editor:
In Visual Studio double-click on the Platforms/Android/AndroidManifest.xml file to open the Android manifest editor. Then, under Required permissions check the permissions listed above. This will automatically update the AndroidManifest.xml file.
Tip
Be sure to read the Android documentation on background location updates, as there are many restrictions that need to be considered.
Get the last known location
The device may have cached the most recent location of the device. Use the GetLastKnownLocationAsync() method to access the cached location, if available. This is often faster than doing a full location query, but can be less accurate. If no cached location exists, this method returns null
.
Note
When necessary, the Geolocation API prompts the user for permissions.
The following code example demonstrates checking for a cached location:
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";
}
Depending on the device, not all location values may be available. For example, the Altitude property might be null
, have a value of 0, or have a positive value indicating the meters above sea level. Other values that may not be present include the Speed and Course properties.
Get the current location
While checking for the last known location of the device may be quicker, it can be inaccurate. Use the GetLocationAsync method to query the device for the current location. You can configure the accuracy and timeout of the query. It's best to the method overload that uses the GeolocationRequest and CancellationToken parameters, since it may take some time to get the device's location.
Note
When necessary, the Geolocation API prompts the user for permissions.
The following code example demonstrates how to request the device's location, while supporting cancellation:
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();
}
Not all location values may be available, depending on the device. For example, the Altitude property might be null
, have a value of 0, or have a positive value indicating the meters above sea level. Other values that may not be present include Speed and Course.
Warning
GetLocationAsync can return null
in some scenarios. This indicates that the underlying platform is unable to obtain the current location.
Listen for location changes
In addition to querying the device for the current location, you can listen for location changes while an app is in the foreground.
To check to see if the app is currently listening for location changes, there's a IsListeningForeground property you can query. Once you're ready to start listening for location changes you should call the StartListeningForegroundAsync method. This method starts listening for location updates and raises the LocationChanged event when the location changes, provided that the app is in the foreground. The GeolocationLocationChangedEventArgs object that accompanies this event has a Location property, of type Location, that represents the new location that's been detected.
Note
When necessary, the Geolocation API prompts the user for permissions.
The following code example demonstrates how to listen for a location change, and how to process the changed location:
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
}
Error handling can be implemented by registering an event handler for the ListeningFailed event. The GeolocationListeningFailedEventArgs object that accompanies this event has an Error property, of type GeolocationError, that indicates why listening failed. When the ListeningFailed event is raised, listening for further location changes stops and no further LocationChanged events are raised.
To stop listening for location changes, call the StopListeningForeground method:
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
}
}
Note
The StopListeningForeground method has no effect when the app isn't listening for location changes.
Accuracy
The following sections outline the location accuracy distance, per platform:
Important
iOS has some limitations regarding accuracy. For more information, see the Platform differences section.
Lowest
Platform | Distance (in meters) |
---|---|
Android | 500 |
iOS | 3000 |
Windows | 1000 - 5000 |
Low
Platform | Distance (in meters) |
---|---|
Android | 500 |
iOS | 1000 |
Windows | 300 - 3000 |
Medium (Default)
Platform | Distance (in meters) |
---|---|
Android | 100 - 500 |
iOS | 100 |
Windows | 30-500 |
High
Platform | Distance (in meters) |
---|---|
Android | 0 - 100 |
iOS | 10 |
Windows | <= 10 |
Best
Platform | Distance (in meters) |
---|---|
Android | 0 - 100 |
iOS | ~0 |
Windows | <= 10 |
Detecting mock locations
Some devices may return a mock location from the provider or by an application that provides mock locations. You can detect this by using the IsFromMockProvider on any 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
}
}
Distance between two locations
The CalculateDistance method calculates the distance between two geographic locations. This calculated distance doesn't take roads or other pathways into account, and is merely the shortest distance between the two points along the surface of the Earth. This calculation is known as the great-circle distance calculation.
The following code calculates the distance between the United States of America cities of Boston and 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);
The Location(Double, Double, Double) constructor accepts the latitude and longitude arguments, respectively. Positive latitude values are north of the equator, and positive longitude values are east of the Prime Meridian. Use the final argument to CalculateDistance
to specify miles or kilometers. The UnitConverters class also defines KilometersToMiles and MilesToKilometers methods for converting between the two units.
Platform differences
This section describes the platform-specific differences with the geolocation API.
Altitude is calculated differently on each platform.
On Android, altitude, if available, is returned in meters above the WGS 84 reference ellipsoid. If this location doesn't have an altitude, 0.0
is returned.
The Location.ReducedAccuracy property is only used by iOS and returns false
on all other platforms.