核心函數:球面距離計算(Haversine 公式變體)
<?php
define('PI', 3.1415926535898);
define('EARTH_RADIUS', 6378.137); // 千米
/**
* 計算兩點球面距離 (公里)
* @param float $lat1 緯度1
* @param float $lng1 經度1
* @param float $lat2 緯度2
* @param float $lng2 經度2
* @return float 距離(公里),保留4位小數
*/
function GetDistance($lat1, $lng1, $lat2, $lng2) {
$radLat1 = $lat1 * (PI / 180);
$radLat2 = $lat2 * (PI / 180);
$a = $radLat1 - $radLat2;
$b = ($lng1 * (PI / 180)) - ($lng2 * (PI / 180));
$s = 2 * asin(sqrt(
pow(sin($a / 2), 2) +
cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)
));
$s = $s * EARTH_RADIUS;
return round($s * 10000) / 10000; // 公里,4位小數
}
/**
* 計算以某點為中心、指定半徑的經緯度範圍(矩形近似)
* @param float $lat 中心緯度
* @param float $lon 中心經度
* @param float $radius 半徑(公里)
* @return array ['minLat','maxLat','minLon','maxLon']
*/
function GetRange($lat, $lon, $radius) {
// 1度緯度 ≈ 111.195 公里
$degree = (24901 * 1609) / 360.0; // 地球周長(英尺) / 360
$dpmLat = 1 / $degree;
$radiusLat = $dpmLat * $radius;
$minLat = $lat - $radiusLat;
$maxLat = $lat + $radiusLat;
// 經度距離隨緯度變化:cos(緯度)
$mpdLng = $degree * cos($lat * (PI / 180));
$dpmLng = 1 / $mpdLng;
$radiusLng = $dpmLng * $radius;
$minLon = $lon - $radiusLng;
$maxLon = $lon + $radiusLng;
return [
'minLat' => $minLat, 'maxLat' => $maxLat,
'minLon' => $minLon, 'maxLon' => $maxLon,
];
}
?>
使用示例
<?php
// 北京天安門到上海人民廣場
$dist = GetDistance(39.9042, 116.4074, 31.2304, 121.4737);
echo "距離: $dist 公里\n"; // 約 1068 公里
// 以北京為中心 50 公里範圍
$range = GetRange(39.9042, 116.4074, 50);
print_r($range);
/*
Array
(
[minLat] => 39.45...
[maxLat] => 40.35...
[minLon] => 115.78...
[maxLon] => 117.03...
)
*/
?>
應用場景
- 範圍查詢: 先用
GetRange 算出矩形範圍,SQL WHERE lat BETWEEN minLat AND maxLat AND lng BETWEEN minLon AND maxLon 粗略過濾,再用 GetDistance 精確過濾
- 附近的人/商家: 結合空間索引(MySQL 5.7+ GIS、PostgreSQL PostGIS)更高效
- 物流/跑腿計費: 直線距離估算基礎費用
注意事項
- 百度地圖坐標為 **BD-09** 加密坐標,若源數據為 GPS (WGS-84) 或高德 (GCJ-02),需先轉換,否則距離有偏差
- 矩形範圍為近似,極地附近誤差較大,中國大陸緯度範圍內誤差可接受
- 高性能場景建議使用數據庫原生空間函數(
ST_Distance_Sphere、ST_Within)
相關專題