Geolocation

Browse sample. Browse the sample

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(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.