天天看點

Thinkphp5(tp)在資料庫中根據經緯度搜尋,并按距離排序實踐過程

場景:後端需要根據使用者所在位置,擷取距離使用者附近的店鋪清單,并按距離遠近排序
           

在網上一番搜尋後,找到了這篇文章TP5 根據經緯度計算距離,排序+分頁,經過測試,總結出如下的代碼

PS:該方法擷取到的機關是米
/**
   * 在資料庫中擷取所有店鋪,按距離使用者的遠近排序(預設升序
   * 距離機關是米
   * @param double $lat 緯度
   * @param double $lng 經度
   * @param string $order 排序方式(asc/升序,desc/降序
   * @return array 傳回查詢的資料
   */
  public function getDistanceByLatLng($lat, $lng, $order = 'asc')
  {
    // 資料庫表名
    $database_name = 'test_table';
    // 資料庫字段名 - 緯度
    $field_lat = 'lat';
    // 資料庫字段名 - 經度
    $field_lng = 'lng';
    return Db::table($database_name)
      ->field("*, (6378.138 * 2 * asin(sqrt(pow(sin(({$field_lat} * pi() / 180 - {$lat} * pi() / 180) / 2),2) + cos({$field_lat} * pi() / 180) * cos({$lat} * pi() / 180) * pow(sin(({$field_lng} * pi() / 180 - {$lng} * pi() / 180) / 2),2))) * 1000) as distance")
      // 按距離升序排列(由近到遠
      ->order("distance {$order}")
      ->select();
  }
           

實踐過程

首先在資料庫中建立表,結構如下

資料表名:

test_table

類型 備注
id int(255)
address varchar(255) 位址
lat double 緯度
lng double 經度
remark varchar(255) 備注

填充資料

以下資料已上傳到CSDN

id address lat lng remark
1 河南省鄭州市金水區農業快速路河南博物館 34.787258 113.672303
2 河南省鄭州市金水區文博東路文博公園 34.787831 113.674265 在河南博物館旁邊,距離河南博物館很近
3 河南省鄭州市二七區二七路97号鄭州市人民公園 34.761103 113.662795
4 河南省洛陽市洛龍區通衢路錦台廣場 34.596366 112.467488
5 山東省德州市德城區新河路萬達廣場 37.429004 116.31163
6 新疆維吾爾自治區烏魯木齊市沙依巴克區紫金公園 43.761815 87.458698

開始測試

// 使用者1的位置(河南省鄭州市金水區農業路河南省農業科學院
$lat = 34.788179;
$lng = 113.679335;

// 調用文章開頭的方法
$result = $this->getDistanceByLatLng($lat, $lng);
halt($result);
           
Thinkphp5(tp)在資料庫中根據經緯度搜尋,并按距離排序實踐過程

由圖可知,使用者1的左邊就是資料庫中的

文博公園

,再左邊就是資料庫中的

河南博物館

,是以列印結果中文博公園的位置應該比河南博物館的位置靠前,來看一下

Thinkphp5(tp)在資料庫中根據經緯度搜尋,并按距離排序實踐過程

運作結果和預期一樣,接下來再測試一次

假設使用者2在新疆,那麼列印結果中資料庫中新疆的位置應該會靠前,看代碼和結果

// 使用者2的位置(新疆維吾爾自治區阿克蘇地區阿克蘇市幸福中路幸福公園
$lat = 41.165831;
$lng = 80.278014;

$result = $this->getDistanceByLatLng($lat, $lng);
halt($result);
           
Thinkphp5(tp)在資料庫中根據經緯度搜尋,并按距離排序實踐過程