Xamarin.Essentials:地理位置

地理位置類別會提供 API 來擷取裝置的目前地理位置座標。

開始使用

若要開始使用此 API,請閱讀 入門指南Xamarin.Essentials,以確保連結庫已正確安裝並設定在您的專案中。

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

需要粗略和精確位置的權限,並且必須在 Android 專案中設定。 此外,如果您的應用程式針對 Android 5.0 (API 層級 21) 或更新版,則必須宣告您應用程式使用資訊清單 檔案中的硬體功能。 能以下列方式新增:

開啟 [Properties] 資料夾下的 AssemblyInfo.cs 檔案並新增:

[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 資訊清單:

開啟 [Properties] 資料夾下的 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 專案並開啟專案的屬性。 在 [Android 資訊清單] 下,尋找 [必要權限] 區域並選取 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 權限。 這將會自動更新 AndroidManifest.xml 檔案。

如果您的應用程式是以 Android 10 - Q (API 層級 29 或更高版本)為目標,而且要求 LocationAlways,您也必須將下列許可權新增至 AssemblyInfo.cs

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

或直接進入您的 AndroidManifest.xml

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

建議您閱讀 有關背景位置更新 的 Android 檔,因為需要考慮許多限制。

此 API 會在 Android 上使用執行時間許可權。 請確定 Xamarin.Essentials 已完全初始化,且已在您的應用程式中設定許可權處理。

在 Android 專案的 MainLauncher 或任何 Activity 啟動時 Xamarin.Essentials ,必須在 方法中 OnCreate 初始化:

protected override void OnCreate(Bundle savedInstanceState) 
{
    //...
    base.OnCreate(savedInstanceState);
    Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
    //...
}    

若要處理 Android 上的執行時間權限, Xamarin.Essentials 必須接收任何 OnRequestPermissionsResult。 將下列程式碼新增到所 Activity 類別:

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
    Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

使用地理位置

在類別中新增 的 Xamarin.Essentials 參考:

using Xamarin.Essentials;

地理位置 API 也會在必要時,提示使用者提供權限。

您可以透過呼叫 GetLastKnownLocationAsync 方法取得裝置的最後一個已知位置。 這通常比執行完整查詢更快,但較不精確,而且可能會在沒有快取位置存在時傳回 null

try
{
    var location = await Geolocation.GetLastKnownLocationAsync();

    if (location != null)
    {
        Console.WriteLine($"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
}

若要查詢目前裝置的位置座標,可以使用 GetLocationAsync。 建議您傳入完整的 GeolocationRequestCancellationToken,因為可能需要一些時間才能取得裝置的位置。

CancellationTokenSource cts;

async Task GetCurrentLocation()
{
    try
    {
        var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
        cts = new CancellationTokenSource();
        var location = await Geolocation.GetLocationAsync(request, cts.Token);

        if (location != null)
        {
            Console.WriteLine($"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
    }
}

protected override void OnDisappearing()
{
    if (cts != null && !cts.IsCancellationRequested)
        cts.Cancel();
    base.OnDisappearing();
}

請注意,由於每個裝置如何透過不同的提供者查詢地理位置,因此可能會提供所有值。 例如, Altitude 屬性可能是 null、值為0,或具有正值,其位於海平面上方的公尺。 可能不存在的其他值包括 SpeedCourse

地理位置精確度

下表概述每個平台的精確度:

最低

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

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

中 (預設)

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

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

最佳

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

偵測模擬位置

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

var request = new GeolocationRequest(GeolocationAccuracy.Medium);
var location = await Geolocation.GetLocationAsync(request);

if (location != null)
{
    if(location.IsFromMockProvider)
    {
        // location is from a mock provider
    }
}

兩個位置之間的距離

LocationLocationExtensions 類別會定義 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 建構函式在該順序中具有緯度與經度的引數。 正緯度值位於赤道以北,正經度值位於本初子午線以東。 使用 CalculateDistance 的最後引數來指定英里或公里。 UnitConverters 類別也會定義在兩個單位之間進行轉換的 KilometersToMilesMilesToKilometers 方法。

平台差異

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

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

API

Channel 9YouTube 上尋找更多 Xamarin 影片。