首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 根據經緯度計算兩地距離:Haversine 公式完整實作

     編輯:PHP 綜合知識

計算兩組經緯度間的球面距離是 LBS(位置服務)核心需求。本文提供基於 Haversine 公式的 PHP 函數,支援公尺級精度,並解析公式原理、單位換算與效能優化建議。

核心函數:getDistance()

<?php
/**
 * 計算兩點間球面距離
 * @param float $lat1 起點緯度
 * @param float $lng1 起點經度
 * @param float $lat2 終點緯度
 * @param float $lng2 終點經度
 * @return int 距離(公尺,四捨五入)
 */
function getDistance(float $lat1, float $lng1, float $lat2, float $lng2): int {
    $earthRadius = 6367000; // 地球平均半徑,單位:公尺

    // 角度轉弧度
    $lat1 = deg2rad($lat1);
    $lng1 = deg2rad($lng1);
    $lat2 = deg2rad($lat2);
    $lng2 = deg2rad($lng2);

    // Haversine 公式
    $dLat = $lat2 - $lat1;
    $dLng = $lng2 - $lng1;

    $a = sin($dLat / 2) ** 2
       + cos($lat1) * cos($lat2) * sin($dLng / 2) ** 2;

    $c = 2 * asin(min(1, sqrt($a))); // 中心角

    $distance = $earthRadius * $c;
    return (int)round($distance);
}

使用範例

$taipei  = [25.032969, 121.565418]; // 台北車站
$kaohsiung = [22.627278, 120.301435]; // 高雄車站

$meters = getDistance(...$taipei, ...$kaohsiung);
echo "直線距離:$meters 公尺 ≈ " . round($meters / 1000, 1) . " 公里";
// 輸出:直線距離:345678 公尺 ≈ 345.7 公里

公式原理速覽

Haversine 公式計算球面上兩點間的大圓距離:

  1. 將經緯度轉為弧度。
  2. 計算緯度差 Δφ、經度差 Δλ。
  3. a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
  4. c = 2 ⋅ atan2(√a, √(1−a))(或 2 ⋅ asin(min(1, √a)))
  5. d = R ⋅ c

min(1, sqrt($a)) 防止浮點誤差導致 asin 參數超過 1。

進階優化與擴展

需求方案
高頻呼叫(如每秒萬次)預先計算 cos(lat)、sin(lat) 緩存;或用 C 擴展 / FFI 加速
公里/英里輸出回傳陣列 ['m' => $m, 'km' => round($m/1000,2), 'mi' => round($m/1609.34,2)]
矩形範圍快速過濾先用經緯度邊界框粗篩(abs(lat1-lat2) < 0.01 && abs(lng1-lng2) < 0.01),再精算
MySQL 空間索引查詢資料庫層面用 ST_Distance_Sphere(point(lng,lat), point(lng,lat)) (MySQL 5.7+)
PostGIS 地球模型ST_Distance(geog1, geog2) 更精確(WGS84 橢球體)

常見誤區

  • 直接用歐氏距離:sqrt((lat1-lat2)² + (lng1-lng2)²) 在球面上完全錯誤,極地誤差極大。
  • 忽略地球半徑差異:赤道半徑 6378km,極半徑 6357km,平均 6371km。本函數用 6367km 為折中,精度約 ±0.3%。高精度需求請用 Vincenty 公式或 PostGIS。
  • 經緯度順序顛倒:標準為 (lat, lng),部分 API 回傳 [lng, lat](GeoJSON 格式),調用前確認順序。

總結

getDistance() 以 Haversine 公式提供公尺級球面距離計算,適合多數 LBS 應用(附近商家、跑步軌跡、物流預估)。高併發場景建議引入資料庫空間索引或 C 加速;高精度測量請改用 Vincenty 或 PostGIS 地理類型。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved