【LBS】基於地理位置的搜索之微信 附近的人 簡單實現


緣由

本周技術群有一個同學說我們該怎么實現 由近到遠的基於地理位置的搜索,我創業做電商的系統做過類似這樣的服務,我把我們以前的操作給大家分享下


什么是LBS

LBS 全稱是 Location  Based Service ,基於位置的服務。我們可以使用到這種服務,真是由於我國移動設備的大量增加,讓我們加速進入了 移動互聯網的時代。


由近到遠的基於地理位置的搜索  其實就是 通過當前使用用戶的經緯度,然后從我們自己的數據庫中查出指定范圍內(例如5km)的數據,按照由近到遠的順序 進行展示。

這句話中有四個條件

  • 用戶的經緯度,我們定義 用戶維度為:$lat 、經度:$lng

  • 指定范圍,定義范圍:distince

  • 數據庫中商家的經緯度字段,定義 維度為:lat,經度:lng

  • 計算 用戶經緯度 與 數據庫中商家的經緯度 距離,由遠及近進行排序


接下來,我們一起來用兩種方案實現


實現方案一:

這種方案會比較挫,


理想模型計算閥值點

計算某個經緯度的周圍某段距離的正方形的四個點


600


為什么這么計算,我給大家舉個例子,如果我們把我們的地球儀中國區域直接摁平,是不是地球儀就滅有弧度了 ,我們理想方式就是在一定距離上這么計算的。計算代碼如下


/**
 * 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    附近的人    由遠及近    大眾點評   

智能推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM