原文地址: http://www.jianshu.com/p/f8224779ca63
开发App时会遇到各种坑,本文分享我们在iOS/Android系统中定位和地图中遇到的坑,以及携程App的解决方案。
定位即获取用户当前经纬度,iOS平台都是通过系统SDK接口获取的,因此所有App获取定位及精度的能力是相同的,即使Google Maps、百度地图、高德地图这种专业地图App也是如此;Android平台由于Google Service被阉割,国内App通常是通过高德、百度等第三方SDK接口获取定位信息,因此在定位能力和精度上会有些差异。
App定位遇到的第一个坑是坐标系问题。目前常见的坐标系有三种:地球坐标(WGS84,国际公认坐标),火星坐标(GCJ02,国家标准,适用于高德百度地图大陆+港澳部分、Google地图大陆部分),百度坐标(BD09,适用于百度地图大陆+港澳台部分)。坐标系需要和地图关连才有意义,只有正确匹配地图坐标系的坐标才能在该地图上完美标识位置,否则就会存在偏移。另外对于旅行类App而言,经常需要根据用户当前位置查询周围酒店或者其他POI信息,并且按距离排序,如果坐标系不匹配,就会由于坐标系偏移产生排序问题。
iOS系统上通过定位服务CLLocation相关接口获取定位信息时,获取的经纬度坐标系是WGS84地球坐标,如果直接将该坐标系在iOS系统地图中打点,会发现存在偏移,因为iOS系统地图查看国内时使用的是高德地图数据(这里有另一个坑,详见下文),因此只接受GCJ02火星坐标。如果使用高德或者百度iOS定位SDK中的接口,是可以直接获得火星偏移后的坐标的,由于App Size问题,携程App没有集成第三方SDK,而是通过近似偏移算法直接做偏移(自行Google『transform From WGS To GCJ』)。然而如果在iOS系统地图中获取当前位置,同时在国内,那么获取到的坐标系直接是GCJ02火星坐标系,这点需要小心。
Android系统上通常使用高德或者百度定位SDK获取定位信息。高德SDK没有坐标系参数设定,在大陆和港澳地区获取的坐标系即为GCJ02坐标系,在台湾和海外地区都是WGS84坐标系;百度SDK可以自行设定坐标系参数,即返回WGS84坐标系,还是GCJ02坐标系或者BD09坐标系(注意BD09坐标系只适用于百度地图),如果设定的是GCJ02坐标系,它在大陆+港澳台地区获取的坐标系都是GCJ02坐标系。
海外地图(非大陆和非港澳台地区)是没有火星坐标或者百度坐标之说,都是标准的WGS84地球坐标系。
第二个常见的坑是定位精度问题,经常有用户或者Boss反馈,为什么两台一样的手机,获取的当前位置不一样?我明明在这个位置,为什么定位却显示在附件另一个位置,相差那么远?
这类问题的根源是手机不同定位方式导致的,通常手机定位方式有三种:
1. GPS:根据系统GPS模块获取经纬度,精度10-100米左右,限制是容易受环境影响,在室内几乎不起作用。
2. 基站:根据运营商基站位置计算经纬度,精度1000-3000米左右,限制是定位较慢,精度差。
3. WIFI:根据周围WIFI路由器位置计算经纬度,精度100-200米左右,限制是受周围WIFI数量和分布影响,需要打开手机WIFI开关。
如果用户没有打开WIFI开关,定位的精度会受到极大的影响,下图是同一部iPhone手机在相同位置,百度地图在打开或者关闭WIFI场景下的效果对比,直观反映出是否打开WIFI开关对于定位精度的影响。携程iOS App的用户统计数据显示37.1%在非WIFI情况下定位精度超过了1000米,因而用户会感觉偏移很厉害。
一定会有同学疑问,开车导航时周围通常没有WIFI,为什么地图软件可以精确的把用户当前位置显示在道路上。那是因为在导航模式下,用户的运动速度和方向信息是可以获取的,导航算法会将用户位置重新计算后定位在道路上,如果把车停在路边,关闭WIFI,一样有可能偏移很严重。
App地图同样存在坐标系和精度问题。前面说过在地图里显示坐标点时,需要匹配正确的坐标系,否则就会产生偏移。
iOS平台的系统地图就存在数据源地图坐标系导致的一个大坑,iOS系统地图App以及系统地图组件MKMapView的数据源分为两种:高德数据源和TomTom数据源。最坑的是iOS地图使用的数据源和当前手机所处的位置是相关的:
如果手机在大陆地区,iOS地图使用的是高德地图数据源:此时用户查看大陆和港澳台地图细节信息时,使用的是GCJ02火星坐标系,同时地图显示精度很高,但是看海外地图时,由于数据源问题精度就会很差(坐标系是WGS84),例如下图看新加坡,如果你要在地图中显示POI打点,效果可想而知。
如果手机在港澳台和海外地区,iOS地图使用的是TomTom地图数据源,该地图匹配的是WGS84地球坐标系,如果用户在海外查看国内地图,同时你的POI打点坐标系是GCJ02火星坐标系,那么就会产生偏差;另外手机在港澳台和海外地区时查看港澳台和海外的地图数据精度很好,然而查看国内地图便会精度偏低。
因此要想在iOS地图中正确打点,就需要先行判断使用的地图数据源,以及对应的坐标系,同时使用的POI坐标系务必与地图坐标系匹配,否则就会出现偏差。下图是旅行类App显示香港一家酒店的POI打点,可以看出除了携程外其他App都有偏差,其中三家偏差的方式还是一样的,原因应该是他们服务端仅保存了这家酒店的WGS84地球坐标系坐标。
Android平台我们使用的是百度地图(高德地图情况相同),然而百度地图在大陆+港澳台地区的数据源精度是正常的,海外地区的精度就和iOS平台下高德数据源在海外的精度类似,不够详尽(百度在日韩地区的地图精度已经变好,数据源的精度提升确实需要一个过程)。我们的解决办法是开发WebView版的Google地图组件,载入ditu.google.cn,见下图效果。
前面提到的iOS系统地图精度问题我们也是用相同方法解决的,只是显示时处理逻辑更复杂,人在国内看国外地图,人在国外看国内地图两种情况就会使用自定义Google地图替代,其他情况下继续使用系统地图。
WebView版地图虽然操作体验比Native地图差一些,但是显示数据源精度高很多,对用户价值更大。