选择!我需要选择!
有些流行的移动设备——比如 iPhone 和 Android 手机——支持两种方法来找出你在哪里。第一种方法是三角定位法,凭借的是你的手机和与手机交互的运营商的基站的相对位置。这种方法很快速,不需要 GPS 模块,但只能给你一个大致的未知区域。这取决于你所在的区域有多少基站。所以“一块大致的区域”,可能是仅仅是一个城市街区,也可能有一公里的距离。
第二种方法要使用你的设备上的 GPS 模块。GPS 模块与绕地轨道上的 GPS 定位卫星通讯。GPS 通常可以精确到只有几米的位置。这种方法的不足在于 GPS 模块需要消耗大量电力,所以,手机或者其它终端只在需要的时候才开启 GPS 模块。这意味着,启动模块并且等待模块与 GPS 卫星通讯成功都会有一定的延时。
如果你曾经在 iPhone 或者其他智能终端上使用过 Google Map,你应该注意到这两种方法的实际应用。首先你会看到有一个大大的圆圈,显示出你的大概位置(这是由最近的基站找到的),然后是一个较小的圆圈(与其他基站的三角定位),最后是一个点,指出准确的位置(由 GPS 卫星给出)。
我之所以要提到这个问题,在于你的 web 应用程序是否需要如此高得精度。如果你仅仅为了找到附近电影院的列表,“低精度”就足够了。因为不会有很多电影院,即使在大都市中心也不会,并且你也会希望范围尽可能大一点,而不是很小的一个区域。另一方面,如果你需要实时导航,指引方向,显然你应该知道用户的准确位置,于是你就可以说“20米后右转”之类的提示了。
getCurrentPosition()
函数有第三个可选参数,一个PositionOptions
对象。这个对象有三个属性,所有属性都是可选的。你可以一个都不设置,也可能全部设置。
PositionOptions 对象属性 | |||
属性 | 类型 | 默认值 | 说明 |
enableHighAccuracy | Boolean | false | 设置为 true 的话会慢一些 |
timeout | long | (无) | 毫秒 |
maximumAge | long | 0 | 毫秒 |
enableHighAccuracy
属性正如其名字一样,使用高精度。如果设置为true
,并且设备也支持,用户也希望共享他们的准确位置,那么设备就会尝试获取高精度值。iPhones 和 Android 手机对低精度、高精度位置信息都有独立的权限控制,所以,有可能设置了enableHighAccuracy
为true
的getCurrentPosition()
会失败,但是设置enableHighAccuracy
为false
的却会成功。
timeout
属性是你的应用程序获取位置信息的一个等待时间,以毫秒为单位。这个计时器在用户同意计算位置之后才会开始倒计时。你并不是对用户计时,而是对网络计时。
maximumAge
属性允许设备使用一个缓存的位置马上应答。例如,你第一次调用getCurrentPosition()
获取位置信息的时候,用户同意了,你在上午 10:00 的时候成功获取用户的位置信息。一分钟之后,也就是 10:01 的时候,你将maximumAge
设置为 75000,再一次调用getCurrentPosition()
:
navigator.geolocation.getCurrentPosition( success_callback, error_callback, {maximumAge: 75000} );
此时,你并不太需要用户的当前位置信息,使用一个 75 秒以内的位置就足够了(也就是 75000 毫秒以内任何时刻的位置)。你的设备知道 60 秒之前(60000 毫秒之前)用户的位置,这是刚刚第一次调用getCurrentPosition()
的获得的。于是,你的设备就不重复计算用户的当前位置了,直接返回第一次获得的结果:相同的经纬度,相同精度,并且是相同的时间戳(上午 10:00)。
在你询问用户位置之前,你应该仔细思考下究竟需要怎样的精度,恰当地设置enableHighAccuracy
。如果你需要不止一次的获取用户位置,你应该仔细想想有些以前的信息是不是够用了?此时就要恰当地设置maximumAge
。如果你需要持续不断地获取用户位置信息,getCurrentPosition()
就不适合你,你应该使用watchPosition()
函数。
watchPosition()
同getCurrentPosition()
有几乎一样的签名。它也接受两个回调函数,必选的一个用于成功时获得位置信息,可选的一个则用于错误控制。它也接受一个可选的PositionOptions
对象,能够赋予刚刚我们说过的那些属性值。二者区别在于只要用户位置改变,这个回调函数就会被调用。应用程序没有必要主动拉取其位置。设备自己决定最优的拉取间隔,然后在它认为用户位置发生改变的时候调用你的回调函数。你可以利用这一函数在地图上设置一个标记,提供下一站到哪里,或者其他什么信息。
watchPosition()
函数返回一个数字。你应该将这个数字保存到什么地方。如果你希望停止对用户位置改变的观察,需要调用clearWatch()
方法,并将这个数字作为参数传给它。设备就会停止调用你的回调函数。如果你曾经使用过 JavaScript 的setInterval()
和clearInterval()
函数,就会发现它们是很相像的。
IE 怎么办?
IE9 之前的版本(确切地说是 IE 9.0RC1 之前)并不支持 W3C 的地理位置 API。但是不要失望!Google 提供了一个开源的浏览器插件 Gears 可以解决这个问题。这个插件可以运行于 Windows、Mac、Linux、Windows Mobile 和 Android 上。它可以为老版本浏览器增加新的功能。其中之一就是提供地理位置 API。它的 API 与 W3C 地理位置 API 有些区别,但是目的都是一样的。
现在我们正在处理这些历史遗留问题,我们不得不支持,很多旧的手机浏览器都提供了设备相关的地理位置 API。BlackBerry、Nokia、Palm 和 OMTP BONDI 都提供了自己的 API。当然,这些 API 与 Gears 又不相同,与 W3C 地理位置 API 也不一样。这怎么办呢?
使用 geo.js 解决这个问题
geo.js 是一个使用 MIT 协议开源的 JavaScript 库,用于屏蔽掉 W3C 地理位置 API、Gears API 和 设备相关 API 之间的不同之处。为了使用这个库,我们应该在页面底部添加两个<script>
标签。(从技术上说,这两个标签放在哪里都可以,但是将其放在<head>
里面会减缓页面加载,所以不要这么干!)
第一个脚本是 gears_init.js,用于初始化已经安装了的 Gears。第二个脚本是 geo.js。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Dive Into HTML5</title> </head> <body> ... <script src="gears_init.js"></script> <script src="geo.js"></script> </body> </html>
现在你可以使用任何能够使用的地理位置 API 了。
if (geo_position_js.init()) { geo_position_js.getCurrentPosition(geo_success, geo_error); }
我们一步步来解释这个问题。首先,你需要调用init()
函数。如果函数找到一个支持的地理位置 API,则返回true
。
if (geo_position_js.init()) {
调用init()
函数不会真正找到你的位置。仅仅是验证找到位置的可能性。为实际找出位置信息,你应该调用getCurrentPosition()
函数。
geo_position_js.getCurrentPosition(geo_success, geo_error);
getCurrentPosition()
函数会要求浏览器想你询问是否授权查看和分享你的位置信息。如果地理位置信息由 Gears 给出,将会弹出一个对话框,问你是否信任这个使用了 Gears 的网站。如果浏览器原生支持地理位置 API,对话框就会不同。例如,Firefox 3.5 原生支持地理位置 API。当你尝试使用 Firefox 3.5 获取地理位置时,就会在页面最上方弹出一个信息栏。
getCurrentPosition()
有两个回调函数作为参数。如果getCurrentPosition()
函数成功找到你的位置——也就是你允许浏览器发现你的位置,并且地理位置 API 也成功了——它会调用作为第一个参数的这个函数。例如,在我们的例子中,成功的回调函数就是geo_success
。
geo_position_js.getCurrentPosition(geo_success, geo_error);
成功回调函数有一个参数,保存有位置信息。
function geo_success(p) { alert("Found you at latitude " + p.coords.latitude + ", longitude " + p.coords.longitude); }
如果getCurrentPosition()
函数找不到你的位置——不管是因为你不允许还是因为地理位置 API 调用失败——它都会调用作为第二个参数的这个函数。在本例中则是geo_error
。失败的回调函数没有参数:
function geo_error() { alert("Could not find you!"); }
geo.js 不支持watchPosition()
函数。如果你需要持续不断地获取位置信息,只能自己拉取getCurrentPosition()
的值。
一个完整的实例
下面是一个使用 geo.js 获取你的位置的实例,它会在页面的地图中显示你的实际位置。
它是怎么运作的呢?首先在页面加载的时候,调用了geo_position_js.init()
来查看地理位置 API 是否可用。如果可用,则点击连接,查找你的位置。这个点击的函数是这样的:
function lookup_location() { geo_position_js.getCurrentPosition(show_map, show_map_error); }
如果你同意发现你的位置,浏览器的后台服务就会去查找你的位置。geo.js 会调用第一个回调函数,show_map()
,这个函数接受一个参数,loc
。loc
对象包含经度、纬度和精度信息。(在本例中没有使用精度信息。)show_map()
函数的其余部分则是调用了 Google Maps API 嵌入一个地图。
function show_map(loc) { $("#geo-wrapper").css({'width':'320px','height':'350px'}); var map = new GMap2(document.getElementById("geo-wrapper")); var center = new GLatLng(loc.coords.latitude, loc.coords.longitude); map.setCenter(center, 14); map.addControl(new GSmallMapControl()); map.addControl(new GMapTypeControl()); map.addOverlay(new GMarker(center, {draggable: false, title: "You are here (more or less)"})); }
如果 geo.js 不能找到你的位置,则会调用第二个回调函数show_map_error()
:
function show_map_error() { $("#live-geolocation").html('Unable to determine your location.'); }