HTML5 Geolocation学习

renpinghao 2019-06-26

GeolocationAPI学习,我写的挺枯燥的,直接跳到最后看示例。

5.1 位置信息

HTML5 Geolocation API的使用方法相当简单。请求一个位置信息,如果用户同意,浏览器就会返回位置信息,该位置信息是通过支持HTML5地理定位功能的底层设备(手机、笔记本电脑等)提供给浏览器的。位置信息由维度、经度坐标和其他一些元数据组成。

5.1.1 经度和维度坐标

位置信息主要有一对维度和经度坐标组成。
例如坐标:维度:39.17222、维度:-120.13778
其中维度(距离赤道以北或以南的数值表示)是39.17222,经度(距离英国格林威治以东或以西的数值表示)是-120.13778。

经纬度可以用以下两种方式表示:

十进制格式(例如:39.17222);
DMS(Degree Minute Second,角度)格式(例如,66°33' 38" )

除了经纬度,HTML5 Geolocation还提供了位置坐标的准确度,以及根据浏览器的硬件设备是否能够提供海拔、海拔准确度、形式方向和速度等元数据,若无法提供则返回null。

5.1.2 位置的由来

Geolocation仅是检索设备提供位置信息的API,并且通过该API检索到的数据只具有某种程度的准确性。并不能保证设备返回的实际位置是精确的。

设备可以使用的数据源:

IP地址
三维坐标
    GPS(Global Positioning System,全球定位系统)
    从RFID,Wi-Fi和蓝牙到Wi-Fi的MAC地址:
    GSM或CDMA手机的ID

用户自定义数据

5.1.3 IP地址地理定位数据

基于IP地址的地理定位的实现方式是:自动查询用户的IP地址,然后检索器注册的物理地址。因此,如果用户的IP地址是ISP提供的,其位置往往是由服务供应商的物理地址决定的,该地址可能距离用户数千米。

表5-1 基于IP地址的地理位置数据的优缺点  
    优点                缺点
任何地方都可用      不精确
在服务器端处理      运算代价大

5.1.4 GPS地理定位数据

只要可以看到天空的地方,GPS就可以提供非常精确的定位。GPS定位是通过手机运行在地球周围的多个GPS卫星的信号实现的。但是,他的定位时间可能较长,因此它不适合需要快速响应的应用程序。

表5-2 基于GPS的地理定位数据的优缺点
优点                缺点
很精确          定位时间长,用户耗电量大
                室内效果不好
                需要额外硬件设备

5.1.5 Wi-Fi地理定位数据

基于WiFi的地理定位信息是通过三角距离计算得出的,这个三角距离值得是用户当前位置到一只的多个WiFi接入点的距离。不同于GPS,WiFi在室内也非常准确。

表5-3 基于Wi-Fi的地理定位的优缺点
    优点                缺点
    精确            在乡村这些无线接入点较少的地区效果不好
可在室内使用        
可以简单、快捷定位

5.1.6 手机地理定位数据

基于手机的地理定位信息是通过用户到一些基站的三角距离确定的。这种方法可提供相当准确的位置结果。这种方法通常同基于Wi-Fi和基于GPS的地理定位信息结合使用。表5-4是基于手机的地理定位数据的优缺点。

表5-4               基于手机的地理定位数据的优缺点
    优点                    缺点
    相当准确            需要能够访问手机或其modem的设备
    可在室内使用        在基站较少的偏远地区效果不好
    可以简单、快捷定位

5.1.7 用户自定义的地理定位数据

应用程序可能允许用户输入他们的地理、邮政编码和其他的一些详细信息。应用程序可以利用这些信息来提供位置感知服务。

表5-5 用户自定义的地理定位数据的优缺点
    优点                                        缺点
用户可以获得比程序定位服务更准确的位置数据  可能很不准确,特别是当用户位置变更后
允许地理定位服务结果作为备用位置信息
用户自行输入可能比自动检测更快

5.2 Geolocation的浏览器支持情况

目前主流浏览器都支持。

5.4 使用HTML5 Geolocation API

5.4.1 浏览器支持检查

function loadDemo(){
    if(navigator.geolocation){
        document.getElementById('support').innerHTML = "Geolocation supported";
    }else{
        document.getElementById("support").innerHTML = "Geolocation is not supported in your browser.";
    }
}

5.4.2 位置请求

有两种类型的位置要求:
1 单次定位的请求
2 重复性的位置更新请求

1 单次定位的请求

1 只检索或请求一次用户位置即可。例如,如果要查询在接下来的一个小时内放映某大片的最近的电影院。就可以使用代码清单5-2所示的简单HTML5 Geolocation API.

单次定位请求

void getCurrentPosition(in PositionCallback successCallback,
                        in optional PositionErrorCallback errorCallback,
                        in optional PositionOptions options);

这个函数接受一个必须参数和两个可选参数
函数参数successCallback为浏览器指明位置数据可用时调用的函数。因为像获取位置数据这样的操作可能需要较长的时间才能完成,所以这个参数很重要。没有用户希望在检索位置时浏览器被锁定,也没有开发人员希望它的程序无限期被暂停(特别是要成功取得位置信息,经常必须等待用户的许可)。successCallback是收到实际位置信息并进行处理的地方。

跟绝大多数编程场景一样,最好提前准备出错处理。位置信息请求很可能因为一些不可控因素失败,对于这些情况,你可能需要提供一个跟用户解释或者提示其重试的errorCallback函数。虽然此参数是可选的,不过建议选用。

最后,options对象可以调整HTML5 Geolocation服务的数据收集方式。这是一个可选参数,随后在论。

假设在页面上已经创建了一个名为updateLocation()的Javascript函数,它使用最新的位置数据更新页面内容,同样的,也创建了一个handleLocationError()函数来处理错误情况。而请求访问用户位置的核心代码如下所示:

navigator.geoocation.getCurrentPosition(updateLocation,handleLocationError);

updateLocation()函数:

只要浏览器具备访问位置信息的条件,就会调用updateLocation()函数,该函数只接受一个参数:位置对象.这个对象包含坐标(coords特性)和一个获取位置数据时的时间戳。在实际开发中不一定需要时间戳,重要位置数据都包含在了coords特性中。

以下是前三个特性:
latitude(纬度)
logitude(经度)
accuracy(准确度)
latitude和longitude将包含HTML5Geolocaiton服务测定的最佳十进制用户位置。accuracy将以m指定维度和经度值与实际位置的差距,置信度为95%。

updateLocation()函数使用实例

function updateLocation(position){
    var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
    var accuracy = position.coords.accuracy;
    var timestamp = position.timestamp;
    
    document.getElementById("latitude").innerHTML = latitude;
    document.getElementById('longitude').innerHTML = longitude;
    document.getElementById('accuracy').innerHTML = accuracy;
    document.getElementById('timestamp').innerHTML = timestamp;
}

handleLocationError()函数

因为位置计算服务很可能出错,对于HTML5 Geolocation应用程序来说处理错误非常重要。而该API定义了所有需要处理的错误情况的编号。错误编号设置在错误对象中,错误对象作为code参数传递给错误处理程序。这些错误编号为:

PERMISSION_DENIED(错误编号为1)---用户选择拒绝浏览器获得其位置信息。
POSITION_UNAVAILABLE(错误编号为2)---尝试获取用户位置数据,但失败了。
TIMEOUT(错误编号为3)--设置了可选的timeout值。尝试确定用户位置的过程超时。

使用错误处理函数
function hadleLocationError(error){
    switch(error.code){
        case 0:
            updateStatus("There was an error while retrieving your location" + error.message);
        break;
        case 1:
            updateStatus("The user prevented this page from retrieving a location");
        break;
        case 2:
            updateStatus("The browser was unable to determine your location" + error.message);
        break;
        case 3:
            updateStatus("The browser timed out before retrieving the location");
        break;
    }
}
可选的地理定位请求特性:

如果要同时处理正常情况和错误情况,就应该把注意力集中到三个可选参数(enableHight-Accuracy,timeout和maximumAge)上,将这三个可选参数传递给HTML5 Geolocation服务以调整数据收集方式。请注意,这三个参数可以使用JSON对象传递,这样更便于添加到HTML5Geolocation请求调用中。

enableHighAccuracy:启用该参数,则通知浏览器启用HTML5 Geolocation服务的高精度模式。参数的默认值为false。如果启用该参数,可能没有任何差别,也可能会导致机器花费更多的时间和资源来确定位置,所以请谨慎。


timeout:可选值,单位为ms,告诉浏览器计算当前位置所允许的最长时间。如果在这个时间内未完成计算,就会调用错误处理程序。其默认值为Infinity,即无穷大或无限制。

maximumAge : 这个值表示浏览器重新计算位置的时间间隔。它是一个以ms为单位的值。此值默认为0,这意味着浏览器每次请求超时时必须立即重新计算位置。

注意:地理定位API不允许我们为浏览器指定多长时间重新计算一次位置信息。这是完全由浏览器的实现所决定的。我们能做的就是告诉浏览器maximumAge的返回值是什么。实际频率是我们一个无法控制的细节。

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

navigator.geolocation.getCurrentPostion(updateLocation,handleLocationError ,{timeout:10000});

这个新调用告诉HTML5 Geolocation,任何处理时间超过10s的位置请求都应该调用handleLocationError函数处理编号对应的TIMEOUT的错误。

2 重复性的位置更新请求

有时候,仅更新一次是不够的。还好,Geolocation服务的设计者使应用程序可以在单词请求用户定位和以既定时间间隔多次请求用户位置间相互转换。事实上,转换的方式很简单,只需要变换函数请求即可,如下所示:

一次更新:

navigator.geolocation.getCurrentPosition(updateLocation,handleLocationError);

重复更新:

navigator.geolocation.wathcPosition(updateLocation,handleLocationError);
// 只要用户的位置发生变化,Geolocation服务就会调用updateLocaiton处理程序,它的效果就像是程序在监视用户的位置,并会在其变化时及时通知用户一样。

重复更新的好处:如有一个页面,随着观察者在城镇周围移动,网页上的方向指示也随之改变,再假设关于一个加油站的页面,随着用户开车在高速公路上持续行驶,页面不断更新显示最近的加油站。另外,还可以在一个页面中记录和传送用户位置来实现回溯已走的路线。如果位置信息一发生改变,就能传递给应用程序,那么前面所假设的所有服务都会变得很容易实现。

如果应用程序不再需要接受有关用户的持续位置更新,则只需调用clearWatch()函数,如下所示:

navigator.geolocation.clearWatch(watchId);

watchId表示一个唯一的监视请求以便将来取消监视。所以,如果应用程序要停止接收位置更新信息,可以参照以下代码。

var watchId = navigator.geolocation.watchPosition(updateLocation,handleLocationError);
 // 基于持续更新的位置信息的一些功能
 //......
 // ok,现在我们可以停止接收位置更新信息了
 navigator.geolocation.clearWatch(watchId);

4.5 使用HTML5 Geolocation构建实时应用

接下来我们使用多次请求特性构建一个简单有用的Web应用程序---距离跟踪器。

基于HTML5 Geolocation提供的强大定位服务,开发人员可以创建一个网页来跟踪从网页被加载的地方到目前所在位置所经过的距离。虽然它在台式机上不大实用,但是对于手机是很理想的,只要在手机浏览器中打开这个示例页面并授予其位置访问的权限,每隔几秒钟,应用程序就会根据刚才走过的距离更新,并将其增加到总距离中。

距离计算公式使用Haversine公式来实现,这个公式能够根据经纬度来计算地球两点距离。代码如下(这个公式后面看看有没有时间研究一下,有就后续补上):

Number.prototype.toRadians = function() {
      return this * Math.PI / 180;
    }


    function distance(latitude1, longitude1, latitude2, longitude2) {
      // R is the radius of the earth in kilometers
      var R = 6371;

      var deltaLatitude = (latitude2-latitude1).toRadians();
      var deltaLongitude = (longitude2-longitude1).toRadians();
      latitude1 = latitude1.toRadians(), latitude2 = latitude2.toRadians();

      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;
    }

4.5.1 编写HTML显示代码

以简单的表格进行展示,分行显示纬度、经度、准确度和以ms为单位的时间戳。

<h1>HTML5 Geolocation Distance Tracker</h1>

<p id="status">HTML5 Geolocation is <strong>not</strong> supported in your browser.</p>

<h2>Current Position:</h2>
<table border="1">
<tr>
<th width="40" scope="col"><h5>Latitude</h5></th>
<td width="114" id="latitude">?</td>
</tr>
<tr>
<td> Longitude</td>
<td id="longitude">?</td>
</tr>
<tr>
<td>Accuracy</td>
<td id="accuracy">?</td>
</tr>
<tr>
<td>Last Timestamp</td>
<td id="timestamp">?</td>
</tr>
</table>

<h4 id="currDist">Current distance traveled: 0.0 km</h4>
<h4 id="totalDist">Total distance traveled: 0.0 km</h4>

4.5.2 处理Geolocation数据

在Geolocation数据处理部分,第一段Javascript代码应该看起来很熟悉了。之前我们设置过一个处理程序loadDemo(),它会在页面加载完成时候执行。这个脚本会检测浏览器是否支持HTML5 Geolocation,然后将检测结果显示在页面上。最后,代码会请求检测用户位置。

添加loadDemo()方法
var totalDistance = 0.0;
var lastLat;
var lastLong;
function updateStatus(message){
    document.getElementById("status").innerHTML = message;
}
function loadDemo(){
    if(navigator.geolocation){
        updateStatus.geolocation(){
            updateStatus("HTML5 Geolocation is supported in your browser");
            navigator.geolocation.watchPosition(updateLocation,handleLocationError,{maximumAge:20000});
        }
    }
}
window.addEventListener('load',loadDemo,true);

对于错误处理,我们使用之前提到过的那段代码,我们将检查收到的所有错误编号,并更新页面上的状态信息。

添加错误代码

function handleLocationError(error) {
        switch(error.code)
        {
        case 0:
          updateStatus("There was an error while retrieving your location: " + error.message);
          break;
        case 1:
          updateStatus("The user prevented this page from retrieving a location.");
          break;
        case 2:
          updateStatus("The browser was unable to determine your location: " + error.message);
          break;
        case 3:
          updateStatus("The browser timed out before retrieving the location.");
          break;
        }
    }

而我们大部分工作都将在updateLocation()函数中实现,此函数中我们将使用最新数据来更新页面并计算路程。代码如下:

function updateLocation(position) {
        var latitude = position.coords.latitude;
        var longitude = position.coords.longitude;
        var accuracy = position.coords.accuracy;
        var timestamp = position.timestamp;

        document.getElementById("latitude").innerHTML = latitude;
        document.getElementById("longitude").innerHTML = longitude;
        document.getElementById("accuracy").innerHTML = accuracy;
        document.getElementById("timestamp").innerHTML = timestamp;

        // sanity test... don't calculate distance if accuracy
        // value too large
        if (accuracy >= 500) {
            updateStatus("Need more accurate values to calculate distance.");
            return;
        }

        // calculate distance 计算距离

        if ((lastLat != null) && (lastLong != null)) {
            var currentDistance = distance(latitude, longitude, lastLat, lastLong);
            document.getElementById("currDist").innerHTML =
              "Current distance traveled: " + currentDistance.toFixed(4) + " km";

            totalDistance += currentDistance;

            document.getElementById("totalDist").innerHTML =
              "Total distance traveled: " + currentDistance.toFixed(4) + " km";
        }


        lastLat = latitude;
        lastLong = longitude;

        updateStatus("Location successfully updated.");
    }

首先获取坐标数据后,记录所有信息。即收集经纬度,准确度和时间戳,然后将这些数据更新到表格中。
时间戳对于用户而言没有意义,主要是提供程序使用,可以将时间戳替换成便于用户识别的时间指示器,或者将其完全删除。

准确度是以米为单位的,显示不准确的值会向用户提供错误的位置信息,因此,需要将过滤掉有所有低经度的位置更新数据。

if (accuracy >= 500) {
            updateStatus("Need more accurate values to calculate distance.");
            return;
        }

完整代码如下

<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>HTML5 Geolocation Odometer</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body onload="loadDemo()">

<h1>HTML5 Geolocation Distance Tracker</h1>

<p id="status">HTML5 Geolocation is <strong>not</strong> supported in your browser.</p>

<h2>Current Position:</h2>
<table border="1">
<tr>
<th width="40" scope="col"><h5>Latitude</h5></th>
<td width="114" id="latitude">?</td>
</tr>
<tr>
<td> Longitude</td>
<td id="longitude">?</td>
</tr>
<tr>
<td>Accuracy</td>
<td id="accuracy">?</td>
</tr>
<tr>
<td>Last Timestamp</td>
<td id="timestamp">?</td>
</tr>
</table>

<h4 id="currDist">Current distance traveled: 0.0 km</h4>
<h4 id="totalDist">Total distance traveled: 0.0 km</h4>


<script type="text/javascript">

    var totalDistance = 0.0;
    var lastLat;
    var lastLong;

    Number.prototype.toRadians = function() {
      return this * Math.PI / 180;
    }


    function distance(latitude1, longitude1, latitude2, longitude2) {
      // R is the radius of the earth in kilometers
      var R = 6371;

      var deltaLatitude = (latitude2-latitude1).toRadians();
      var deltaLongitude = (longitude2-longitude1).toRadians();
      latitude1 = latitude1.toRadians(), latitude2 = latitude2.toRadians();

      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;
    }


    function updateStatus(message) {
        document.getElementById("status").innerHTML = message;
    }

    function loadDemo() {
        if(navigator.geolocation) {
            updateStatus("HTML5 Geolocation is supported in your browser.");
            navigator.geolocation.watchPosition(updateLocation,
                                                handleLocationError,
                                                {maximumAge:20000});
        }
    }

    function updateLocation(position) {
        var latitude = position.coords.latitude;
        var longitude = position.coords.longitude;
        var accuracy = position.coords.accuracy;
        var timestamp = position.timestamp;

        document.getElementById("latitude").innerHTML = latitude;
        document.getElementById("longitude").innerHTML = longitude;
        document.getElementById("accuracy").innerHTML = accuracy;
        document.getElementById("timestamp").innerHTML = timestamp;

        // sanity test... don't calculate distance if accuracy
        // value too large
        if (accuracy >= 500) {
            updateStatus("Need more accurate values to calculate distance.");
            return;
        }

        // calculate distance

        if ((lastLat != null) && (lastLong != null)) {
            var currentDistance = distance(latitude, longitude, lastLat, lastLong);
            document.getElementById("currDist").innerHTML =
              "Current distance traveled: " + currentDistance.toFixed(4) + " km";

            totalDistance += currentDistance;

            document.getElementById("totalDist").innerHTML =
              "Total distance traveled: " + currentDistance.toFixed(4) + " km";
        }


        lastLat = latitude;
        lastLong = longitude;

        updateStatus("Location successfully updated.");
    }

    function handleLocationError(error) {
        switch(error.code)
        {
        case 0:
          updateStatus("There was an error while retrieving your location: " + error.message);
          break;
        case 1:
          updateStatus("The user prevented this page from retrieving a location.");
          break;
        case 2:
          updateStatus("The browser was unable to determine your location: " + error.message);
          break;
        case 3:
          updateStatus("The browser timed out before retrieving the location.");
          break;
        }
    }

</script>
</body>
</html>

Geolocation匆匆忙忙结束了,请大家当个键盘侠,多多指点,毕竟键多识广。

相关推荐