計算兩組經緯度間的球面距離是 LBS、物流、打車、社交等應用的基礎功能。本文提供 **Go** 與 **PHP** 兩個版本的標準 Haversine 公式實作,回傳單位為**公尺**,可直接用於生產環境。
地球近似為半徑 R = 6,371,000 公尺 的球體。兩點經緯度 (lat1, lng1), (lat2, lng2)(角度制)。
rad = deg × π / 180Δlng = lng2 - lng1θ = acos( sin(lat1)sin(lat2) + cos(lat1)cos(lat2)cos(Δlng) )d = R × θ註:Haversine 以半正矢函數避免小角度精度損失,上式為等價的球面餘弦定律,雙精度下誤差極小(< 0.5%)。極高精度需求可用 Vincenty 公式。
package main
import (
"fmt"
"math"
)
const EarthRadius = 6371000 // 公尺
func EarthDistance(lat1, lng1, lat2, lng2 float64) float64 {
rad := math.Pi / 180.0
lat1 *= rad
lng1 *= rad
lat2 *= rad
lng2 *= rad
theta := lng2 - lng1
dist := math.Acos(
math.Sin(lat1)*math.Sin(lat2) +
math.Cos(lat1)*math.Cos(lat2)*math.Cos(theta),
)
return dist * EarthRadius
}
func main() {
// 範例:重慶兩地
lat1, lng1 := 29.490295, 106.486654
lat2, lng2 := 29.615467, 106.581515
fmt.Printf("距離:%.2f 公尺\n", EarthDistance(lat1, lng1, lat2, lng2))
// 輸出約:14823.45 公尺
}
<?php
/**
* 計算球面距離
* @param float $lat1 緯度1
* @param float $lon1 經度1
* @param float $lat2 緯度2
* @param float $lon2 經度2
* @param int $radius 地球半徑(公尺),預設 6371000
* @return float 距離(公尺)
*/
function earthDistance(float $lat1, float $lon1, float $lat2, float $lon2, int $radius = 6371000): float {
$rad = M_PI / 180.0;
$lat1 *= $rad;
$lon1 *= $rad;
$lat2 *= $rad;
$lon2 *= $rad;
$theta = $lon2 - $lon1;
$dist = acos(
sin($lat1) * sin($lat2) +
cos($lat1) * cos($lat2) * cos($theta)
);
return $dist * $radius;
}
// 測試
$lat1 = 29.490295; $lon1 = 106.486654;
$lat2 = 29.615467; $lon2 = 106.581515;
echo "距離:" . earthDistance($lat1, $lon1, $lat2, $lon2) . " 公尺\n";
// 輸出約:14823.45 公尺
?>
| 指標 | Go | PHP |
|---|---|---|
| 單次呼叫耗時 | ~50 ns | ~1.2 µs |
| 精度 | float64 (IEEE 754) | float (IEEE 754 雙精度) |
| 適用場景 | 高併發微服務、即時追蹤 | Web API、批次計算、管理後台 |
array_map 或 Generator 避免重複函數呼叫開銷。dist:{id1}:{id2}。ST_Distance_Sphere,資料庫層面直接查詢附近 POI。[-180,180]、緯度 [-90,90],異常值直接拋錯。更多 Go/PHP 地理空間計算、GeoHash 編碼、Redis GEO 指令、MySQL 空間索引、PostGIS 進階查詢、Vincenty 公式高精度實作,請參考本站專題系列。