您的位置:首页 > 移动开发 > 微信开发

基于微信地理位置的附近商家距离坐标数据查询方法

2016-05-19 17:40 706 查看
之前写过一篇微信公众平台里基于地理位置显示商户的文章,很多朋友加了我的QQ,探讨具体的功能细节。

这阵子我刚好有一点时间,特意写了一篇文档分享这方面的东西。

要解决的问题:

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>


可以看到其中有元素LatitudeLongitude,获取到了坐标就好说啦

方式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语句做一个排序

其中这个sql语句当中有个两个坐标之间的计算公式

公式如下

function rad($d)
{
return $d * 3.1415926535898 / 180.0;
}
function GetDistance($lat1, $lng1, $lat2, $lng2)
{
$EARTH_RADIUS = 6378.137;
$radLat1 = rad($lat1);
$radLat2 = rad($lat2);
$a = $radLat1 - $radLat2;
$b = rad($lng1) - rad($lng2);
$s = 2 * asin(sqrt(pow(sin($a/2),2) +
cos($radLat1)*cos($radLat2)*pow(sin($b/2),2)));
$s = $s *$EARTH_RADIUS;
$s = round($s * 10000) / 10000;
$s=$s*1000;
return ceil($s);
}


当前使用GetDistance方法只要你带入两个坐标点就可以计算出这两个坐标的的距离,距离精度是以米为单位的

使用这种方法如果用编程语言编写的话,数据计算非常耗时

我们需要把数据记录全部查询出,然后在内存中通过自己的方法逐条对全部查询出数据集做过滤,并且计算出一个距离字段,然后再做一次排序才会得到想要的结果

虽然这种方法思考逻辑上简单容易实现,但是耗时多了。

我跟倾向于用下面的方法来实现,那就是借助于SQL语句,通过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= '距离我【<font color="#FF4C06"><b>'.$v.'</b></font>】千米内。';}
else{
$v= '距离我【<font color="#FF4C06"><b>'.$v.'</b></font>】米内。';
}
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;

}




用户地理位置模式的显示就到此结束了

如果有不明白的,麻烦加QQ:187395037
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: