緣由
本周技術群有一個同學說我們該怎么實現 由近到遠的基於地理位置的搜索,我創業做電商的系統做過類似這樣的服務,我把我們以前的操作給大家分享下
什么是LBS
LBS 全稱是 Location Based Service ,基於位置的服務。我們可以使用到這種服務,真是由於我國移動設備的大量增加,讓我們加速進入了 移動互聯網的時代。
由近到遠的基於地理位置的搜索 其實就是 通過當前使用用戶的經緯度,然后從我們自己的數據庫中查出指定范圍內(例如5km)的數據,按照由近到遠的順序 進行展示。
這句話中有四個條件
用戶的經緯度,我們定義 用戶維度為:$lat 、經度:$lng
指定范圍,定義范圍:distince
數據庫中商家的經緯度字段,定義 維度為:lat,經度:lng
計算 用戶經緯度 與 數據庫中商家的經緯度 距離,由遠及近進行排序
接下來,我們一起來用兩種方案實現
實現方案一:
這種方案會比較挫,
理想模型計算閥值點
計算某個經緯度的周圍某段距離的正方形的四個點
為什么這么計算,我給大家舉個例子,如果我們把我們的地球儀中國區域直接摁平,是不是地球儀就滅有弧度了 ,我們理想方式就是在一定距離上這么計算的。計算代碼如下
/** * php代碼 *計算某個經緯度的周圍某段距離的正方形的四個點 *@param float $lng 經度 *@param float $lat 緯度 *@param float $distance 該點所在圓的半徑,該圓與此正方形內切,默認值為5千米 *@return array 正方形的四個點的經緯度坐標 */ function squarePoint($lat, $lng,$distance = 5.0){ $earth_radius = 6371;//地球半徑 $dlng = 2 * asin(sin($distance / (2 * $earth_radius)) / cos(deg2rad($lat))); $dlng = rad2deg($dlng); $dlat = $distance/$earth_radius; $dlat = rad2deg($dlat); return [ 'left-top' =>['lat'=>$lat + $dlat, 'lng'=>$lng - $dlng], 'right-top' =>['lat'=>$lat + $dlat, 'lng'=>$lng + $dlng], 'left-bottom' =>['lat'=>$lat - $dlat, 'lng'=>$lng - $dlng], 'right-bottom'=>['lat'=>$lat - $dlat, 'lng'=>$lng + $dlng], ]; }
取出用戶指定距離的數據
根據上面的方法,我們計算出來了4個點,接下來我們直接從數據庫取出符合條件的數據
$geo_data = squarePoint($lat,$lng,$distance); $left_bottom = $geo_data['left-bottom']; $right_top = $geo_data['right-top']; $lat_min = $left_bottom['lat']; $lat_max = $right_top['lat']; $lng_min = $left_bottom['lng']; $lng_max = $right_top['lng']; $sql = "SELECT * FROM table_name WHERE lat > {$lat_min} lat < {$lat_max} and lng > {$lng_min} and lng < {$lng_max} ";
按照距離遠近排序
這個將 上面的符合條件的結果集取出來,在代碼中排序,計算 兩個經緯度之間距離的方法如下
/** * * 根據經緯度計算距離 單位(公里) * @param $lng1 float 經度1 * @param $lat1 float 緯度2 * @param $lng2 float 經度1 * @param $lat2 float 緯度2 * @return float */ function getdistance($lng1,$lat1,$lng2,$lat2) { $dx = $lng1 - $lng2; // 經度差值 $dy = $lat1 - $lat2; // 緯度差值 $b = ($lat1 + $lat2) / 2.0; // 平均緯度 $Lx = deg2rad($dx) * cos(deg2rad($b)); // 東西距離 $Ly = deg2rad($dy); // 南北距離 return round(6371*sqrt($Lx * $Lx + $Ly * $Ly),4); // 用平面的矩形對角距離公式計算總距離 }
按照方法一計算出來,基本問題不大,但是在數據量大(第二步結果集)到一定程度了是有很嚴重的效率問題的。這里給出一個衍生版本,直接計算距離
SELECT *,SQRT( POWER( $lat - lat, 2) + POWER($lng - lng, 2) ) AS d FROM table_name WHERE (lat BETWEEN $lat_min AND $lat_max ) AND (lng BETWEEN $lng_min AND $lng_max ) AND d < $distance ORDER BY d ASC LIMIT 10;
實現方案二
這個方法很快,直接可以SQL實現,由於數據庫本身也支持很多函數的,我們直接在數據庫本身計算就可以了。具體計算代碼如下:
/** * $lat:用戶維度 * $lng:用戶精度 * $as_name:查詢出來的SQL字段名稱 */ get_distance_sql($lat,$lng,$as_name='distance') { return sprintf('round(6371*sqrt( pow((PI()*(abs(`lat`-%f))/180) * cos(PI()*(`lat`+%f)/360),2) + pow((PI()*abs(`lng`-%f)/180),2)),4) as %s',$lat,$lat,$lng,$as_name); }
具體SQL如下:
/** * shop_id 商家id */ $sql = "SELECT shop_id,lat,lng,".get_distance_sql( $lat,$lng)." FROM table_name WHERE distance< = ".$distance." ORDER BY distance ASC" ;
這樣就可以直接查出結果並排序了
結論
以上兩種方案中,第二個是我我們當時使用的,我們當時數據庫幾十萬,效率還可以。上百萬乃至更高的需要大家去實現了
原文地址: 【LBS】基於地理位置的搜索之微信 附近的人 簡單實現
標簽: lbs 附近的人 由遠及近 大眾點評