某人

此前素未谋面、此后遥遥无期

0%

地理定位H5

html5 Geolocation 基本介绍

HTML5有一个新功能:Geolocation,它允许用户在 Web 应用程序中共享他们的位置,使其能够享受位置感知服务。
Geolocation API的使用很简单,请求一个位置信息,如果用户同意,就返回位置信息。

注:此处所提到的定位都是javascript地图API,适合Web 应用程序

获取地理位置的方式

设备可以使用的数据源:

  • IP 地址
  • GPS(Global Positioning System,全球定位系统)
  • WiFi基站的mac地址
  • 手机信号(GSM或CDMA基站)
  • 用户自定义数据

数据源对比:

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

推荐:

可见它们各有优缺点,为了保证更高的精确度,许多设备使用多个数据源组合的方式。

地理位置获取流程

  1. 用户打开需要获取地理位置的web应用
  2. 应用通过Geolocation API 向浏览器请求地理位置,浏览器拦截请求,向用户请求授权。
  3. 假设用户允许,浏览器从设备查询相关信息,如IP地址、WIFI地址或GPS坐标。
  4. 浏览器将相关信息发送给信任的外部定位服务器,服务器返回具体的地理位置。

隐私

HTML5 Geolocation API提供了一套保护用户隐私的机制,必须先得到用户明确许可,才能获取用户的位置信息。访问使用Geolocation API的页面应用时,会触发隐私保护机制,浏览器会询问用户是否共享位置信息。

因为位置数据属于敏感信息,所以应用程序的开发人员应考虑关于隐私的以下准则:

  • 仅在必要时请求位置信息,并且仅将位置信息用于其原计划用于的任务。
  • 如果用户没有授权存储这些数据,那么应用程序应该在相应任务完成后立即删除。
  • 如果要在服务器上存储位置数据,请务必确保位置数据不受到未经授权的访问,并允许用户更新和删除此信息。

检测浏览器是否支持Geolocation

在html5使用Geolocation API时,应确保浏览器支持Geolocation API,如果geolocation对象存在,那么地理位置服务可用。

1
2
3
4
5
if ("geolocation" in navigator) {
console.log("地理位置服务可用");
} else {
console.log("地理位置服务不可用")
}

注意: 在 Firefox 24 和之前的浏览器中,即使 API 被禁止,代码 “geolocation” in navigator 也总是会得到 true。这在 Firefox 25 中已经被修复bug 884921

##getCurrentPosition获取当前定位
###getCurrentPosition函数介绍
使用 getCurrentPosition 方法获取当前的地理位置。
语法

1
navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);

getCurrentPosition包含三个参数,前两个为函数名,第三个为一个对象。其中只有第一个是必须的。当你执行上面的JavaScript语句后,浏览器通常会弹出一个提示,询问用户是否允许网站跟踪位置信息;同时getCurrentPosition函数会立即返回。如果用户选择了允许,则会执行上述successCallback函数,

这个方法使用的是异步回调的方式。它的3个参数分别是:

  1. successCallback – 为浏览器成功获得位置信息后的回调函数。
  2. errorCallback – 用于位置信息获取失败时的回调函数。
  3. options – 配置参数,可以调整geolocation的数据收集方式

注意: 默认情况下,getCurrentPosition() 会尽快返回一个低精度结果,这在您不关心准确度只关心快速获取结果的情况下很有用。有 GPS 的设备可能需要一分钟或更久来获取 GPS 定位,在这种情况下 getCurrentPosition() 会返回低精度数据(基于 IP 的定位或 Wi-Fi 定位)。

成功获得地理位置信息

下面是函数successCallback的写法示例。它有一个参数,你将最终通过这个函数获取到用户的位置。经纬度分别存在longitude和latitude变量中,同时还有精度、时间戳,以及一些其他额外信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function successCallback(pos) {
// 将会获得以下信息
var latitude = pos.coords.latitude;
var longitude = pos.coords.longitude;
var accuracy = pos.coords.accuracy;
var timestamp = pos.timestamp;

// 以下信息不一定提供,和具体设备有关
var altitude = pos.coords.altitude;
var altitudeAccuracy = pos.coords.altitudeAccuracy;
var heading = pos.coords.heading;
var speed = pos.coords.speed;

console.log(pos.coords.latitude); // 纬度
console.log(pos.coords.longitude); // 经度
console.log(pos.coords.accuracy); // 准确度,由于geolocation的实现方式,呈现返回值时一定要检查返回值的准确度
console.log(pos.coords.altitude); // 海拔,以米为单位,如不支持altitude特性,返回null
console.log(pos.coords.altitudeAccuracy); // 海拔经度,以米为单位,如不支持altitude特性,返回null
console.log(pos.coords.heading); // 行进方向,相对正北
console.log(pos.coords.speed); // 行进速度,单位m/s
console.log(pos.timestamp); // 获取位置的时间
}

其中altitudealtitudeAccuracyheadingspeed则根据用户的终端不同,可能提供,可能为null
参考图片

错误信息处理

上面我们还有一个errorCallback。显然,如果用户不同意,那我们无法获得任何信息,这时候系统会自动调用这个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function errorCallback(error) {
console.log(error.message);
switch (error.code) {
case error.PERMISSION_DENIED:
console.log("用户不允许地理定位");
break;
case error.POSITION_UNAVAILABLE:
console.log("无法获取当前位置");
break;
case error.TIMEOUT:
console.log("操作超时");
break;
case error.UNKNOWN_ERROR:
console.log("发生未知错误");
break;
}
}

其中error.code为一个枚举类型,可能的取值如下:

  • PERMISSION_DENIED:用户拒绝
  • POSITION_UNAVAILABLE:地理位置获取失败(可能是用户没网或卫星搜不到等原因)
  • TIMEOUT:地理位置获取超时
  • UNKNOWN_ERROR:发生未知错误

而error.message则为一个可以帮助开发者调试的错误信息(此信息一般不适合直接显示在网页中给用户查看)。

可选项

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

其中timeout是设定地理位置获取的超时时间(单位为毫秒,用户选择允许的时间不计算在内);而maximumAge表示允许设备从缓存中读取位置,缓存的过期时间,单位是毫秒,设为0来禁用缓存读取。如果返回的是缓存中的时间,会在timestamp中反映出来。

watchPosition函数

您可以设定一个回调函数来响应定位数据发生的变更(设备发生了移动,或获取到了更高精度的地理位置信息)。您可以通过 watchPosition() 函数实现该功能。它与 getCurrentPosition() 接受相同的参数,但回调函数会被调用多次。错误回调函数与 getCurrentPosition() 中一样是可选的,也会被多次调用。

语法:

1
var watchID = navigator.geolocation.watchPosition(successCallback, errorCallback, options);

watchPosition方法与getCurrentPosition方法类似,都有同样的三个参数,都立即返回。下面是watchPosition的特殊之处:

  • 该函数会返回一个整数,表示这个监视任务的 ID。
  • 将持续追踪用户的位置;相应,on_success函数会被多次地调用来更新信息。
  • 通过clearWatch(id)函数调用来停止监视。

clearWatch函数

这个方法接受一个参数,需要清理监视位置变化的方法的id:watchID(这个参数由watchPosition方法返回)。

参考代码

1
navigator.geolocation.clearWatch(watchID);

DEMO源码

百度地图源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="email=no" />
<meta name="apple-touch-fullscreen" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1, user-scalable=no">
<title>百度地图</title>
<meta name="keywords" content="百度地图">
<meta name="description" content="百度地图">
<style type="text/css">
*{margin:0;padding:0;}
html,body{height:100%;}
.map{display:block;width:calc(100% - 20px);margin:10px auto;height:calc(100% - 20px);}
</style>
</head>
<body>

<div id="map" class="map"></div>

<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=OuwScGRpy5UlO2jfRrDe7KKq"></script>
<script type="text/javascript">
//此处使用百度地图2.0API
var map = new BMap.Map("map");
var point = new BMap.Point(116.331398, 39.897445);//默认北京
map.centerAndZoom(point, 12);

var geolocation = new BMap.Geolocation();
geolocation.getCurrentPosition(function(r) {
if (this.getStatus() == BMAP_STATUS_SUCCESS) {
var mk = new BMap.Marker(r.point);
map.addOverlay(mk);// 将标注添加到地图中
map.panTo(r.point);
map.addControl(new BMap.GeolocationControl()); //定位控件,针对移动端开发,默认位于地图左下方。
if(r.address){
alert("地址:"+r.address.province+r.address.city+r.address.district+r.address.street+r.address.street_number)
}
//console.log('您的位置:' + r.point.lng + ',' + r.point.lat);
} else {
console.log('failed' + this.getStatus());
}
}, {
enableHighAccuracy: true
});
//关于状态码
//BMAP_STATUS_SUCCESS 检索成功。对应数值“0”。
//BMAP_STATUS_CITY_LIST 城市列表。对应数值“1”。
//BMAP_STATUS_UNKNOWN_LOCATION 位置结果未知。对应数值“2”。
//BMAP_STATUS_UNKNOWN_ROUTE 导航结果未知。对应数值“3”。
//BMAP_STATUS_INVALID_KEY 非法密钥。对应数值“4”。
//BMAP_STATUS_INVALID_REQUEST 非法请求。对应数值“5”。
//BMAP_STATUS_PERMISSION_DENIED 没有权限。对应数值“6”。(自 1.1 新增)
//BMAP_STATUS_SERVICE_UNAVAILABLE 服务不可用。对应数值“7”。(自 1.1 新增)
//BMAP_STATUS_TIMEOUT 超时。对应数值“8”。(自 1.1 新增)
</script>
</body>
</html>

腾讯地图源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="email=no" />
<meta name="apple-touch-fullscreen" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1, user-scalable=no">
<title>腾讯地图</title>
<meta name="keywords" content="腾讯地图">
<meta name="description" content="腾讯地图">
<style type="text/css">
*{margin:0;padding:0;}
html,body{height:100%;}
.map{display:block;width:calc(100% - 20px);margin:10px auto;height:calc(100% - 20px);}
</style>
</head>
<body>

<div id="map" class="map"></div>

<script charset="utf-8" src="http://map.qq.com/api/js?v=2.exp&key=QFUBZ-BFPAR-RYOWE-WGKR7-TUEOK-CWF3N"></script>
<script type="text/javascript">
if(window.navigator.geolocation){
browserPostion();
} else {
alert("您的浏览器不支持定位");

}
function browserPostion(){
var options = {
enableHighAccuracy:true, //它将告诉浏览器是否启用高精度设备。
maximumAge:600000, //超时,获取位置信息时超出设定的这个时长,将会触发错误,捕获错误的函数将被调用,并且错误码指向TIMEOUT。
timeout:60000 //缓存时间 缓存时间过后浏览器会再次调用getCurrentPosition方法。
};
//谷歌地图调用
window.navigator.geolocation.getCurrentPosition(getPositionSuccess, getPositionError, options);
}
function getPositionSuccess(position){
var lat = position.coords.latitude; //获取用户位置的纬度
var lng = position.coords.longitude; //获取用户位置的经度
var map = new qq.maps.Map(document.getElementById("map"),{
center: new qq.maps.LatLng(39.914850, 116.403765),//默认坐标
zoom: 13,//设置地图的缩放级别
draggable: true,
scrollwheel: true,
disableDoubleClickZoom: true
});

var info = new qq.maps.InfoWindow({map: map});
var geocoder = new qq.maps.Geocoder({
complete : function(result){
map.setCenter(result.detail.location);
var marker = new qq.maps.Marker({
map:map,
position: result.detail.location
});
//添加监听事件 当标记被点击了 设置图层
qq.maps.event.addListener(marker, 'click', function() {
info.open();
info.setContent('<div style="width:200px;height:40px;">'+
result.detail.address+'</div>');
info.setPosition(result.detail.location);
});
alert('地址为:'+result.detail.address);
}
});
var center = new qq.maps.LatLng(lat,lng);
geocoder.getAddress(center);
}
function getPositionError(error) {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log("用户不允许地理定位");
break;
case error.POSITION_UNAVAILABLE:
console.log("无法获取当前位置");
break;
case error.TIMEOUT:
console.log("操作超时");
break;
case error.UNKNOWN_ERROR:
console.log("发生未知错误");
break;
}
}
</script>
</body>
</html>

谷歌地图源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="email=no" />
<meta name="apple-touch-fullscreen" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1, user-scalable=no">
<title>谷歌地图</title>
<meta name="keywords" content="谷歌地图">
<meta name="description" content="谷歌地图">
<style type="text/css">
*{margin:0;padding:0;}
html,body{height:100%;}
.map{display:block;width:calc(100% - 20px);margin:10px auto;height:calc(100% - 20px);}
</style>
</head>
<body>

<div id="map" class="map"></div>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>

<script type="text/javascript">
if(window.navigator.geolocation){
browserPostion();
} else {
alert("您的浏览器不支持定位");
}
function browserPostion(){
var options = {
enableHighAccuracy:true, //它将告诉浏览器是否启用高精度设备。
maximumAge:600000, //超时,获取位置信息时超出设定的这个时长,将会触发错误,捕获错误的函数将被调用,并且错误码指向TIMEOUT。
timeout:60000 //缓存时间 缓存时间过后浏览器会再次调用getCurrentPosition方法。
};
//谷歌地图调用
window.navigator.geolocation.getCurrentPosition(getPositionSuccess, getPositionError, options);
}
function getPositionSuccess(position){
var lat = position.coords.latitude;
var lon = position.coords.longitude;
var latlon = new google.maps.LatLng(lat, lon)
var mapholder = document.getElementById('map');
var myOptions = {
center: latlon,
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: false,
navigationControlOptions: {
style: google.maps.NavigationControlStyle.SMALL
}
};
var map = new google.maps.Map(mapholder, myOptions);
var marker = new google.maps.Marker({
position: latlon,
map: map,
title: "你在这里"
});
}
function getPositionError(error) {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log("用户不允许地理定位");
break;
case error.POSITION_UNAVAILABLE:
console.log("无法获取当前位置");
break;
case error.TIMEOUT:
console.log("操作超时");
break;
case error.UNKNOWN_ERROR:
console.log("发生未知错误");
break;
}
}
</script>
</body>
</html>

高德地图源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<!DOCTYPE html>
<html lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="email=no" />
<meta name="apple-touch-fullscreen" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1, user-scalable=no">
<title>高德地图</title>
<meta name="keywords" content="高德地图">
<meta name="description" content="高德地图">
<style type="text/css">
*{margin:0;padding:0;}
html,body{height:100%;}
.map{display:block;width:calc(100% - 20px);margin:10px auto;height:calc(100% - 20px);}
</style>
</head>
<body>

<div id="map" class="map"></div>

<script language="javascript" src="http://webapi.amap.com/maps?v=1.3&key=e8496e8ac4b0f01100b98da5bde96597"></script>
<script type="text/javascript">
var map = new AMap.Map("map", {
view: new AMap.View2D({
center: new AMap.LngLat(116.397428, 39.90923),
zoom: 13
})
});

map.plugin(["AMap.MapType"], function() {
var type = new AMap.MapType({
defaultType: 0
});
map.addControl(type);
});
map.plugin(["AMap.ToolBar"], function() {
toolBar = new AMap.ToolBar();
map.addControl(toolBar);
});

var geolocation = null;
map.plugin('AMap.Geolocation', function() {
geolocation = new AMap.Geolocation({
enableHighAccuracy: true, //是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:无穷大
maximumAge: 0, //定位结果缓存0毫秒,默认:0
convert: true, //自动偏移坐标,偏移后的坐标为高德坐标,默认:true
showButton: true, //显示定位按钮,默认:true
buttonPosition: 'LB', //定位按钮停靠位置,默认:'LB',左下角
buttonOffset: new AMap.Pixel(10, 20), //定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
showMarker: true, //定位成功后在定位到的位置显示点标记,默认:true
showCircle: true, //定位成功后用圆圈表示定位精度范围,默认:true
panToLocation: true, //定位成功后将定位到的位置作为地图中心点,默认:true
zoomToAccuracy: true, //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
});
map.addControl(geolocation);
AMap.event.addListener(geolocation, 'complete', onComplete); //返回定位信息
AMap.event.addListener(geolocation, 'error', onError); //返回定位出错信息
var marker = new AMap.Marker({
map: map,
bubble: true
})
});
getCurrentPosition();

/*
*获取当前位置信息
*/
function getCurrentPosition() {
geolocation.getCurrentPosition();
};

/*
*监控当前位置并获取当前位置信息
*/
function watchPosition() {
geolocation.watchPosition();
};

function onComplete(data) {
var str = '<p>定位成功</p>';
str += '<p>经度:' + data.position.getLng() + '</p>';
str += '<p>纬度:' + data.position.getLat() + '</p>';
str += '<p>精度:' + data.accuracy + ' 米</p>';
str += '<p>是否经过偏移:' + (data.isConverted ? '是' : '否') + '</p>';
//console.log(str);
};

/*
*解析定位错误信息
*/
function onError(data) {
var str = '<p>定位失败</p>';
str += '<p>错误信息:'
switch (data.info) {
case 'PERMISSION_DENIED':
str += '浏览器阻止了定位操作';
break;
case 'POSITION_UNAVAILBLE':
str += '无法获得当前位置';
break;
case 'TIMEOUT':
str += '定位超时';
break;
default:
str += '未知错误';
break;
}
str += '</p>';
console.log(str);
};
</script>
</body>
</html>

注:以上地图文件引入可能需要一个开发者key,具体参考官网的API

浏览器支持情况

参考链接

  1. w3schoolHTML5 地理定位
  2. HTML5 Geolocation 构建基于地理位置的 Web 应用
  3. 冰点-html5 Geolocation 地理定位
  4. MDN-使用地理位置定位
  5. Introduction to the Geolocation API
  6. 地理定位API规范-en-us
  7. 百度地图JavaScript API大众版
  8. 腾讯地图JavaScript API V2
  9. geolocation the Google地图API
  10. 高德地图 JavaScript API