[TOC] #### 1. 前言 --- 想要測試本文提供的幾個功能函數(shù),可以使用下面這個數(shù)據(jù)表結構及其數(shù)據(jù) ```sql CREATE TABLE `user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用戶id', `name` varchar(60) DEFAULT NULL COMMENT '昵稱', `longitude` varchar(64) DEFAULT NULL COMMENT '經度', `latitude` varchar(64) DEFAULT NULL COMMENT '緯度', `remark` varchar(50) DEFAULT NULL COMMENT '備注', `distance` varchar(20) DEFAULT NULL COMMENT '距離', PRIMARY KEY (`id`) ) ENGINE=InnoDB COMMENT='用戶表'; INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('中海九號公館', '113.899529', '22.60063', '深圳市寶安區(qū)中海九號公館', '3.66km'); INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('平巒山公園', '113.876462', '22.608322', '深圳市寶安區(qū)平巒山公園', '2.88km'); INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('鐵仔山公園', '113.86359', '22.592355', '深圳市寶安區(qū)鐵仔山公園', '1.16km'); INSERT INTO `user` (`name`, `longitude`, `latitude`, `remark`, `distance`) VALUES ('寶安公園', '113.902671', '22.58621', '深圳市寶安區(qū)寶安公園', '3.45km'); ``` 本文內容測試各個功能函數(shù)時,使用的當前位置坐標均為: ```php // 深圳市寶安區(qū)西鄉(xiāng)街道九方廣場 $longitude = '113.869205';//經度 $latitude = '22.583286';//緯度 ``` #### 2. 計算經緯度坐標間的距離 --- `計算經緯度坐標間的距離` 功能函數(shù) (前四個參數(shù)為兩組經緯度坐標) ```php /** * 計算經緯度坐標間的距離 * @param $lng1 經度 * @param $lat1 緯度 * @param $lng2 經度 * @param $lat2 緯度 * @param $lang 語言 */ function get_distance($lng1, $lat1, $lng2, $lat2, $lang = 'en') { // 地球的近似半徑(單位:米) $earthRadius = 6367000; // 將這些度數(shù)轉換為弧度以使用公式 $lat1 = ($lat1 * pi()) / 180; $lng1 = ($lng1 * pi()) / 180; $lat2 = ($lat2 * pi()) / 180; $lng2 = ($lng2 * pi()) / 180; // 使用 Haversine 公示計算距離 // http://en.wikipedia.org/wiki/Haversine_formula $calcLongitude = $lng2 - $lng1; $calcLatitude = $lat2 - $lat1; $stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2); $stepTwo = 2 * asin(min(1, sqrt($stepOne))); // 兩個經緯度坐標的距離(單位: 米) $calculatedDistance = round($earthRadius * $stepTwo); // 距離單位 $language = [ 'en' => ['m' => 'm', 'km' => 'km'], 'cn' => ['m' => '米', 'km' => '公里'], ]; if (!isset($language[$lang])) throw new \Exception('不支持的語言:' . $lang); foreach ($language[$lang] as $key => $value) $$key = $value; // 兩個坐標間的距離,單位:米 $distance = round($calculatedDistance); // 距離單位轉換:超出 1000m 時單位轉為km if ($distance < 1000) { $distance .= $m; } else { $distance = floatval(number_format($distance / 1000, 2)) . $km; } return $distance; // 返回單位轉換后的距離 } ``` **使用示例:** 我在 `九方廣場`,手機上的高德地圖導航至 `中海九號公館` 顯示的距離為 `3.6公里`,計算結果還是很準確的 ```php // 深圳市寶安區(qū)西鄉(xiāng)街道九方廣場: 113.869205, 22.583286 // 深圳市寶安區(qū)西鄉(xiāng)街道中海九號公館: 113.899529, 22.60063 $distance = get_distance(113.869205, 22.583286, 113.899529, 22.60063); echo $distance; //3.66km ``` #### 3. 根據(jù)經緯度坐標距離排序 --- 項目中經常有距離顯示數(shù)據(jù)的場景,根據(jù)距離排序,越近越靠前顯示;比如: 店鋪地址、房源信息等。代碼示例: ```php // 當前坐標 $longitude = '113.869205'; $latitude = '22.583286'; // 數(shù)據(jù)庫中經緯度字段分別為:longitude、latitude $field = '*,( 2 * 6378.137 * ASIN( SQRT( POW( SIN( PI() * (' . $longitude . ' - longitude) / 360 ), 2 ) + COS(PI() * ' . $latitude . ' / 180) * COS(latitude * PI() / 180) * POW( SIN( PI() * (' . $latitude . ' - latitude) / 360 ), 2 ) ) ) ) AS juli'; // 根據(jù)距離升序查詢(越近越靠前) $order = 'juli asc,id desc'; // 查詢數(shù)據(jù) Db::name('user')->field($field)->order($order)->select(); ``` #### 4. 經緯度范圍查詢 --- `經緯度范圍計算` 功能函數(shù) ```php /** * 經緯度范圍計算 * @param $longitude 經度 * @param $latitude 緯度 * @param $radius 半徑(米) * @return array */ function get_around($longitude, $latitude, $radius) { $PI = 3.14159265; $degree = (24901 * 1609) / 360.0; $dpmLat = 1 / $degree; $radiusLat = $dpmLat * $radius; $minLat = $latitude - $radiusLat; $maxLat = $latitude + $radiusLat; $mpdLng = $degree * cos($latitude * ($PI / 180)); $dpmLng = 1 / $mpdLng; $radiusLng = $dpmLng * $radius; $minLng = $longitude - $radiusLng; $maxLng = $longitude + $radiusLng; return compact('minLat', 'maxLat', 'minLng', 'maxLng'); } ``` **使用示例** 查詢 3 公里內的數(shù)據(jù)。首先,根據(jù)當前位置獲取 3 公里內的經緯度范圍,然后帶上查詢條件查詢數(shù)據(jù)庫即可 ```php $longitude = 113.869205; //經度 $latitude = 22.583286; //緯度 $radius = 3000; //單位:米 // 經緯度范圍 $around = get_around($longitude, $latitude, $radius); // 構造查詢條件 // 數(shù)據(jù)庫經緯度字段分別為:longitude,latitude $where = [ ['longitude', '>=', $around['minLng']], ['longitude', '<=', $around['maxLng']], ['latitude', '>=', $around['minLat']], ['latitude', '<=', $around['maxLat']], ]; // 按照經緯度范圍查詢數(shù)據(jù) // 建議使用 where 的閉包查詢(TP6.0) // 因為閉包可以生成以下SQL,標明這幾個查詢條件是一個整體,便于后期維護 // SQL語句示例: SELECT * FROM `user` WHERE ( 經緯度查詢條件 ) and 其他條件 $data = Db::name('user') ->where(function ($query) use ($where) { $query->where($where); }) ->select(); ```