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 设备| |用户自定义|可获得比程序定位服务更准确的位置数据,用户自行输入可能比自动检测更快|可能很不准确,特别是当用户位置变更后|
推荐: 可见它们各有优缺点,为了保证更高的精确度,许多设备使用多个数据源组合的方式。
地理位置获取流程
用户打开需要获取地理位置的web应用
应用通过Geolocation API 向浏览器请求地理位置,浏览器拦截请求,向用户请求授权。
假设用户允许,浏览器从设备查询相关信息,如IP地址、WIFI地址或GPS坐标。
浏览器将相关信息发送给信任的外部定位服务器,服务器返回具体的地理位置。
隐私 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个参数分别是:
successCallback – 为浏览器成功获得位置信息后的回调函数。
errorCallback – 用于位置信息获取失败时的回调函数。
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); // 获取位置的时间 }
其中altitude 、altitudeAccuracy 、heading 和speed 则根据用户的终端不同,可能提供,可能为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
参考链接
w3schoolHTML5 地理定位
HTML5 Geolocation 构建基于地理位置的 Web 应用
冰点-html5 Geolocation 地理定位
MDN-使用地理位置定位
Introduction to the Geolocation API
地理定位API规范-en-us
百度地图JavaScript API大众版
腾讯地图JavaScript API V2
geolocation the Google地图API
高德地图 JavaScript API