使用 HTML5 Geolocation 构建基于地理位置的 Web

本文将介绍 HTML5 提供的一个全新功能:Geolocation,它允许用户在 Web 应用程序中共享他们的位置,使其能够享受位置感知服务。首先本文将介绍 HTML5 Geolocation 位置信息的构成:纬度、经度,以及获得这些数据的途径(GPS、Wi-Fi 和蜂窝站点)。然后将讨论 HTML5 地理定位数据的隐私问题,以及浏览器如何使用这些数据。最后本文将探讨它在实际中的应用,演示并让您学会如何使用它构建一个实用的基于地理位置的 Web 应用。

HTML5 中的新功能

HTML5 是最新一代的 HTML 规范,是 W3C 与 WHATWG 合作的结果,目前仍外于开发中。自从上一代 HTML4,Web 世界已经发生了巨大的变化,HTML5 的到来更大地促进了 Web 的发展,HTML5 提供了很多新的功能,主要有:

  • 新的 HTML 元素,例如 section, nav, header, footer, article 等
  • 用于绘画的 Canvas 元素
  • 用于多媒体播放的 video 和 audio 元素
  • 用于定位的 Geolocation API
  • 本地存储以及离线应用
  • Web Workers API

本文主要对 HTML5 用于定位的 Geolocation 功能进行讲解,并提供实例,介绍它在实际中的应用。

位置信息

在 HTML5 中,当请求一个位置信息时,如果用户同意,浏览器就会返回位置信息,该位置信息是通过支持地理定位功能的底层设备(比如笔记本电脑或手机)提供给浏览器 的。位置信息由纬度、经度坐标和一些其他元数据组成。例如北京故宫的位置信息主要由一对纬度和经度坐标组成:纬度:北纬 39.9,经度:东经 116.4。

经纬度坐标有两种表示方式:十进制格式(例如 39.9)和 DMS(Degree Minute Second,角度)格式(例如 39 ° 54 ′ 20 ″)。HTML5 Geolocation API 返回的坐标格式为十进制格式。除了纬度和经度坐标,HTML5 Geolocation 还提供位置坐标的准确度。除此之外,它还会提供其他一些元数据,比如海拔、海拔准确度、行驶方向和速度等,具体情况取决于浏览器所在的硬件设备。

位置信息一般从如下数据源获得:

  • IP 地址
  • 三维坐标
    • GPS(Global Positioning System,全球定位系统)
    • Wi-Fi
    • 手机信号
  • 用户自定义数据

它们各有优缺点如表 1 所示,为了保证更高的准确度,许多设备使用多个数据源组合的方式。

表 1. 位置信息获取方式对比
数据源 优点 缺点
IP 地址 任何地方都可用
在服务器端处理
不精确(经常出错,一般精确到城市级)
运算代价大
GPS 很精确 定位时间长,耗电量大
室内效果差
需要额外硬件设备支持
Wi-Fi 精确
可在室内使用
简单、快捷
在乡村这些 Wi-Fi 接入点少的地区无法使用
手机信号 相当准确
可在室内使用
简单、快捷
需要能够访问手机或其 modem 设备
用户自定义 可获得比程序定位服务更准确的位置数据
用户自行输入可能比自动检测更快
可能很不准确,特别是当用户位置变更后

 

浏览器支持情况

各 浏览器对 HTML5 Geolocation 的支持程度不同,并且还在不断更新中。好消息是:在 HTML5 的所有功能中,HTML5 Geolocation 是第一批被全部接受和实现的功能之一,相关规范已经达到一个非常成熟的阶段,不大可能做太大改变。如表 2 所示,很多浏览器已经支持 HTML5 Geolocation:

表 2. 浏览器对 HTML5 Geolocation 的支持情况
浏览器 支持情况
Firefox 3.5 及以上版本支持
Chrome 在带有 Gears 的第 2 版 Chrome 中被支持
Internet Explorer 通过 Gears 插件支持
Opera 在版本 10 中支持
Safari 在版本 4 中支持以实现在 iPhone 上可用

由于浏览器对它的支持程度不同,在使用之前最好先检查浏览器是否支持 HTML5 Geolocation API。后面将讲解如何检查浏览器是否支持此功能。本文中所有示例程序都在 Firefox 12.0 上运行测试成功。

 

隐私

HTML5 Geolocation 规范提供了一套保护用户隐私的机制。必须先得到用户明确许可,才能获取用户的位置信息。不过,从可接触到的 HTML5 Geolocation 应用程序示例中可以看到,通常会鼓励用户共享这些信息。例如,午餐时间到了,如果应用程序可以让用户知道附近餐馆的特色菜及其价格和评论,那么用户就会觉 得共享他们的位置信息是可以接受的。

访问使用 HTML5 Geolocation API 的页面时,会触发隐私保护机制。图 1 显示了在 Firefox 12.0 中触发隐私保护机制的页面。

图 1. HTML5 Geolocation 在 Firefox 12.0 中触发隐私保护机制

因为位置数据属于敏感信息,所以接收到之后,必须小心地处理、存储和重传。如果用户没有授权存储这些数据,那么应用程序应该在相应任务完成后立即删除它。如果要重传位置数据,建议对其进行加密。

 

HTML5 Geolocation API

本节将详细介绍 HTML5 Geolocation API 的使用方法。

浏览器支持性检查

在调用 HTML5 Geolocation API 函数前,需要确保浏览器支持此功能。当浏览器不支持时,可以提供一些替代文本,以提示用户升级浏览器或安装插件(如 Gears)来增强现有浏览器功能。清单 1 是浏览器支持性检查的一种途径。

清单 1. 检查浏览器支持性
 function testSupport() { 
    if (navigator.geolocation) { 
        document.getElementById(“support”).innerHTML = "支持 HTML5 Geolocation。"; 
    } else { 
    document.getElementById(“support”).innerHTML = 
    "该浏览器不支持 HTML5 Geolocation !建议升级浏览器或安装插件(如 Gears)。"; 
    } 
 }

 

在清单 1 中,testSupport() 函数检测了浏览器的支持情况。这个函数应该在页面加载的时候就被调用,如果浏览器支持 HTML5 Geolocation,navigator.geolocation 调用将返回该对象,否则将触发错误。预先定义的 support 元素会根据检测结果显示浏览器支持情况的提示信息。

位置请求

在 HTML5 Geolocation 功能中,位置请求有两种:

  • 单次定位请求
  • 重复性位置更新请求

单次位置请求

在许多应用中,只检索或请求一次用户位置即可。例如前面提到的,午餐时间到了,要查询用户附近餐馆的特色菜及其价格和评论,就可以使用清单 2 所示的 HTML5 Geolocation API。

清单 2. 单词定位请求 API
void getCurrentPosition(updateLocation, optional handleLocationError, optional options);

 

这个函数接受一个必选参数和两个可选参数。

  • 必选参数 updateLocation 为浏览器指明位置数据可用时应调用的函数。获取位置操作可能需要较长时间才能完成,用户不希望在检索位置时浏览器被锁定,这个参数就是异步收到实际位置信 息后,进行数据处理的地方。它同时作为一个函数,只接受一个参数:位置对象 position。这个对象包含坐标(coords)和一个获取位置数据时的时间戳,许多重要的位置数据都包含在 coords 中,比如:
    • latitude(纬度)
    • longitude(经度)
    • accuracy(准确度)

    毫无疑问,这三个数据是最重要的位置数据。latitude 和 longitude 包含 HTML5 Geolocation 服务测定的十进制用户位置。accuracy 以 m 为单位制定纬度和经度值与实际位置间的差距。局限于 HTML5 Geolocation 的实现方式,位置只能是粗略的近似值。在呈现返回值前请一定要检查返回值的准确度。如果推荐的所谓“附近的”餐馆,实际上要耗费用户几个小时的路程,那就 不好了。

    坐标可能还包含其他一些数据,不能保证浏览器对其都支持,如果不支持则返回 null:

    • altitude – 海拔高度,以 m 为单位;
    • altitudeAccuracy – 海拔高度的准确度,以 m 为单位;
    • heading – 行进方向,相对于正北而言;
    • speed – 速度,以 m/s 为单位。

    清单 3 给出了 updateLocation() 函数的常用实现代码,该函数根据坐标信息执行具体的更新操作:用获得的位置信息分别更新 HTML 页面上三个空间元素的文本。

    清单 3. updateLocation() 函数使用示例
     function updateLocation(position) { 
        var latitude = position.coords.latitude; 
        var longitude = position.coords.longitude; 
        var accuracy = position.coords.accuracy; 
    
        document.getElementById(“纬度”).innerHTML = latitude; 
        document.getElementById(“经度”).innerHTML = longitude; 
        document.getElementById(“准确度”).innerHTML = accuracy + “米”; 
     }

     

  • 可选参数 handleLocationError 为浏览器指明出错处理函数。位置信息请求可能因为一些不可控因素失败,这时,您需要在这个函数中提供对用户的解释。幸运的是,该 API 已经定义了所有需要处理的错误情况的错误编号。错误编号 code 设置在错误对象中,错误对象作为 error 参数传递给错误处理程序。这些错误编号有:
    • UNKNOWN_ERROR (0):不包括在其它错误编号中的错误,需要通过 message 参数查找错误的详细信息。
    • PERMISSION_DENIED (1):用户拒绝浏览器获得其位置信息。
    • POSITION_UNVAILABLE (2):尝试获取用户信息失败。
    • TIMEOUT (3):在 options 对象中设置了 timeout 值,尝试获取用户位置超时。

    在这些情况下,您可以通知用户应用程序运行出了什么问题,如清单 4 所示。

    清单 4. 使用错误处理函数
     function handleLocationError(error) { 
        switch (error.code) { 
        case 0: 
            updateStatus(“尝试获取您的位置信息时发生错误:” + error.message); 
            break; 
        case 1: 
            updateStatus(“用户拒绝了获取位置信息请求。”); 
            break; 
        case 2: 
            updateStatus(“浏览器无法获取您的位置信息。”); 
            break; 
        case 3: 
            updateStatus(“获取您位置信息超时。”); 
            break; 
        } 
     }

     

  • 可选参数 options 对象可以调整 HTML5 Geolocation 服务的数据收集方式。该对象有三个可选参数:
    • enableHighAccuracy:如果启动该参数,浏览器会启动 HTML5 Geolocation 服务的高精确度模式,这将导致机器花费更多的时间和资源来确定位置,应谨慎使用。默认值为 false;
    • timeout:单位为 ms,告诉浏览器获取当前位置信息所允许的最长时间。如果在这个时间段内未完成,就会调用错误处理程序。默认值为 Infinity,即无穷大(无限制);
    • maximumAge:以 ms 为单位,表示浏览器重新获取位置信息的时间间隔。默认值为 0,这意味着浏览器每次请求时必须立即重新计算位置。

    使用可选参数 options 更新我们的位置请求,让其包含一个使用 JSON 对象表示的可选参数,如下所示:

    清单 5. 包含 options 的更新位置请求
     navigator.geolocation.getCurrentPosition(updateLocation, handleLocationError, 
     {timeout: 10000});
    • 这个调用告诉 HTML5 Geolocation,当获取位置请求的处理时间超过 10s(10000ms)时触发错误处理程序,这时,error code 应该是 3。

    重复性位置更新请求

    有 时候,仅获取一次用户位置信息是不够的。比如用户正在移动,随着用户的移动,页面应该能够不断更新显示附近的餐馆信息,这样,所显示的餐馆信息才对用户有 意义。幸运的是,HTML5 Geolocation 服务的设计者已经考虑到了这一点,应用程序可以使用如下 API 进行重复性位置更新请求,当监控到用户的位置发生变化时,HTML5 Geolocation 服务就会重新获取用户的位置信息,并调用 updateLocation() 函数处理新的数据,及时通知用户。

    清单 6. 重复性位置更新请求 API
    void watchPosition(updateLocation, optional handleLocationError, optional options);

     

    这个函数的参数跟前面提到的 getCurrentPosition 函数的参数一样,不再累赘介绍。

    关闭更新也很简单,如果应用程序不需要再接收用户的位置更新消息,只需要使用 clearWatch() 函数。参照清单 7 给的例子。

    清单 7. watchPosition 和 clearWatch 的使用
     var watchId = navigator.geolocation.watchPosition(updateLocation, handleLocationError); 
     // 基于持续更新的位置信息实现一些功能…
     // 停止接收位置更新消息
     navigator.geolocation.clearWatch(watchId);

     

    构建实用应用

    本节将介绍如何用刚刚介绍的“重复性位置更新请求”构建一个简单有用的 Web 应用程序:距离跟踪器。通过此应用程序可以了解到 HTML5 Geolocation API 的强大之处。

    想 要快速确定在一定时间内的行走距离,通常可是使用 GPS 导航系统或计步器这样的专业设备。基于 HTML5 Geolocation 提供的强大服务,我们可以自己创建一个网页来跟踪从网页被加载的地方到目前所在位置所经过的距离。虽然它在台式机上不大实用,可在手机上运行时非常理想 的。只要在手机浏览器中打开这个示例页面并授予其位置访问的权限,每隔几秒钟,应用程序就会更新计算走过的距离。如图 2 所示。

    图 2. HTML5 Geolocation 示例应用程序在 Moto ME525 上的运行界面

    在 此实例中使用的 watchPosition() 函数刚刚在上一节中介绍过。每当有新的位置返回,就将其与上次保存的位置进行比较以计算距离。距离计算使用著名的 Haversine 公式来实现,这个公式能够根据经纬度计算地球上两点间的距离。它的 JavaScript 实现如清单 8 所示:

    清单 8. Haversine 公式的 JavaScript 实现
     function toRadians(degree) { 
        return this * Math.PI / 180; 
     } 
    
     function distance(latitude1, longitude1, latitude2, longitude2) { 
        // R 是地球半径(KM)
        var R = 6371; 
    
        var deltaLatitude = toRadians(latitude2-latitude1); 
        var deltaLongitude = toRadians(longitude2-longitude1); 
        latitude1 = toRadians(latitude1); 
        latitude2 = toRadians(latitude2); 
    
        var a = Math.sin(deltaLatitude/2) * 
                Math.sin(deltaLatitude/2) + 
                Math.cos(latitude1) * 
                Math.cos(latitude2) * 
                Math.sin(deltaLongitude/2) * 
                Math.sin(deltaLongitude/2); 
    
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
        var d = R * c; 
        return d; 
     }

     

    其中 distance() 函数用来计算两个经纬度表示的位置间的距离,我们可以定期检查用户的位置,并调用这个函数来得到用户的近似移动距离。这里有一个假设,即用户在每个区间上都是直线移动的。

    显示不准确的位置信息会给用户提供误导,给用户以极其坏的印象,认为我们的应用程序不可靠,我们应该尽量避免。因此,我们将通过 position.coords.accuracy 过滤掉所有低精度的位置更新数据。如清单 9 所示:

    清单 9. 过滤不准确的位置更新数据
     // 如果 accuracy 的值太大,我们认为它不准确,不用它计算距离
     if (accuracy >= 500) { 
        updateStatus("这个数据太不靠谱,需要更准确的数据来计算本次移动距离。"); 
        return; 
     }

     

    最后,我们来计算移动距离。假设前面已经至少收到了一个准确的位置,我们将更新移动的总距离并显示给用户,同时还存储当前数据以备后面的比较。代码如清单 10 所示:

    清单 10. 计算移动距离
     // 计算移动距离
     if ((lastLat != null) && (lastLong != null)) { 
        var currentDistance = distance(latitude, longitude, lastLat, lastLong); 
        document.getElementById("本次移动距离").innerHTML = 
            "本次移动距离:" + currentDistance.toFixed(4) + " 千米"; 
    
        totalDistance += currentDistance; 
    
        document.getElementById("总计移动距离").innerHTML = 
            "总计移动距离:" + currentDistance.toFixed(4) + " 千米"; 
        } 
    
        lastLat = latitude; 
        lastLong = longitude; 
    
        updateStatus("计算移动距离成功。"); 
     }

     

    至此,本文已经给出了该示例的所有核心代码(完整代码请从附件中下载)。用这么简短的不到 150 行的 HTML 和脚本代码,我们就构建了一个能够持续监控用户位置变化的示例应用程序,几乎完整地演示了 Geolocation API 的使用。不妨把它放到您支持地理位置定位的手机或移动设备上,看看您一天大概能走多少路吧,这是不是很有趣呢?

    结束语

    本文介绍了 HTML5 提供的新功能 Geolocation,讲述了位置信息的构成:纬度、经度和准确度等,以及它们的获取途径。本文还介绍了 HTML5 Geolocation 引发的隐私问题,最后演示如何把这个新功能添加到您的应用程序中,它特别适合在移动设备如手机上运行。

    下载:code

    参考资料

    学习

    讨论

    • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

Leave a Reply