add by zhj: 本文得到的经纬度没有转化为高德坐标系的经纬度就直接去查询了,查询结果是有偏差的。在原文的评论中,有些博友也提到了这个问题。
这里有一篇文章:利用HTML5定位功能,实现在百度地图上定位,才是正确的,在手机上测试(用微信内置浏览器打开),无论使用WIFI还是移动4G联网,
定位精度都是蛮高的,误差在几十米内。
要想获取地理位置,分成两步,先获取经纬度,这是html5的新功能,具体实现各个浏览器自己想办法,比如chrome和firefox就是用的google的位置服务,对于firefox,
参见firefox geolocation说明文档。貌似国内还没有哪家公司可以提供这样的服务(待确认)。拿到了经纬度,再调用经纬度转地理位置的接口(html5没有这个标准),
Google/百度/高德/腾讯都有这个服务,不过,html5地理定位得到的经纬度要经过转换,转化成接口服务端的坐标系的坐标再查询。
Geolocation
HTML5 的
geolocation
是一个令人兴奋的 API,通过这套 API,Javascript 代码就能够访问到用户的当前位置。当然,访问之前必须得到用户的明确认可,即同意在页面共享位置。如果页面尝试访问地理位置信息,浏览器就会显示一个对话框,请求用户许可共享其地理位置信息,比如这样:
用户同意(允许)之后,Geolocation 的 api 就能起作用了。
getCurrentPosition() 方法
Geolocation API 在浏览器中的实现是
navigator.geolocation
对象,这个对象包含 3 个方法。第一个方法是 getCurrentPosition(),调用这个方法就会触发请求用户共享地理定位信息的对话框。这个方法接收 3 个参数:成功回调函数,可选的失败回调函数和可选的选项对象。
其中,成功回调函数会接收到一个 Position 对象参数,该对象有两个属性:coords 和 timestamp。而 coords 对象中将包含下列与位置相关的信息。
- latitude:以十进制度数表示的维度
- longtitude:以十进制度数表示的经度
- accuracy:经纬度坐标的精度,以米为单位
有些浏览器可能会在 coords 对象中提供如下属性。
- altitude:以米为单位的海拔高度,如果没有相关数据则值为 null
- altitudeAccuracy:海拔高度的精度,以米为单位,数值越大越不精确
- heading:指南针的方向,0°表示正北,值为 NaN 表示没有检测到数据
- speed:速度,即每秒移动多少米,如果没有相关数据则值为 null
说了这么多,我们来简单应用下,写一段代码获取当前的经纬度,然后输出:
navigator.geolocation.getCurrentPosition(geo_success, geo_error);
function geo_success(position) {
console.log(position.coords.latitude, position.coords.longitude);
}
function geo_error(msg) {
console.log(msg.code, msg.message);
}
代码很简单,如果请求成功了就执行 geo_success() 函数,打印经纬度,如果失败了,输出一些信息(失败回调)。
getCurrentPosition() 的第二个参数,即失败回调函数,在被调用的时候也会接收到一个参数。这个参数是一个对象,包含两个属性:message 和 code。其中,message 属性中保存着给人看的文本消息,解释为什么会出错,而 code 属性中保存着一个数值,表示错误的类型:用户拒绝共享(1),位置无效(2)或者超时(3)。实际开发中,大多数 Web 应用只会讲错误消息保存到日志文件中,而不一定会修改用户界面。
我们在 PC 端的 chrome 浏览器中执行这段代码,结果是令人遗憾的:
掐指一算,估计是被墙了... 事实上,以 Chrome 浏览器为例,如果您允许 Chrome 浏览器与网站共享您的位置,Chrome 浏览器会向 Google 位置服务(此环节被墙)发送本地网络信息,估计您所在的位置。然后,浏览器会与请求使用您位置的网站共享您的位置。
接着在 Android 机上测试了下,没被墙,毕竟谷歌是 Android 的亲爹啊。打印出来的信息如下:
31.188199 121.632919
当然只是知道经纬度或许不太那么直观,如果能把位置显示在地图上那就直观多了!这里我用了高德地图的API(猛戳这里看效果):
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<title>地图显示</title>
<link rel="stylesheet" href="http://cache.amap.com/lbs/static/main.css?v=1.0" />
<script src="http://webapi.amap.com/maps?v=1.3&key=您申请的key值"></script>
</head>
<body>
<div id="mapContainer"></div>
<script>
navigator.geolocation.getCurrentPosition(geo_success, geo_error);
function geo_success(position) {
var map = new AMap.Map(‘mapContainer‘, {
// 设置中心点
center: [position.coords.longitude, position.coords.latitude],
// 设置缩放级别
zoom: 13
});
var marker = new AMap.Marker({
//复杂图标
icon: new AMap.Icon({
//图标大小
size: new AMap.Size(28, 37),
//大图地址
image: "http://webapi.amap.com/images/custom_a_j.png",
imageOffset: new AMap.Pixel(-28, 0)
}),
//在地图上添加点
position: [position.coords.longitude, position.coords.latitude]
});
marker.setMap(map);
}
function geo_error(msg) {
console.log(msg.code, msg.message);
}
</script>
</body>
</html>
在手机上打开后:
我的天哪!实在是太准了!如果我把代码里的 zoom 参数再加大点,能精确到小区了...当然,这并不奇怪,因为这本来就是高德地图百度地图定位的一部分嘛。
事实上,getCurrentPosition() 还有第三个参数,该参数是一个选项对象,用于设定信息的类型。可以设置的选项有三个:enableHighAccuracy 是一个布尔值,表示必须尽可能使用最精确的位置信息;timeout 是以毫秒数表示的等待位置信息的最长时间;maximumAge 表示上一次取得的坐标信息的有效时间,以毫秒表示,如果时间到则重新取得新坐标信息。
除非确实需要非常精确的信息,否则建议保持 enableHighAccuracy 的 false 值(默认值)。将这个选项设置为 true 需要更长的时候,而且在移动设备上更耗电。类似的,如果不需要频繁更新用户的位置信息,那么可以将 maximumAge 设置为 Infinity,从而始终都使用上一次的坐标信息。
navigator.geolocation.getCurrentPosition(locationSuccess, locationError, {
// 指示浏览器获取高精度的位置,默认为false
enableHighAcuracy: true,
// 指定获取地理位置的超时时间,默认不限时,单位为毫秒
timeout: 5000,
// 最长有效期,在重复获取地理位置时,此参数指定多久再次获取位置。
maximumAge: 3000
});
watchPosition() 方法
如果要跟踪用户的位置,那么可以使用 watchPosition() 方法。这个方法的使用和 getCurrentPosition() 完全相同。实际上 watchPosition() 与定时调用 getCurrentPosition() 能得到相同效果。在第一次调用 watchPosition() 方法后,会取得当前位置,执行成功回调或者错误回调。然后,watchPosition() 就地等待系统发出位置已改变的信号。
调用 watchPosition() 会返回一个数值标识符,用于跟踪监控的操作。基于这个返回值可以取消监控操作,只要将其传递给 clearWatch() 方法即可(与使用 setTimeout() 和 clearTimeout() 类似),例如:
var watchId = navigator.geolocation.watchPosition(geo_success, geo_error);
clearWatch(watchId);
Geolocation 定位原理
作者:Jesion Wang
链接:http://www.zhihu.com/question/20473051/answer/24069580
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Geolocation API 的数据来源可能是 GPS、IP 地址、RFID、WiFi、蓝牙 MAC 地址、GSM/CDMA 卡 ID 等。因为 Geolocation API 是运行在你本地设备上的。所以,在使用 VPN 或代理的情况下,Geo API 仍能获得你准确的 IP 地址信息(除非因为某些因素浏览器获取不到这些信息)。
下面是 W3C 文档的描述:
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定位,而笔记本和部分平板,最准的定位是WIFI,至于网线上网的台式机,一般就只能使用IP来定位了,这个准确度最低。
在这些方法里,GPS定位最好理解,卫星直接给出定位数据。而WIFI和IP地址定位,都不是浏览器本身能够实现的。这两种方式都必须将IP地址或WIFI信号收集到的周围路由信息,上传到某个服务器,由服务器的查询计算位置信息,然后返回给浏览器。那么这些查询服务由谁来提供呢?
首先来看chrome,很明显,肯定是google自己提供的服务。通过chrome自带的抓包方法(chrome://net-internals/)可以看到,在使用geolocation时,chrome向www.googleapis.com/geolocation/v1/geolocate的接口发送了请求,由于请求用spdy加密过,所以看不出具体内容,只有一点可以确定,即wifi上网时,是post方式传数据,而使用网线时,使用的是get。
firefox使用的也是google的服务,但是和chrome用的接口不同,这个是https://maps.googleapis.com/maps/api/browserlocation/json
请求数据是:browser=firefox&sensor=true&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-43&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-43&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-43&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-44&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-60&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-61&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-62&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-63&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-63&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-67&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-67&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-67&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-67&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-74&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-82&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-84&wifi=mac:xx-xx-xx-xx-xx-xx%7Cssid:xxxxxxx%7Css:-85
这个就很清晰了,是周边wifi设备的ssid、mac地址,以及信号强度。(公司网络,路由名和mac地址隐去。)
opera用的也是google的服务,接口url没弄到,不过从ip地址来看,也是google的ip。
对于不支持geolocation的浏览器,本来可以调用google的gears项目提供的接口来查询地理位置,但是该服务目前2011年已经停止,暂时也没有出现更好的替代方案。
参考资料
下面的参考资料非常有价值,
HTML5 中 Geolocation 获取地理位置的原理是什么?
使用 HTML5 Geolocation 构建基于地理位置的 Web 应用
How HTML5 Geolocation Feature Works?