Dive Into HTML5:地理位置(续)

选择!我需要选择!

有些流行的移动设备——比如 iPhone 和 Android 手机——支持两种方法来找出你在哪里。第一种方法是三角定位法,凭借的是你的手机和与手机交互的运营商的基站的相对位置。这种方法很快速,不需要 GPS 模块,但只能给你一个大致的未知区域。这取决于你所在的区域有多少基站。所以“一块大致的区域”,可能是仅仅是一个城市街区,也可能有一公里的距离。

第二种方法要使用你的设备上的 GPS 模块。GPS 模块与绕地轨道上的 GPS 定位卫星通讯。GPS 通常可以精确到只有几米的位置。这种方法的不足在于 GPS 模块需要消耗大量电力,所以,手机或者其它终端只在需要的时候才开启 GPS 模块。这意味着,启动模块并且等待模块与 GPS 卫星通讯成功都会有一定的延时。

如果你曾经在 iPhone 或者其他智能终端上使用过 Google Map,你应该注意到这两种方法的实际应用。首先你会看到有一个大大的圆圈,显示出你的大概位置(这是由最近的基站找到的),然后是一个较小的圆圈(与其他基站的三角定位),最后是一个点,指出准确的位置(由 GPS 卫星给出)。

我之所以要提到这个问题,在于你的 web 应用程序是否需要如此高得精度。如果你仅仅为了找到附近电影院的列表,“低精度”就足够了。因为不会有很多电影院,即使在大都市中心也不会,并且你也会希望范围尽可能大一点,而不是很小的一个区域。另一方面,如果你需要实时导航,指引方向,显然你应该知道用户的准确位置,于是你就可以说“20米后右转”之类的提示了。

getCurrentPosition()函数有第三个可选参数,一个PositionOptions对象。这个对象有三个属性,所有属性都是可选的。你可以一个都不设置,也可能全部设置。

PositionOptions 对象属性
属性类型默认值说明
enableHighAccuracyBooleanfalse设置为 true 的话会慢一些
timeoutlong(无)毫秒
maximumAgelong0毫秒

enableHighAccuracy属性正如其名字一样,使用高精度。如果设置为true,并且设备也支持,用户也希望共享他们的准确位置,那么设备就会尝试获取高精度值。iPhones 和 Android 手机对低精度、高精度位置信息都有独立的权限控制,所以,有可能设置了enableHighAccuracytruegetCurrentPosition()会失败,但是设置enableHighAccuracyfalse的却会成功。

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(),这个函数接受一个参数,locloc对象包含经度、纬度和精度信息。(在本例中没有使用精度信息。)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.');
}

Leave a Reply