前端开发中的地理定位问题小总结

前端开发中的地理定位问题小总结

项目中遇到地理定位的问题,没遇过不知道,遇过就发现这是个比较坑的问题。这个问题其实困扰了我挺久的,还不如一次性了解清楚。所以本文进行了一些小总结,先简单总结我看过的关于定位技术的资料,然后介绍前端开发中的可选定位方案,以及存在的一些问题。

定位技术

概念上很好理解,就是定位你在哪里的技术。常见的定位方式有基站定位,WiFi定位,IP定位,GPS定位等。

基站定位

基站是能进行信号交换的站点。手机能接受信号,打电话就是通过手机基站。这些基站是由国家移动通信运营商建的,比如中国移动,中国联通,中国电信。原理如下,通过手机接受不同几个基站的信号强度来判断二者之间距离,当然基站的位置信息本来是已知的,所以手机的位置就知道了。前提是手机必须处于SIM卡注册状态下。由于手机信号会受干扰,基站定位精度较低。而且精度也受基站的密度影响,密度越大越精准。

WiFi定位

WiFi是无线上网的一种技术。平时手机不连上WiFi的功能就能定位。大致的原理是,WiFi信号被设备检测到,数据库记录这个WiFi信号和设备对应的位置。当它被越来越多的设备检测到,它的位置就可以利用这些数据通过某种算法来得出。由于信号随着距离的增加而减少,根据设备获取到的WiFi信号强度就可以计算出两者之间的距离。知道了周围几个点,以及与这些点之间的距离,待定位的设备位置就不难计算出来了。WiFi定位是由谷歌提出的,主要解决了室内定位的问题。缺点是,当某个WiFi搬家的时候,数据库没有及时更新,就会出现定位不准的问题。

身边wifi坐标获取
根据周围wifi信息计算设备

IP定位

每个能联网的设备都被分配了一个ip,通过查到数据库,可以粗略地知道这个ip所在的地理位置。你可以点这里试试IP定位。

GPS定位

GPS,全称Global Position System,全球定位系统,简单的说,就是天上有很多卫星(24颗),通过4颗卫星的位置以及卫星与待定位设备之间的距离,计算出该设备的位置。GPS精度高,但费电。还有在室内的时候,讯号就会被阻挡 ,所以室内GPS定位不准。

AGPS定位

AGPS是Assisted GPS的意思。由于GPS定位最初使用都有一个冷启动时间(2-3分钟),在此之前先借助其他的定位方式进行粗略地定位,然后可以较快地根据GPS进行精确定位。一般借用的辅助定位方式为基站定位。

定位技术比较

以上的定位方式各有优缺点,实际开发中一般同时采用多个定位方式进行定位。

开发实战

一般情况下,我们肯定不会自己去实现这些定位技术的算法,而是借助于第三方的SDK。

h5开发中的方案大体来说有以下几种:

  • 微信JSSDK定位
  • HTML5定位
  • 高德JS API定位

这些定位基本上都是需要获取用户的授权。

微信JSSDK定位

利用微信的JSSDK来定位,简单代码如下。具体可以参考开发手册

wx.config({
     debug: false,
     appId: json.appid,
     timestamp: json.timestamp,
     nonceStr: json.nonceStr,
     signature: json.signature,
     jsApiList: [
         'getLocation'
     ]
 });
 wx.error(function (res) {
     console.log(res.errMsg); 
     options.callback();
 });
 wx.ready(function () { 
     wx.getLocation({
        type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
        success: function (res) {
            var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
            var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。 
            console.log('微信定位成功;'); 
        },
        fail: function(){
            console.log('微信定位失败;'); 
        }
    });
 }); 

在网上没有找到微信定位的原理,估计就是混合定位的方式。微信定位比较好用,只要你授权了定位,成功率比较高。缺点是,只能有微信客户端才能调用微信JSSDK。

但我发现在微信web开发者工具中,定位不准确。原因不详。

HTML5定位

定位是HTML5新增的特性。官网上对它使用的定位技术进行了描述:

The Geolocation API defines a high-level interface to location information associated only with the device hosting the implementation, such as latitude and longitude. The API itself is agnostic of the underlying location information sources. Common sources of location information include Global Positioning System (GPS) and location inferred from network signals such as IP address, RFID, WiFi and Bluetooth MAC addresses, and GSM/CDMA cell IDs, as well as user input. No guarantee is given that the API returns the device’s actual location.

简而言之,HTML5定位是优先采用GPS,失败就用网络信号,比如IP地址,WiFi,蓝牙等等。而且它明确说明,不能保证返回结果的正确性。

简单例子如下,具体的API可参考这篇博客

if (navigator.geolocation) {
    console.log("h5 定位中");
    navigator.geolocation.getCurrentPosition(function(position) {
        var latitude  = position.coords.latitude;
        var longitude = position.coords.longitude;
        console.log('h5定位成功;'); 
    }, function(error) {
        console.log('h5定失败;'); 
    })
}

Geolocation API规范提供了一套保护用户隐私的机制,必须先得到用户明确许可,才能获取用户的位置信息。

看起来这是个很不错的方式,现代浏览器都已经支持HTML5了。不幸的是,Chrome浏览器从50版本开始,http协议的网址是不能用了。控制台会有这样的提示:

getCurrentPosition() and watchPosition() no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.

高德JS API定位

以下是高德官网的说明。你可以点击这里来测试高德的定位功能。

AMap.Geolocation定位服务插件。融合了浏览器定位、高精度IP定位、安卓定位sdk辅助定位等多种手段,提供了获取当前准确位置、获取当前城市信息、持续定位(浏览器定位)等功能。

由于Chrome、IOS10等已不再支持非安全域的浏览器定位请求,为保证定位成功率和精度,请尽快升级您的站点到HTTPS。

高德用Geolocation.getCurrentPosition获取精确位置的流程如下:

在PC端,因为原生接口成功率很低,JS-API会优先调用精确IP定位服务,在IP定位失败的时候,尝试使用浏览器原生定位接口进行定位,如果原生定位接口也定位失败,则返回error事件或回调error信息。定位成功之后我们会对浏览器定位的经纬度结果进行向高德坐标的转化,并对所有有效定位结果融合地址信息后返回complete事件或者回调complete信息。

在移动端,如果开发者开启了sdk辅助定位,那么安卓手机上我们会优先尝试调用sdk的定位接口,失败之后优先调用浏览器原生定位接口进行定位,浏览器定位失败之后尝试进行精确IP定位,如果以上三种定位全部尝试失败则返回error事件或回调error信息,否则和PC端的一样,定位成功之后进行高德坐标转化和地址融合。

我们在定位的回调或者事件响应中返回了message字段,message字段明确指出了每一步的成功和失败原因。

以及高德对 getCurrentPosition定位失败的原因说明:

  1. IP精确定位失败,message包含‘Get ipLocation failed.’信息,精确IP定位服务无法完全覆盖所有IP和用户,故而失败;

  2. sdk定位失败,请检查sdk的key是否设置好,以及webview的定位权限及应用和系统的定位权限是否开启。

  3. 浏览器定位失败,有多种情况:

    1. 第一种情况,浏览器不支持原生定位接口,如IE较低版本的浏览器等,message字段包含‘Browser not Support html5 geolocation.’信息;
    2. 第二种情况,用户禁用了定位权限,需要用户开启定位权限,message字段包含‘Geolocation permission denied.’
    3. 第三种情况,浏览器禁止了非安全域的定位请求,比如Chrome、IOS10已陆续禁止,这时候需要升级站点到HTTPS,message字段也是包含‘Geolocation permission denied.’信息。注意Chrome不会禁止localhost等域名HTTP协议下的定位;
    4. 第四种情况,浏览器定位超时,包括原生的超时,可以适当增加超时属性的设定值以减少这一现象,另外还有个别浏览器本身的定位接口就是黑洞,完全没有回应,也会超时返回失败,message字段包含‘Get geolocation time out.’信息;
    5. 第五种情况,确实定位失败,Chrome、火狐以及部分套壳浏览器接入的定位服务在国外,有较大限制,失败率高;

具体的代码例子如下:

AMap.service('AMap.Geolocation', function() {
    geolocation = new AMap.Geolocation({
        enableHighAccuracy: true, //是否使用高精度定位,默认:true
        timeout: 10000, //超过10秒后停止定位,默认:无穷大
        zoomToAccuracy: true, //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
        buttonPosition: 'LB', //显示定位按钮的位置 ,右下角 
    }); 
    geolocation.getCurrentPosition();
    AMap.event.addListener(geolocation, 'complete', onComplete); //返回定位信息
    AMap.event.addListener(geolocation, 'error', onError); //返回定位出错信息
})

我做了一点小测试,结果如下:

A处:我的实际地址
B处: 我的IP地址

+ PC端
    + Chrome:B处
    + IE:B处
    + Safari:B处
+ 手机端(安卓)
    + 数据
        + 自带浏览器:A处偏一点
        + 微信浏览器:A处
        + Chrome:合肥市
        + Safari:准确
    + WiFi
        + 自带浏览器:A处
        + 微信浏览器:A处
        + Chrome:杭州市
        + Safari:准确

结果分析:PC端定位都是IP,所以是精确IP定位;在手机端,由于自带浏览器和微信浏览器都支持HTML5定位(即浏览器定位,访问前也有授权操作),所以都返回了同样的结果;对于Chrome,浏览器定位不管用了,WiFi的IP定位定准确了城市,而3G下的IP定位定到了手机号码归属地。

那么,问题是对于这些定位不成功的例子怎么来解决?

我的方案

最初,我只用高德地图的定位来做。但是时好时坏,我瞎折腾,又添加浏览器定位。然后想想这个在附在微信公众号上的,于是又添加了微信JSSDK的方式。

所以,这个项目使用的定位方案是这样的:同时调用微信定位,浏览器定位,高德定位,以最快出结果的为准。想想三重保障是不是很完美了?

现实很残酷,问题还很多,不然就不会有这篇文章了。

本来我还缓存了一定时间的城市和位置信息,但考虑到出错的情况,还是取消缓存比较恰当。

分析下来,其实浏览器定位的方式已经包括在高德定位了。由于微信定位的高成功率,方案可以调整为:判断是否为微信客户端,是的话优先调用微信定位,若微信定位失败再调用高德;否的话,直接调用高德定位

存在的问题

由于有些测试需要在项目上线后才好测试,我只是做一些分析猜测。据我所了解,目前的问题主要考虑的是高德定位失败后怎么办?

而高德失败的主要原因也就是浏览器定位失败,条件允许的话把网站(只需要那个页面)升级到HTTPS就好了。

那网址即使已经HTTPS协议的了呢?我们换个角度来思考一下。

从产品的角度出发,无论怎么样还是要考虑一定不成功的情况,那就提供手动输入地址的功能吧。这就万无一失了。

参考资料:

编辑于 2017-03-24