由来
一个客户提出一个需求,想在xiunobbs中实现,发帖时显示 定位信息,然后在查看时 可以实现,如果是Android或IOS端安装了百度地图APP的可以唤起,否则就和网页PC端的一样,直接跳转到网页版的百度地图并定位到改点。
简单实现步骤:
- 发帖时增加按钮,可以选择是否获取定位
- 使用HTML5 自带的api来获取 https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation/Using_geolocation
- 将获取到的经纬度数值 通过 百度JavaScript API来逆向解析,得到省、市、县、街道等信息。
- 查看帖子时,将经纬度再次拼接成URI,供客户端唤起服务。
大致步骤如上吧,具体如何制作的xiunobbs插件这里就不谈了。
geolocation对象
Geolocation:https://developer.mozilla.org/zh-CN/docs/Web/API/Geolocation
该对象有三个方法:
-
Geolocation.getCurrentPosition()
确定设备的位置并返回一个携带位置信息的
Position
对象。 -
注册一个位置改变监听器,每当设备位置改变时,返回一个
long
类型的该监听器的ID值。 -
取消由
watchPosition()注册的位置监听器。
增加按钮
这个不是本篇主要内容,也就一笔带过吧。
hook了post_message_after.htm 这个位置加入了
<div class="form-group">
<button class="btn-primary get-location">使用位置</button><span class="ml-2 badge badge-pill badge-secondary icon-location-arrow location-str d-none">Secondary</span>
</div>
<input type="hidden" name="location" value="" id="location">
获取当前定位
利用getCurrentPosition()
方法来获取位置得到经纬度。
语法
navigator.geolocation.getCurrentPosition(success, error, options)
参数
success
成功得到位置信息时的回调函数,使用
Position
对象作为唯一的参数。error 可选
获取位置信息失败时的回调函数,使用
PositionError
对象作为唯一的参数,这是一个可选项。options 可选
一个可选的
PositionOptions
对象。
于是简单写了下代码
$(".get-location").click(function () {
// $(this).preventDefault();
var options = {
enableHighAccuracy : true,
maximumAge : 1000
};
// alert('this is getLocation()');
if (navigator.geolocation) {
//浏览器支持geolocation
console.log('support');
$(".location-str").removeClass("d-none");
$(".location-str").text("正在获取中……")
navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
} else {
//浏览器不支持geolocation
alert('您的浏览器不支持地理位置定位');
}
return false;
});
onSuccess和onError分别如下
function onSuccess(position) {
// 用户位置字符串
var locationStr = "";
//返回用户位置
//经度
var longitude = position.coords.longitude;
//纬度
var latitude = position.coords.latitude;
$(".location-str").text('当前地址的经纬度:经度' + longitude + ',纬度' + latitude);
}
function onError(error) {
switch (error.code) {
case 1:
alert("位置服务被拒绝");
break;
case 2:
alert("暂时获取不到位置信息");
break;
case 3:
alert("获取信息超时");
break;
case 4:
alert("未知错误");
break;
}
}
这样简单的就能获取到经纬度了。
结合百度地图逆向解析
JavaScript API:http://lbsyun.baidu.com/index.php?title=jspopular/guide/geocoding
API v2.0类参考:http://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference.html
它给出的示例代码如下
var map = new BMap.Map("l-map"); // 这个是显示的div class类名 如果不显示无需写
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);
// 创建地理编码实例
var myGeo = new BMap.Geocoder();
// 根据坐标得到地址描述
myGeo.getLocation(new BMap.Point(116.364, 39.993), function(result){
if (result){
alert(result.address);
}
});
完善一下之前的onSuccess函数
function onSuccess(position) {
// 用户位置字符串
var locationStr = "";
//返回用户位置
//经度
var longitude = position.coords.longitude;
//纬度
var latitude = position.coords.latitude;
$(".location-str").text('当前地址的经纬度:经度' + longitude + ',纬度' + latitude);
var point = new BMap.Point(longitude, latitude);
var gc = new BMap.Geocoder();
gc.getLocation(point, function(rs) {
locationStr = rs.address;
$(".location-str").text(locationStr);
$("#location").val(locationStr + "|" + longitude + "|" + latitude);
console.log($("#location").val());
});
}
将之前获取到的经纬度,传参进入,调用这个方法,然后拿到解析后的address地址内容。将id为location的这个内容赋值为address字符串内容。(这个是为了提交到后端,下次读取帖子时可以拿到。PS:这里得注意下XSS)
显示效果:
(PC端不准,还有其他因素,结果定位到洛杉矶了):)
点击唤起百度地图APP
简单的定位也拿到了,然后该显示了。
hook显示位
这里还是简单带过下显示的地方,hook了thread_plugin_before.htm这个点。
<?php if($first['location']!=''){
list($location_str,$lng,$lat) = explode('|',$first['location']);
// 网页版
$baidu_pc_map_url = 'http://api.map.baidu.com/geocoder?location='.$lat.','.$lng.'&output=html&src=webapp.baidu.openAPIdemo';
// 安卓端
$android_url = 'bdapp://map/geocoder?location='.$lat.','.$lng.'&src=andr.baidu.openAPIdemo';
// IOS端
$ios_url = 'baidumap://map/geocoder?location='.$lat.','.$lng.'&src=ios.baidu.openAPIdemo';
?>
<div>
<span class="d-inline-block badge badge-pill badge-secondary icon-location-arrow location-str d-none"><a
href="<?php echo $baidu_pc_map_url;?>" style="text-decoration: none;color: #fff;" id="location_url"><?php echo $location_str;?></a></span>
</div>
<?php } ?>
下面的js操作是hook的thread_js.htm。
唤起应用
百度地图调起:http://lbsyun.baidu.com/index.php?title=uri
唤起的逻辑简单如下:
- PC端:直接显示百度地图web地址
- Mobile端:
- Android端:Android的百度地图APP
- IOS|iPhone|iPad端:iOS的百度地图APP
利用uri的反向地址解析来完成,从手册中可以查到三个分别是
-
http://api.map.baidu.com/geocoder
网页 -
bdapp://map/geocoder?location
安卓 -
baidumap://map/geocoder?location
IOS
然后就是利用JavaScript来判断下:
<script>
var browser={
versions:function(){
var u = navigator.userAgent, app = navigator.appVersion;
return {//移动终端浏览器版本信息
trident: u.indexOf('Trident') > -1, //IE内核
presto: u.indexOf('Presto') > -1, //opera内核
webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
iPad: u.indexOf('iPad') > -1, //是否iPad
webApp: u.indexOf('Safari') == -1 //是否web应该程序,没有头部与底部
};
}(),
language:(navigator.browserLanguage || navigator.language).toLowerCase()
}
$("#location_url").click(function (event) {
event.preventDefault();
var web_scheme = "<?php echo $baidu_pc_map_url;?>";
var scheme = '';
if(browser.versions.mobile || browser.versions.ios || browser.versions.android ||
browser.versions.iPhone || browser.versions.iPad){
console.log('mobile');
if (browser.versions.ios || browser.versions.iPhone || browser.versions.iPad) {
scheme = "<?php echo $ios_url;?>";
}else if (browser.versions.android) {
scheme = "<?php echo $android_url;?>";
}
}else{
scheme = "<?php echo $baidu_pc_map_url;?>";
console.log('pc',scheme);
}
// 轮询判断能否唤起,如果不能唤起 则使用网页版
window.location.href = scheme;
var startTime = Date.now();
var count = 0;
var endTime = 0;
var t = setInterval(function () {
count += 1;
endTime = Date.now() - startTime;
if (endTime > 800) {
clearInterval(t);
}
if (count < 30) return;
if (!(document.hidden || document.webkitHidden)) {
window.location.href = web_scheme;
}
}, 20);
})
</script>
然后就可以唤起应用并显示标注了地图上的点。