PHP,Mysql-根據一個給定經緯度的點,進行附近地點查詢–算法


  幾個星期以前的一個項目,需求是根據當前用戶上傳的經緯度坐標,在數據庫幾十萬萬條數據中查詢出符合“周圍3公里范圍內”條件的坐標點。

  Mysql本身是支持空間索引的,但是在5.X版本中取消了Distance()和Related(),無法使用空間的距離函數去直接查詢距離在一定范圍內的點。所以,我首先想到的是,對每條數據去進行遍歷,跟數據庫中的每個點進行距離計算,當距離小於3公里時候,認為匹配成功。經測試,這樣做確實能得到結果,但是效率極其低下,因為每條數據都得去和數據庫中的幾十萬條數據進行比對,其耗費的時間可想而知。對於這種情況,是用戶所無法忍受的。

  后來經過自己的仔細考慮,以及查詢各種資料,想到一種利用正方形將方圓3公里這個圓包圍起來。利用正方形的四個點,去和用戶上傳的經緯度進行比較。由此,問題轉向了,如何計算正方形四個點經緯度的問題!

  無意中看到一個附近地點搜索初探的帖子,里面使用python實現了計算四個點經緯度的方法。由此,我將其用PHP的方式實現了。

  其實現原理也是很相似的,先計算出當前點周圍的正方形的四個點,然后使用經緯度直接去數據庫匹配數據。

  假設已知點的經緯度分別為$lng, $lat
  先實現經度范圍的查詢,
  在haversin公式中令φ1 = φ2,可得:
      
     用PHP實現的整體方式,就是:

 1     /**
 2      * 計算某個經緯度的周圍某段距離的正方形的四個點
 3      *
 4      * @param
 5      *            radius 地球半徑 平均6371km
 6      * @param
 7      *            lng float 經度
 8      * @param
 9      *            lat float 緯度
10      * @param
11      *            distance float 該點所在圓的半徑,該圓與此正方形內切,默認值為1千米
12      * @return array 正方形的四個點的經緯度坐標
13      */
14     public function returnSquarePoint($lng, $lat, $distance = 1, $radius = 6371)
15     {
16         $dlng = 2 * asin(sin($distance / (2 * $radius)) / cos(deg2rad($lat)));
17         $dlng = rad2deg($dlng);
18         
19         $dlat = $distance / $radius;
20         $dlat = rad2deg($dlat);
21         
22         return array(
23             'left-top' => array(
24                 'lat' => $lat + $dlat,
25                 'lng' => $lng - $dlng
26             ),
27             'right-top' => array(
28                 'lat' => $lat + $dlat,
29                 'lng' => $lng + $dlng
30             ),
31             'left-bottom' => array(
32                 'lat' => $lat - $dlat,
33                 'lng' => $lng - $dlng
34             ),
35             'right-bottom' => array(
36                 'lat' => $lat - $dlat,
37                 'lng' => $lng + $dlng
38             )
39         );
40     }

匹配路線時候就可以采取一下辦法(截取當時寫的方法,大家理解就好)

$array[0]就是用戶上傳的起點終點坐標數組 
1
$start = $this->returnSquarePoint($array[0]['start_lng'], $array[0]['start_lat']);
下面是匹配方法,只是代碼截取,請諒解!
2 ->andwhere([ 3 '>', 4 'start_lat', 5 $start['right-bottom']['lat'] 6 ]) 7 ->andWhere([ 8 '<', 9 'start_lat', 10 11 $start['left-top']['lat'] 12 ]) 13 ->andWhere([ 14 '>', 15 'start_lng', 16 $start['left-top']['lng'] 17 ]) 18 ->andWhere([ 19 '<', 20 'start_lng', 21 $start['right-bottom']['lng'] 22 ]);

在lat和lng上建立一個聯合索引后,使用此項查詢,運行效率飛漲。

總結:這應該也不是效率最好的辦法,但是效率比以前確實有明顯的提升。大家如果有什么剛好的解決辦法,歡迎留言學習。


免責聲明!

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



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