摘要:第 28 章. 位置和地图
注意
本书于 2016 年春季出版,之后再未更新。 书中有许多内容仍然有价值,但有些内容已过时,有些主题不再完全正确或完整。
Xamarin.Forms 支持派生自 View
的 Map
元素。 由于使用地图涉及到特殊的平台要求,因此它们在单独的程序集 (Xamarin.Forms.Maps) 中实现,并涉及到不同的命名空间:Xamarin.Forms.Maps
。
地理坐标系统
地理坐标系统确定球形(或将近球形)对象(如地球)上的位置。 坐标由纬度和经度组成,用角度表示。
称为 equator
的大圆位于地球的两极之间,从概念上讲,地轴就是从两极延伸出去的。
纬线和纬度
从地球中心到赤道以北或以南测得的角度标示等纬度线,称为纬线。 范围从赤道的 0 度到南北极的 90 度。 按照惯例,赤道以北的纬度为正值,赤道以南的纬度为负值。
经度和经线
从北极到南极的大圆的一半是等经度线,也被称为经线。 它们相对于英格兰格林威治的本初子午线。 按照惯例,本初子午线以东的经度是 0 度到 180 度之间的正值,而本初子午线以西的经度是 0 度到 –180 度之间的负值。
Equirectangular 投影
地球的任何平面地图都会产生变形。 如果纬度和经度的所有线都是直线,并且纬度和经度的相等差异与地图上的相等距离相对应,结果就是 Equirectangular 投影。 这张地图使靠近两极的区域发生了形变,因为它们是水平延伸的。
墨卡托投影
常见的墨卡托投影同样尝试通过垂直拉伸这些区域来补偿水平拉伸。 这会生成一个地图,其中靠近两极的区域会看起来比实际大得多,但任何局部区域都与实际区域非常接近。
地图服务和磁贴
地图服务使用称为 Web Mercator
的墨卡托投影的变体。 地图服务根据位置和缩放级别将位图磁贴传送到客户端。
获取用户位置
Xamarin.FormsMap
类不包含用于获取用户地理位置的工具,但在使用地图时,这通常是需要的,因此依赖项服务必须处理好这一情况。
注意
Xamarin.Forms 应用程序可以改用 Xamarin.Essentials 中包含的 Geolocation
类。
位置跟踪器 API
Xamarin.FormsBook.Platform 解决方案包含位置跟踪器 API 代码。 GeographicLocation
结构封装纬度和经度。 ILocationTracker
接口定义了用于启动和暂停位置跟踪器的两种方法,并定义了新位置可用时的事件。
iOS 位置管理器
ILocationTracker
的 iOS 实现是使用 iOS CLLocationManager
的 LocationTracker
类。
Android 位置管理器
ILocationTracker
的 Android 实现是使用 Android LocationManager
类的 LocationTracker
类。
UWP 地理位置定位器
ILocationTracker
的通用 Windows 平台实现是使用 UWP Geolocator
的 LocationTracker
类。
显示手机位置
WhereAmI 示例使用位置跟踪器在文本和 Equirectangular 地图上显示手机位置。
必需的开销
WhereAmI 使用位置跟踪器时需要一些开销。 首先,WhereAmI 解决方案中的所有项目都必须引用 Xamarin.FormsBook.Platform 中的相应项目,并且每个 WhereAmI 项目都必须调用 Toolkit.Init
方法。
还需要一些特定于平台的额外开销(以位置权限的形式)。
iOS 位置权限
对于 iOS,info.plist 文件必须包括包含问题文本的项,以请求用户允许获取该用户的位置。
Android 位置权限
获取用户位置的 Android 应用程序必须在 AndroidManifest.xml 文件中具有 ACCESS_FILE_LOCATION 权限。
UWP 位置权限
通用 Windows 平台应用程序必须在 Package.appxmanifest 文件中标记 location
设备功能。
使用 Xamarin.Forms.Maps
使用 Map
类涉及几个要求。
NuGet 包
必须将 Xamarin.Forms.Maps NuGet 库添加到应用程序解决方案。 版本号应与当前安装的 包相同Xamarin.Forms。
初始化 Maps 包
在调用 Xamarin.Forms.Forms.Init
之后,应用程序项目必须调用 Xamarin.FormsMaps.Init
方法。
启用地图服务
由于 Map
可以获取用户的位置,因此应用程序必须按照本章节前面所述的方式获取用户权限:
启用 iOS 地图
使用 Map
的 iOS 应用程序需要 info.plist 文件中的两行。
启用 Android 地图
使用 Google Map 服务需要授权密钥。 此密钥插入到 AndroidManifest.xml 文件中。 此外,AndroidManifest.xml 文件需要使用 manifest
标记来获取用户位置。
启用 UWP 地图
通用 Windows 平台应用程序需要授权密钥才能使用必应地图。 此密钥将作为参数传递给 Xamarin.FormsMaps.Init
方法。 还必须为位置服务启用该应用程序。
未经修饰的地图
MapDemos 示例包含 MapsDemoHomePage.xaml 文件和 MapsDemoHomePage.xaml.cs 代码隐藏文件,该文件允许导航到各种演示程序。
BasicMapPage.xaml 文件展示了如何显示 Map
视图。 默认情况下,它会显示罗马城,但该地图可以由用户操作。
若要禁用水平滚动和垂直滚动,请将 HasScrollEnabled
属性设置为 false
。 若要禁用缩放,请将 HasZoomEnabled
设置为 false
。 这些属性可能不适用于所有平台。
街道和地形
可以通过设置 MapType
类型的 Map
属性 MapType
(包含三个成员的枚举)来显示不同类型的地图:
MapTypesPage.xaml 文件展示了如何使用单选按钮来选择地图类型。 它利用 Xamarin.FormsBook.Toolkit 库中的 RadioButtonManager
类,以及基于 MapTypeRadioButton.xaml 文件的类。
地图坐标
程序可以获取 Map
通过 VisibleRegion
属性显示的当前区域。 可绑定属性不支持此属性,并且没有用于指示其发生更改的通知机制,因此希望监视属性的程序应为此使用计时器。
VisibleRegion
是 MapSpan
类型,一个具有四个只读属性的类:
Position
类型的Center
double
类型的LatitudeDegrees
,指示地图显示区域的高度double
类型的LongitudeDegrees
,指示地图的显示区域的宽度Distance
类型的Radius
,指示在地图上可见的最大圆形区域的大小
Position
和 Distance
都是结构。 Position
定义两个通过 Position
构造函数设置的只读属性:
Distance
旨在通过公制和英制单位之间的转换来提供独立于单位的距离。 Distance
值可以通过以下几种方式创建:
- 距离单位为米的
Distance
构造函数 Distance.FromMeters
静态方法Distance.FromKilometers
静态方法Distance.FromMiles
静态方法
可从以下三个属性获取该值:
double
类型的Meters
double
类型的Kilometers
double
类型的Miles
MapCoordinatesPage.xaml 文件包含若干个用于显示 MapSpan
信息的 Label
元素。 MapCoordinatesPage.xaml.cs 代码隐藏文件在用户操作地图时使用计时器来更新信息。
位置扩展
本书新增了名为 Xamarin.FormsBook.Toolkit.Maps 的库,包含特定于地图但独立于平台的类型。 PositionExtensions
类具有用于 Position
的 ToString
方法,以及用于计算两个 Position
值之间的距离的方法。
设置初始位置
可以调用 Map
的 MoveToRegion
方法,以编程方式设置地图上的位置和缩放级别。 此参数类型为 MapSpan
。 可以使用下列任一方法创建 MapSpan
对象:
- 带有
Position
以及纬度和经度范围的MapSpan
构造函数 - 带有
Position
和半径范围的MapSpan.FromCenterAndRadius
还可以使用 ClampLatitude
或 WithZoom
方法从现有对象创建新的 MapSpan
。
WyomingPage.xaml 文件和 WyomingPage.xaml.cs 代码隐藏文件演示如何使用 MoveToRegion
方法来显示怀俄明州的状态。
也可以将 Map
构造函数 与 MapSpan
对象结合使用来初始化地图的位置。 XamarinHQPage.xaml 文件演示了如何完全在 XAML 中执行此操作,以显示 Xamarin 在旧金山的总部。
动态缩放
可以使用 Slider
来动态缩放地图。 RadiusZoomPage.xaml 文件和 RadiusZoomPage.xaml.cs 代码隐藏文件演示如何根据 Slider
值更改地图半径。
LongitudeZoomPage.xaml 文件和 LongitudeZoomPage.xaml.cs 代码隐藏文件演示了更适用于 Android 的另一种方法,但这两种方法在 Windows 平台上都很有用。
手机位置
Map
的 IsShowingUser
属性在每个平台上的工作方式稍有不同,如 ShowLocationPage.xaml 文件所示:
- 在 iOS 上,蓝点指示手机位置,但你必须手动导航到该位置
- 在 Android 上,当推送提示将地图移至手机位置时,会显示一个图标
- UWP 类似于 iOS,但有时会自动导航到位置
MapDemos 项目尝试通过先定义基于图标的按钮来模拟 Android 方法,此做法基于 MyLocationButton.xaml 文件和 MyLocationButton.xaml.cs 代码隐藏文件。
GoToLocationPage.xaml 文件和 GoToLocationPage.xaml.cs 代码隐藏文件使用此按钮导航到手机位置。
图钉和科学博物馆
最后,Map
类定义 IList<Pin>
类型的 Pins
属性。 Pin
类定义以下四个属性:
string
类型的Label
,一个必需属性string
类型的Address
,一个可选的人工可读地址Position
类型的Position
,指示在地图上显示图钉的位置PinType
类型的Type
,不使用的枚举
MapDemos 项目包含 ScienceMuseums.xml 文件,该文件列出了美国的科学博物馆,Locations
和 Site
类用于反序列化此数据。
ScienceMuseumsPage.xaml 文件和 ScienceMuseumsPage.xaml.cs 代码隐藏文件在地图中显示表示这些科学博物馆的图钉。 当用户点击图钉时,它将显示博物馆的地址和网站。
两点之间的距离
PositionExtensions
类包含 DistanceTo
方法,并简化了两个地理位置之间距离的计算。
在 LocalMuseumsPage.xaml 文件和 LocalMuseumsPage.xaml.cs 代码隐藏文件中使用此类还可以显示从用户位置到博物馆的距离:
该程序还演示了如何根据地图的位置动态限制图钉数。
地理位置编码,然后返回
Xamarin.Forms.Maps 程序集还包含 Geocoder
类,其中 GetPositionsForAddressAsync
方法用于将文本地址转换为零个或多个可能的地理位置,另一个方法 GetAddressesForPositionAsync
用于转换为其他方向。
GeocoderRoundTrip.xaml 文件和 GeocoderRoundTrip.xaml.cs 代码隐藏文件演示了此功能。