HTML5地理定位






4.97/5 (35投票s)
探索HTML5的地理定位功能
我在地球上的哪个位置?
我们每个人都在地球上占据一个位置。这个位置由纬度、经度和海拔的地理坐标系统指定。随着定位硬件和软件的普及,在地球上找到自己的位置从未如此简单。有许多技术可以用来识别用户的位置。计算机浏览器通常使用 Wi-Fi 或基于 IP 的定位技术,而移动浏览器可能使用小区三角测量(基于您与电信运营商运营的不同蜂窝塔的相对距离)、GPS、A-GPS 或 Wi-Fi。如今,位置感知是一个日益增长的趋势,并已融入许多应用中,例如:
- 在地图上显示自己的位置,尤其是在不熟悉的地方
- 在不熟悉的路程中提供驾车导航
- 找出附近的热门景点
- 获取您所在区域的最新天气或新闻
- 标记照片的位置
- 车队配送卡车的定位跟踪
感谢 HTML5 Geolocation API,您现在可以使用浏览器(例如 Firefox)查找自己在地球上的位置。这非常容易。实践出真知。让我们动手试试。
准备工作
在文本编辑器中输入清单 1 中的代码,将其保存为“finding_me.html”,然后在浏览器中打开它。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Finding Me</title>
<script>
function getLocation()
{
// Check whether browser supports Geolocation API or not
if (navigator.geolocation) { // Supported
navigator.geolocation.getCurrentPosition(getPosition);
} else { // Not supported
alert("Oop! This browser does not support HTML5 Geolocation.");
}
}
function getPosition(position)
{
document.getElementById("location").innerHTML =
"Latitude: " + position.coords.latitude + "<br>" +
"Longitude: " + position.coords.longitude;
}
</script>
</head>
<body>
<h1>Finding Me</h1>
<button onclick="getLocation()">Where am I?</button>
<p id="location"></p>
</body>
</html>
您应该会看到一个如图 1 所示的网页。请注意,此代码在 Firefox 和 Internet Explorer 中可以正常工作,但在 Google Chrome 中不行。Chrome 不允许从 file:// URI 运行 Geolocation API,如果您将其部署到 Apache 等 Web 服务器上,它就可以工作。
点击“我在哪里?”按钮,您看到了什么?
将弹出如图 2 所示的对话框,请求分享位置的权限,点击“分享位置”以授予权限。
瞧,您的位置已显示在屏幕上,如图 3 所示。这难道不是一段代码,我是说,简直是小菜一碟。
您多么希望能在地图上显示这些位置信息,以便直观地查看自己的位置,对吧?如果您继续阅读,您的愿望将得以实现……
深入了解…
HTML5 geolocation API 只有一个对象——navigator.geolocation
对象。您可以将 navigator.geolocation
比作浏览器的指南针。由于浏览器对该 API 的支持仍不确定,因此检查浏览器支持是约定俗成的做法,然后再进行任何地理定位代码。只需将您的代码包装在此 if
-else
模板中,如下所示:
// Check whether browser supports Geolocation API or not
if (navigator.geolocation) // Supported
{
// place the geolocation code here
}
else // Not supported
{
alert("Oop! This browser does not support HTML5 Geolocation.");
}
navigator.geolocation
对象公开了三个方法——getCurrentPosition()
、watchPosition()
和 clearWatch()
。
getCurrentPosition()
getCurrentPosition()
方法用于获取用户的当前位置。您已经在清单 1 中的代码以最简单的形式使用了此方法。
navigator.geolocation.getCurrentPosition(getPosition);
回想一下图 3 中弹出的对话框,它请求您分享位置信息给网页。每当调用 getCurrentPosition()
方法时,都会发生这种情况。用户可以选择选择加入,以允许该方法继续检索您的当前位置。换句话说,您的隐私受到了充分的尊重和保护。
getCurrentPosition()
方法接受三个参数:
- 第一个参数是一个回调函数,当
getCurrentPosition()
方法调用成功时调用。该回调函数将使用getCurrentPosition()
方法传递的position object 进行调用。此 position object 包含两个属性:coords
和timestamp
。在清单 1 中,回调函数是“getPosition(position)
”,它接受一个position
对象参数,并通过该参数的coords
属性输出latitude
和longitude
。这在以下代码中得到了说明:function getPosition(position) { document.getElementById("location").innerHTML = "Latitude: " + position.coords.latitude + "<br>" + "Longitude: " + position.coords.longitude; }
表 1 显示了
position
对象的所有属性。表 1:Position Object
属性 描述 coords.latitude
coords.latitude
属性以十进制度返回用户当前位置的纬度。coords.longitude
coords.longitude
属性以十进制度返回用户当前位置的经度。coords.altitude
coords.altitude
属性以米为单位返回用户当前位置的海拔高度。如果此信息不可用,则返回null
。coords.accuracy
coords.accuracy
属性以米为单位返回纬度和经度坐标的精度级别。coords.altitudeAccuracy
coords.altitudeAccuracy
属性以米为单位返回海拔高度的精度级别。coords.heading
coords.heading
属性以度为单位返回定位设备行进方向,其中 0° 从北方开始,顺时针计数。如果此信息不可用,则返回null
。coords.speed
coords.speed
属性以米/秒为单位返回定位设备的速度。如果此信息不可用,则返回null
。时间戳
timestamp
属性返回获取 position object 的时间。
- 第二个参数是一个可选的错误处理回调函数,当
getCurrentPosition()
方法调用遇到以下任何一种情况时调用:- 请求超时
- 位置信息不可用
- 用户拒绝分享位置信息的权限
该回调函数将使用
getCurrentPosition()
方法传递的position error object 参数进行调用。此 position error object 包含一个属性——code
。此 code 属性接受与错误类型对应的以下三种值之一,如表 2 所示。表 2:位置错误代码
属性 描述 TIMEOUT
获取位置信息的请求超出了(稍后讨论的)position options object 中设置的 timeout
属性。POSITION_UNAVAILABLE
无法确定定位设备的位置。 PERMISSION_DENIED
用户拒绝分享位置信息的权限。 您将添加第二个回调函数“
catchError(error)
”到<script>
中,如下面的代码所示,并在图 4 中突出显示:function catchError(error) { switch(error.code) { case error.TIMEOUT: alert("The request to get user location has aborted as it has taken too long."); break; case error.POSITION_UNAVAILABLE: alert("Location information is not available."); break; case error.PERMISSION_DENIED: alert("Permission to share location information has been denied!"); break; default: alert("An unknown error occurred."); } }
图 4:catchError() 函数接下来,将以下函数作为第二个参数附加到
getCurrentPosition()
方法,如下所示:navigator.geolocation.getCurrentPosition(getPosition, catchError);
保存并在浏览器(Firefox)中打开它,拔掉网线(或关闭无线开关)以模拟“无网络连接”的情况,授予分享位置信息的权限(图 2),然后点击“我在哪里?”按钮。您看到了什么?
图 5:模拟错误情况您应该会看到一个弹出消息框,显示“位置信息不可用。”,如图 5 所示。如果您回头看
catchError()
函数中的代码,您会注意到这是error.POSITION_UNAVAILABLE
情况下的错误消息。原因显然是网线被拔掉了。由于网络连接中断而无法获取任何位置信息,因此catchError()
函数被getCurrentPosition()
方法调用。(您可以恢复计算机的网络连接。)虽然此错误处理参数是可选的,但您应始终将其作为地理定位代码的一部分。这是允许程序优雅地失败并让用户了解任何运行时错误的最佳实践之一。 - 第三个参数是一个可选的position options object,可用于通过编程方式微调
getCurrentPosition()
方法返回的 position object。它有三个属性,如表 3 所示。表 3:Position Options Object Properties 属性 描述 timeout
timeout 属性表示应用程序等待位置信息可用的毫秒数。默认值为 infinity。 maximumAge
maximumAge
属性表示应用程序在尝试获取新的位置数据之前可以使用缓存(先前获取)的位置数据的毫秒数。零值表示应用程序不得使用缓存的位置数据,而 infinity 值表示应用程序必须使用缓存的位置数据。默认值为零。enableHighAccuracy
enableHighAccuracy
属性表示true
或false
。如果为true
,应用程序将尝试提供最精确的位置。但是,这会导致响应时间变慢和功耗增加。默认值为false
。您将定义一个名为“
positionOptions
”的 position options object,并将其作为第三个参数添加到getCurrentPosition()
方法中,如下面的代码所示,并在图 6 中突出显示:var positionOptions = { timeout : Infinity, maximumAge : 0, enableHighAccuracy : true }; navigator.geolocation.getCurrentPosition(getPosition, catchError, positionOptions);
图 6:Position Options Object在设计您的定位感知应用程序时,您应该根据应用程序的目的选择最合适的精度等级和旧位置数据的保留期,相应地设置 position options object 的
enableHighAccuracy
和maximumAge
属性。例如,如果您的应用程序主要用于查找附近的兴趣点,您可能不需要高精度,并且位置信息不必经常更新。另一方面,如果您的应用程序旨在为驾驶员提供导航方向,那么高精度和持续的位置更新就变得必不可少。
getCurrentPosition()
方法最适合获取一次性位置信息。但是,对于持续变化的位置信息(如我上面提到的导航),您需要反复调用 getCurrentPosition()
方法,这显然非常繁琐且效率低下。别担心!HTML5 Geolocation API 提供了另一种方法来处理这种情况——watchPosition()
。
watchPosition()
watchPosition()
方法与 getCurrentPosition()
方法几乎相同。它们都返回当前位置信息,并且具有相同的函数签名——一个成功回调函数、一个错误回调函数和一个 position options object。唯一的区别在于 getCurrentPosition()
方法在激活时(例如点击按钮)只返回一次位置信息,而 watchPosition()
方法在初始激活后,每次用户设备的位置发生变化时都会继续获取位置信息。
watchPosition()
方法返回一个watch ID,该 ID 可以通过将其传递给 navigator.geolocation
对象的第三个方法——clearWatch()
来停止获取位置信息,例如当使用它进行导航的汽车到达目的地时。
clearWatch()
clearWatch()
方法接受 watchPosition()
方法的 watch ID 作为参数,并停止该 watchPosition()
方法的执行。
位置跟踪
由于 watchPosition()
方法与 getCurrentPosition()
方法相似,如果您熟悉后者,创建 watchPosition()
方法将会容易得多——这主要就是“复制粘贴”的事情,信不信由你。
在文本编辑器中打开“finding_me.html”,将其另存为“watching_me.html”。然后在“watching_me.html”中:
- 将“
geolocation.getCurrentPosition(getPosition, catchError, positionOptions)
”更改为“navigator.geolocation.watchPosition(getPosition, catchError, positionOptions)
”。
- 将
watchPosition()
方法返回的 watch ID 存储到一个变量watchID
中。watchID = navigator.geolocation.watchPosition(getPosition, catchError, positionOptions);
这就是创建不断监视和更新浏览器当前位置的网页所需的所有内容。
然而,要在某个时间点停止监视和更新,您需要添加另一段代码和一个按钮来禁用它。在“watching_me.html”的 <script>
部分,创建一个名为“stopWatch()
”的函数,该函数通过将 watchPosition()
方法的 watchID
传递给 clearWatch()
方法来调用它。
function stopWatch()
{
navigator.geolocation.clearWatch(watchID);
}
最后,用以下代码替换“watching_me.html”的 <body>
中的 HTML 代码:
<body>
<h1>Watching Me</h1>
<button onclick="getLocation()">Start Watching</button>
<button onclick="stopWatch()">Stop Watching</button>
<p id="location"></p>
</body>
“watching_me.html”的完整源代码如下:
<!DOCTYPE html>
<html>
<head>
<title>Watching Me</title>
<script>
function getLocation()
{
// Check whether browser supports Geolocation API or not
if (navigator.geolocation) // Supported
{
var positionOptions = {
timeout : Infinity,
maximumAge : 0,
enableHighAccuracy : true
};
// Set the watchID
watchID = navigator.geolocation.watchPosition(getPosition, catchError, positionOptions);
}
else // Not supported
{
alert("Oop! This browser does not support HTML5 Geolocation.");
}
}
function stopWatch()
{
navigator.geolocation.clearWatch(watchID);
watchID = null;
}
function getPosition(position)
{
document.getElementById("location").innerHTML =
"Latitude: " + position.coords.latitude + "<br>" +
"Longitude: " + position.coords.longitude;
}
function catchError(error) {
switch(error.code)
{
case error.TIMEOUT:
alert("The request to get user location has aborted as it has taken too long.");
break;
case error.POSITION_UNAVAILABLE:
alert("Location information is not available.");
break;
case error.PERMISSION_DENIED:
alert("Permission to share location information has been denied!");
break;
default:
alert("An unknown error occurred.");
}
}
</script>
</head>
<body>
<h1>Watching Me</h1>
<button onclick="getLocation()">Start Watching</button>
<button onclick="stopWatch()">Stop Watching</button>
<p id="location"></p>
</body>
</html>
保存并在已连接 Wi-Fi 的笔记本电脑(Firefox)浏览器中打开“watching_me.html”,点击“开始监视”按钮,授予分享位置的权限,然后移动笔记本电脑。您看到了什么?
您应该会看到您的位置以固定间隔在浏览器中更新,如图 7 所示的示例。
如果您将纬度和经度值复制并粘贴到浏览器上的 Google 地图的搜索框中,Google 地图将用标记标记您的大致位置。该位置是近似的,在不同的浏览器和设备上可能有所不同,其准确性取决于多种因素,例如您的公用 IP 地址、附近的蜂窝塔、GPS 可用性、Wi-Fi 接入点以及您使用的浏览器类型。
地图上的位置跟踪
让我们将监视放到地图上,如图 8 所示的示例。
文件名为“watching_on_map.html”,如下所示,可供下载。
<!DOCTYPE html>
<html>
<head>
<title>Watching on Map</title>
<style>
html,body {
height: 100%;
margin: 0;
padding: 0;
}
#map-holder {
height: 350px;
width: 500px;
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
<script>
var watchID;
function getLocation()
{
// Check whether browser supports Geolocation API or not
if (navigator.geolocation) // Supported
{
var positionOptions = {
timeout : Infinity,
maximumAge : 0,
enableHighAccuracy : true,
};
// Obtain the initial location one-off
navigator.geolocation.getCurrentPosition(getPosition, catchError, positionOptions);
}
else // Not supported
{
alert("Oop! This browser does not support HTML5 Geolocation.");
}
}
function watchLocation()
{
// Check whether browser supports Geolocation API or not
if (navigator.geolocation) // Supported
{
var positionOptions = {
timeout : Infinity,
maximumAge : 0,
enableHighAccuracy : true,
};
// Obtain the location at regularly interval
watchID = navigator.geolocation.watchPosition(getPosition, catchError, positionOptions);
}
else // Not supported
{
alert("Oop! This browser does not support HTML5 Geolocation.");
}
}
function stopWatch()
{
// Discontinue obtaining location
navigator.geolocation.clearWatch(watchID);
}
function getPosition(position)
{
var location = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
var mapOptions = {
zoom : 12,
center : location,
mapTypeId : google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map-holder'), mapOptions);
var marker = new google.maps.Marker({
position: location,
title: 'Here I am!',
map: map,
animation: google.maps.Animation.DROP
});
var geocoder = new google.maps.Geocoder();
geocoder.geocode({
'latLng' : location
}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
var options = {
content : results[1].formatted_address,
position : location
};
var popup = new google.maps.InfoWindow(options);
google.maps.event.addListener(marker, 'click', function() {
popup.open(map);
});
}
else
{
alert('No results found');
}
}
else
{
alert('Geocoder failed due to: ' + status);
}
});
}
function catchError(error) {
switch(error.code)
{
case error.TIMEOUT:
alert("The request to get user location has aborted as it has taken too long.");
break;
case error.POSITION_UNAVAILABLE:
alert("Location information is not available.");
break;
case error.PERMISSION_DENIED:
alert("Permission to share location information has been denied!");
break;
default:
alert("An unknown error occurred.");
}
}
</script>
</head>
<body onload="getLocation()">
<div id="map-holder"></div>
<button onclick="watchLocation()">Start Watching</button>
<button onclick="stopWatch()">Stop Watching</button>
</body>
</html>
代码逻辑如下:
- 当页面首次加载时,将触发
onload
事件并调用getLocation()
函数,该函数又调用navigator.geolocation
对象的getCurrentPosition()
方法来获取用户的当前位置。 - 如果
getCurrentPosition()
方法成功,它将触发回调函数getPosition()
并将 position object 传递给它。 getPosition()
函数的作用是渲染一个以用户位置为中心并显示为标记的 Google 地图。- 点击“开始监视”按钮将调用
watchLocation()
函数,该函数又调用navigator.geolocation
对象的watchPosition()
方法以固定间隔获取用户的当前位置。 - 如果
watchPosition()
方法成功,它将触发回调函数getPosition()
并将 position object 传递给它。 - 上面已描述
getPosition()
函数的作用。 - 点击“停止监视”按钮将停止监视活动。
如果您在保持此网页打开的情况下移动连接 Wi-Fi 的笔记本电脑,您应该会看到您的位置在您移动时定期在浏览器中更新。玩得开心!
下载
这篇 HTML5 Geolocation 文章首发于 Peter Leow's Code Blog。