Xamarin.Essentials:地理位置

Geolocation 提供 API 以检索设备的当前地理位置坐标。

入门

若要开始使用此 API,请阅读 Xamarin.Essentials 的入门指南,确保在项目中正确安装和设置库。

若要访问 Geolocation 功能,需要以下特定于平台的设置:

需要具有 Coarse 和 Fine Location 权限,并且必须在 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_LOCATION”和“ACCESS_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 中,必须在 OnCreate 方法中初始化 Xamarin.Essentials:

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

使用 Geolocation

在类中添加对 Xamarin.Essentials 的引用:

using Xamarin.Essentials;

Geolocation 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

检测模拟位置

一些设备可能会从提供程序或通过提供模拟位置的应用程序返回模拟位置。 可以通过使用位于任何 LocationIsFromMockProvider 进行检测。

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

第 9 频道YouTube 上查找更多 Xamarin 视频。