地理位置

Browse sample. 流覽範例

本文說明如何使用 .NET 多平臺應用程式 UI (.NET MAUI) IGeolocation 介面。 此介面提供 API 來擷取裝置目前的地理位置座標。

介面的預設實作 IGeolocation 可透過 Geolocation.Default 屬性取得。 IGeolocation介面和Geolocation類別都包含在 命名空間中Microsoft.Maui.Devices.Sensors

開始使用

若要存取地理位置功能,需要下列平台特定設定:

必須指定粗略 精細的位置許可權,或兩者,而且應該在Android項目中設定。

此外,如果您的應用程式針對 Android 5.0 (API 層級 21) 或更新版,則必須宣告您應用程式使用資訊清單 檔案中的硬體功能。 能以下列方式新增:

  • 新增元件型權限:

    開啟 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(Manifest.Permission.AccessBackgroundLocation)]
    

    - 或 -

  • 更新 Android 指令清單:

    開啟 [平臺/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" />
    

    - 或 -

  • 更新指令清單編輯器中的 Android 指令清單:

    在 Visual Studio 中,按兩下 [平臺/Android/AndroidManifest.xml ] 檔案,以開啟 Android 指令清單編輯器。 然後,在 [必要許可權] 底下,檢查上面所列的許可權。 這將會自動更新 AndroidManifest.xml 檔案。

提示

請務必閱讀有關背景位置更新Android 檔,因為需要考慮許多限制。

取得最後一個已知位置

裝置可能已快取裝置的最新位置。 GetLastKnownLocationAsync()如果有的話,請使用 方法來存取快取的位置。 這通常比執行完整位置查詢更快,但可能不太精確。 如果沒有任何快取位置存在,這個方法會傳 null回 。

注意

必要時,地理位置 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,或具有正值,表示海平面上方的公尺。 可能不存在的其他值包括 SpeedCourse 屬性。

取得目前的位置

雖然檢查 裝置的最後一個已知位置 可能會更快,但可能不正確。 GetLocationAsync使用方法來查詢裝置的目前位置。 您可以設定查詢的精確度和逾時。 最好是使用 GeolocationRequestCancellationToken 參數的方法多載,因為可能需要一些時間才能取得裝置的位置。

注意

必要時,地理位置 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,或具有正值,表示海平面上方的公尺。 可能不存在的其他值包括 SpeedCourse

警告

GetLocationAsync 在某些情況下可以傳回 null 。 這表示基礎平台無法取得目前的位置。

接聽位置變更

除了查詢裝置的目前位置之外,您還可以在應用程式處於前景時接聽位置變更。

若要檢查應用程式目前是否正在接聽位置變更, IsListeningForeground 您可以查詢的屬性。 準備好開始接聽位置變更之後,您應該呼叫 StartListeningForegroundAsync 方法。 這個方法會開始接聽位置更新,並在位置變更時引發 LocationChanged 事件,前提是應用程式位於前景。 伴隨 GeolocationLocationChangedEventArgs 此事件的物件具有 Location 類型 Location為 的屬性,表示偵測到的新位置。

注意

必要時,地理位置 API 會提示使用者提供許可權。

下列程式代碼範例示範如何接聽位置變更,以及如何處理變更的位置:

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 就沒有作用。

準確率

下列各節概述每個平臺的位置精確度距離:

重要

iOS 對於精確度有一些限制。 如需詳細資訊,請參閱 平台差異 一節。

最低

平台 距離 (以公尺為單位)
Android 500
iOS 3000
Windows 1000 - 5000

平台 距離 (以公尺為單位)
Android 500
iOS 1000
Windows 300 - 3000

中 (預設)

平台 距離 (以公尺為單位)
Android 100 - 500
iOS 100
Windows 30-500

平台 距離 (以公尺為單位)
Android 0 - 100
iOS 10
Windows <= 10

最佳

平台 距離 (以公尺為單位)
Android 0 - 100
iOS ~0
Windows <= 10

偵測模擬位置

某些裝置可能會從提供者,或透過可提供模擬位置的應用程式傳回模擬位置。 您可以在任何 Location上使用 來偵測此狀況IsFromMockProvider

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 類別也會定義在兩個單位之間進行轉換的 KilometersToMilesMilesToKilometers 方法。

平台差異

本節說明與地理位置 API 的平臺特定差異。

高度在每個平臺上會以不同的方式計算。

在 Android 上,如果可用,則會以高於 WGS 84 參考橢圓體公尺的公尺傳回高度。 如果這個位置沒有高度, 0.0 則會傳回 。

屬性 Location.ReducedAccuracy 只供 iOS 使用,而且會在所有其他平台上傳 false 回。