之前寫過一篇微信公眾平台里基於地理位置顯示商戶的文章,很多朋友加了我的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>
可以看到其中有元素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語句做一個排序
其中這個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
