Поделиться через


Геолокация

Просмотрите пример. Обзор примера

В этой статье описывается, как использовать интерфейс многоплатформенного приложения .NET (.NET MAUI) IGeolocation. Этот интерфейс предоставляет API для получения текущих координат геолокации устройства.

Реализация интерфейса IGeolocation по умолчанию доступна через свойство Geolocation.Default. Интерфейс IGeolocation и класс Geolocation содержатся в пространстве имен Microsoft.Maui.Devices.Sensors.

Начало работы

Чтобы получить доступ к функционалу Геолокации, необходимо выполнить следующую настройку, специфичную для платформы:

Разрешения на определение местоположения с грубой или точной точностью, или оба типа, должны быть указаны и настроены в проекте Android.

Кроме того, если ваше приложение предназначено для Android 5.0 (уровень API 21) или выше, вы должны указать в файле манифеста, что ваше приложение использует аппаратные функции. Это можно добавить следующим образом:

  • Add the assembly-based permission:

    Откройте файл Platform/Android/MainApplication.cs и добавьте следующие атрибуты сборки после директив 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)]
    

    Если ваше приложение ориентировано на Android 10 - Q (уровень API 29 или выше) и запрашивает LocationAlways, необходимо также добавить этот запрос на разрешение:

    [assembly: UsesPermission(Android.Manifest.Permission.AccessBackgroundLocation)]
    

    - или -

  • Обновите манифест Android:

    Откройте файл Platform/Android/AndroidManifest.xml и добавьте следующее в узел 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" />
    

    Если ваше приложение нацелено на Android 10 - Q (уровень API 29 или выше) и запрашивает LocationAlways, вы также должны добавить этот запрос на разрешение:

    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    

    - or -

  • Обновите манифест Android в редакторе манифеста:

    В Visual Studio дважды щелкните файл Platform/Android/AndroidManifest.xml, чтобы открыть редактор манифеста Android. Затем, в разделе Необходимые разрешения, проверьте перечисленные выше разрешения. Это автоматически обновит файл AndroidManifest.xml.

Подсказка

Обязательно ознакомьтесь с документацией Android по обновлениям местоположения в фоновом режиме, так как необходимо учитывать множество ограничений.

Получить последнее известное местоположение

Устройство могло сохранить в кэше последнюю из известных координат. Используйте метод GetLastKnownLocationAsync(), чтобы получить доступ к кешированному местоположению, если оно доступно. Это часто быстрее, чем выполнять полный запрос местоположения, но может быть менее точным. Если кешированное местоположение отсутствует, этот метод возвращает null.

Примечание.

При необходимости Geolocation API запрашивает у пользователя разрешения.

Пример кода ниже демонстрирует проверку кэшированного местоположения:

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";
}

В зависимости от устройства, некоторые значения местоположения могут быть недоступны. Например, свойство Altitude может быть null, иметь значение 0 или положительное значение, указывающее на метры над уровнем моря. Другие значения, которые могут отсутствовать, включают свойства Speed и Course.

Получение текущего расположения

Несмотря на то что проверка последнего известного местоположения устройства может быть быстрее, она может быть неточной. Используйте метод GetLocationAsync, чтобы запросить у устройства текущее местоположение. Вы можете настроить точность и время ожидания запроса. Лучше использовать перегрузку метода, которая принимает параметры GeolocationRequest и CancellationToken, поскольку получение местоположения устройства может занять некоторое время.

Примечание.

Когда необходимо, API геолокации запрашивает у пользователя разрешение.

Следующий пример кода демонстрирует, как запросить местоположение устройства с возможностью отмены.

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();
}

Не все значения местоположения могут быть доступны в зависимости от устройства. Например, свойство Altitude может быть null, иметь значение 0 или положительное значение, указывающее количество метров над уровнем моря. Другие значения, которые могут отсутствовать, включают Speed и Course.

Предупреждение

GetLocationAsync может возвращать null в некоторых сценариях. Это указывает на то, что базовая платформа не может определить текущее местоположение.

Слушать изменения местоположения

Помимо запроса у устройства текущего местоположения, вы можете отслеживать изменения местоположения, когда приложение работает в активном режиме.

Чтобы проверить, отслеживает ли приложение в данный момент изменения местоположения, можно обратиться к свойству IsListeningForeground. Как только вы будете готовы начинать отслеживать изменения местоположения, вам следует вызвать метод StartListeningForegroundAsync. This method starts listening for location updates and raises the LocationChanged event when the location changes, provided that the app is in the foreground. Объект GeolocationLocationChangedEventArgs, который сопровождает это событие, имеет свойство Location типа Location, представляющее новое обнаруженное местоположение.

Примечание.

When necessary, the Geolocation API prompts the user for permissions.

Пример кода ниже демонстрирует, как отслеживать изменение местоположения и как обрабатывать измененное местоположение.

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
}

Обработка ошибок может быть реализована путем регистрации обработчика для события ListeningFailed. Объект GeolocationListeningFailedEventArgs, который сопровождает это событие, имеет свойство Error, типа GeolocationError, которое указывает, почему прослушивание не удалось. Когда возникает событие ListeningFailed, прослушивание дальнейших изменений местоположения прекращается, и никаких последующих событий LocationChanged не возникает.

Чтобы прекратить отслеживание изменений местоположения, вызовите метод 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
    }
}

Примечание.

Метод StopListeningForeground не имеет эффекта, когда приложение не отслеживает изменения местоположения.

Проверьте, включены ли службы геолокации

Класс Geolocation имеет свойство IsEnabled, доступное только для чтения, которое можно использовать для определения того, включены ли на устройстве службы геолокации.

Точность

Следующие разделы описывают точность определения местоположения для каждой платформы.

Это важно

У iOS есть некоторые ограничения касающиеся точности. Для получения дополнительной информации см. раздел Различия платформ.

Самый низкий

Платформа Distance (in meters)
Андроид 500
iOS 3000
Виндоус 1000 - 5000

Low

Платформа Distance (in meters)
Андроид 500
iOS 1000
Виндоус 300 - 3000

Средний (по умолчанию)

Платформа Distance (in meters)
Андроид 100 - 500
iOS 100
Виндоус 30-500

High

Платформа Distance (in meters)
Андроид 0–100
iOS 10
Виндоус <= 10

Лучший

Платформа Distance (in meters)
Андроид 0–100
iOS ~0
Виндоус <= 10

Обнаружение фиктивных местоположений

Некоторые устройства могут выдавать фальшивое местоположение от провайдера или через приложение, которое предоставляет фальшивые местоположения. Вы можете обнаружить это, используя IsFromMockProvider на любом 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
    }
}

Расстояние между двумя точками

Метод CalculateDistance вычисляет расстояние между двумя географическими местоположениями. Эта рассчитанная дистанция не учитывает дороги или другие маршруты и является просто кратчайшим расстоянием между двумя точками по поверхности Земли. Этот расчет известен как расчет большой круговой дистанции.

Следующий код вычисляет расстояние между городами Бостон и Сан-Франциско в Соединенных Штатах Америки.

Location boston = new Location(42.358056, -71.063611);
Location sanFrancisco = new Location(37.783333, -122.416667);

double miles = Location.CalculateDistance(boston, sanFrancisco, DistanceUnits.Miles);

Конструктор Location(Double, Double, Double) принимает аргументы для широты и долготы соответственно. Положительные значения широты находятся к северу от экватора, а положительные значения долготы — к востоку от Гринвичского меридиана. Используйте последний аргумент для CalculateDistance, чтобы указать мили или километры. Класс UnitConverters также определяет методы KilometersToMiles и MilesToKilometers для преобразования между двумя единицами.

Различия платформ

Этот раздел описывает различия, специфичные для платформ, в API геолокации.

Высота вычисляется по-разному на каждой платформе.

На Android высота, если доступна, возвращается в метрах над опорным эллипсоидом WGS 84. Если у этого местоположения отсутствует высота, возвращается 0.0.

Свойство Location.ReducedAccuracy используется только в iOS и возвращает false на всех других платформах.