要解决的问题:
1.通过微信公众平台获取用户地理位置(也就是坐标啦,精度和纬度)
2.获取到地理位置后,记录用户的坐标
3.计算出当前的用户坐标和数据表里商户的坐标的距离
4.距离排序与距离的用户体验显示
好吧,现在我们开始具体的细节问题解决
1.通过微信公众平台获取用户地理位置
通过微信获取用户地理位置有两种方式
方式a:此方式必须是服务号,用户进入微信公众号的时候,会向微信服务端推送关于用户的相关信息,如果你设置了微信开发者模式,那么这些信息能接收到用户的相关信息
用户同意上报地理位置后,每次进入公众号会话时,都会在进入时上报地理位置,上报地理位置以推送XML数据包到开发者填写的URL来实现。
通过这种方式,我们的服务端接收到的信息格式是这样的
提示:只有服务号才能获取用户地理位置,并且服务号经过认证,并且需要在开发者的接口权限中去【开启】
<xml> <ToUserName><![CDATA[gh_f6bce85ce621]]></ToUserName> <FromUserName><![CDATA[obxLljpChQwixH0mAZYR1ESeWv3Y]]></FromUserName> <CreateTime>1460636400</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[LOCATION]]></Event>
<Latitude>30.660822</Latitude>
<Longitude>104.066566</Longitude> <Precision>65.000000</Precision> </xml>
可以看到其中有元素Latitude和Longitude,获取到了坐标就好说啦
方式b:用户访问我们的微网站的时候,通过微信的JS-SDK模式获取用户的地理位置(服务号订阅号都可以)
什么是JS-SDK呢?
微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。
微信官方描述:通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验
这种方式获取到用户坐标是基于网页的形式获得的,所以用户的地理位置坐标需要通过异步的模式存储到你自己的系统中
这个是通过JS-SDK的部分代码
wx.getLocation({
type: "wgs84", // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
success: function (res) {
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
}
})
2.获取到地理位置后,记录用户的坐标
用户的坐标获取到后,自行记录到你的系统里,通过缓存也好,session也好,cookie也好,还是数据表也好随便你
3.计算出当前的用户坐标和数据表里商户的坐标的距离
我们目前来个假设一个获取到坐标
例如我的坐标是:30.664385188806,104.07559730274
表名:merchants
表字段:itemid,title,hits,lat,lng lat是经度 lat是纬度
通过SQL语句的运算把距离和排序一次性解决。
mysql函数计算坐标距离
其中30.664385188806是你的经度,104.07559730274是你的纬度
以下SQL语句是全部查询并运算出坐标的的语句
select itemid,title,hits,telephone,ROUND(6378.138*2*ASIN(SQRT(POW(SIN((30.664385188806*PI()/180-lat*PI()/180)/2),2)+COS(30.664385188806*PI()/180)*COS(lat*PI()/180)*POW(SIN((104.07559730274*PI()/180-lng*PI()/180)/2),2)))*1000) AS distance from merchants order by distance
通过如下方式的SQL运行就可查询出相应的距离+排序+多少公里范围的条件检索
下面的检索出5公里范围的语句
select * from (select itemid,title,hits,telephone, ROUND(6378.138*2*ASIN(SQRT(POW(SIN((30.664385188806*PI()/180-lat*PI()/180)/2),2)+COS(30.664385188806*PI()/180)*COS(lat*PI()/180)*POW(SIN((104.07559730274*PI()/180-lng*PI()/180)/2),2)))*1000) AS distance from merchants order by distance ) as a where a.distance<=5000
查询结果见下图
4.距离排序与距离的用户体验显示
查询计算出的distance是数字,需要显示的用户体验更好一点
例如:我和这个商家的精确距离是1290米,因为精度的原因,其实精确距离其实偏差非常大,不能显示一个具体的数字 ,所以要优化显示为1.3公里或者1.5公里内的模式更好
我自用的方法是这样的
A方式
//输入distance,然后对数字做优化显示
function mToKm($number){ if(!is_numeric($number)) return ""; switch ($number){ case $number>1800&&$number<=2000: $v="2"; break; case $number>1500&&$number<=1800: $v="1.8"; break; case $number>1200&&$number<=1500: $v="1.5"; break; case $number>1000&&$number<=1200: $v="1.2"; break; case $number>900&&$number<=1000: $v="1"; break; default: $v=ceil($number/100)*100; break; } if($v<100){ $v= "距离我【".$v."】千米内。"; } else{ $v= "距离我【".$v."】米内。"; } return $v; }
B方式
function distanceDesc($number){ if(!is_numeric($number)) return ""; switch ($number){ case $number>3000&&$number<=5000: $v="5"; break; case $number>2000&&$number<=3000: $v="3"; break; case $number>1000&&$number<=2000: $v="2"; break; case $number>500&&$number<=1000: $v="1"; break; case $number<=500: $v="0.5"; break; default: $v=ceil($number/1000); break; } if($number<=300) { $distance = "【500米内】"; }else{ $distance = "【".$v."千米内】"; } return $distance; }