Edit

Share via


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

Check if ___location services are enabled

The Geolocation class has a read-only IsEnabled property that can be used to determine if ___location services have been enabled on the device.

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.