在 LBS(位置服務)應用中,計算兩點距離、坐標系轉換是核心基礎功能。本文提供兩個實用 PHP 函數:distanceBetween() 基於 Haversine 公式計算球面距離(米),BD09LLtoWGS84() 呼叫百度 API 將百度加密坐標(BD-09)轉為標準 GPS(WGS-84),並說明適用場景與注意事項。
採用 Haversine 公式,以地球半徑 6378137 米計算兩經緯度點間的球面最短距離,精度滿足多數應用需求。
<?php
/**
* 計算兩 GPS 坐標間距離(米)
*
* @param float $lat1 起點緯度
* @param float $lon1 起點經度
* @param float $lat2 終點緯度
* @param float $lon2 終點經度
* @return int 距離(米),四捨五入整數
*/
function distanceBetween(float $lat1, float $lon1, float $lat2, float $lon2): int {
$earthRadius = 6378137; // 地球半徑(米),WGS-84 長半軸
// 角度轉弧度
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);
// 緯度差、經度差
$dLat = $lat2 - $lat1;
$dLon = $lon2 - $lon1;
// Haversine 公式
$a = sin($dLat/2)**2 + cos($lat1) * cos($lat2) * sin($dLon/2)**2;
$c = 2 * asin(sqrt($a));
return (int)round($earthRadius * $c);
}
// 使用範例:天安門到北京西站
$dist = distanceBetween(39.9087, 116.3975, 39.8955, 116.3212);
echo "距離: $dist 米 (" . round($dist/1000, 2) . " 公里)\n";
// 輸出:距離: 6800 米 (6.8 公里)
?>
百度地圖使用加密坐標系 BD-09(在 GCJ-02 火星坐標基礎上再次加密)。若需與 GPS 原始資料、Google Maps、OpenStreetMap 互通,必須轉換為 WGS-84。
<?php
/**
* 百度坐標 (BD-09) 轉標準 GPS (WGS-84)
* 呼叫百度官方轉換 API:https://api.map.baidu.com/ag/coord/convert
*
* @param float $bdLon 百度經度
* @param float $bdLat 百度緯度
* @return string "經度,緯度" (WGS-84) 或原值(轉換失敗)
*/
function BD09LLtoWGS84(float $bdLon, float $bdLat): string {
$url = "https://api.map.baidu.com/ag/coord/convert?from=0&to=4&x={$bdLon}&y={$bdLat}";
$result = @file_get_contents($url);
if ($result === false) {
return "$bdLon,$bdLat"; // 網路失敗返回原值
}
$json = json_decode($result, true);
if (!isset($json['error']) || $json['error'] !== 0) {
return "$bdLon,$bdLat"; // API 錯誤返回原值
}
// 百度返回的 x, y 為 base64 編碼的加密偏移量
$bx = base64_decode($json['x']);
$by = base64_decode($json['y']);
// 反推公式:WGS = 2*BD - 解密偏移
$wgsLon = 2 * $bdLon - $bx;
$wgsLat = 2 * $bdLat - $by;
return "$wgsLon,$wgsLat";
}
// 使用範例
$bdCoord = "116.404,39.915"; // 百度坐標
$wgsCoord = BD09LLtoWGS84(...explode(',', $bdCoord));
echo "WGS-84: $wgsCoord\n";
?>
| 項目 | 說明 |
|---|---|
| 坐標順序 | 慣例為「經度,緯度」(lng, lat),百度 API 參數 x=經度, y=緯度,請勿顛倒。 |
| API 限制 | 百度轉換 API 有配額限制(免費額度),高頻場景請申請 AK 或使用離線轉換庫(如 phayes/geoPHP、wandercn/coord-transform)。 |
| 精度 | Haversine 忽略地球扁率,誤差約 0.5%;高精度需求可用 Vincenty 公式或地理庫。 |
| 批次轉換 | 避免迴圈中逐筆呼叫 HTTP API,應批次收集後單次請求或使用離線庫。 |
| HTTPS | 生產環境請改用 https://api.map.baidu.com/... 並攜帶 AK 參數。 |
不依賴外部 API、無網路延遲、無配額限制:
# Composer 安裝 composer require wandercn/coord-transform # 使用 use Wander\CoordTransform\CoordTransform; $wgs = CoordTransform::bd09ToWgs84($bdLon, $bdLat); // 返回 ['lng' => ..., 'lat' => ...]
該庫實現純 PHP 端坐標系轉換(WGS84 ⇄ GCJ02 ⇄ BD09),效能極高,適合高併發服務。
distanceBetween():純 PHP 實現 Haversine 公式,零依賴、高效能,適合即時距離計算。BD09LLtoWGS84():呼叫百度官方 API,適合低頻、原型開發;生產環境強烈建議改用離線庫 wandercn/coord-transform。