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

PHP 計算座標點周邊 3 公里矩形範圍:經緯度邊界算法實作

     編輯:PHP 綜合知識

前言

LBS(位置服務)常見需求:給定中心點經緯度,計算周邊 N 公里矩形邊界,用於資料庫範圍查詢(WHERE lat BETWEEN min AND max)。本文推導球面幾何公式,提供 PHP 實作與精度分析,適用於附近商家、車輛調度、地理圍欄等場景。

一、數學原理推導

假設

  • 地球為正球體,半徑 R = 6371 km
  • 小範圍(<50km)近似平面,誤差可接受
  • 緯度 1 度 ≈ 111.32 km(恆定)
  • 經度 1 度 ≈ 111.32 km × cos(緯度)(隨緯度變化)

公式推導

距離 d = 3 km
角度半徑 δ = d / R 弧度

緯度跨度 Δlat = δ (弧度) → 角度
經度跨度 Δlng = δ / cos(lat) (弧度) → 角度

四個角點:
左上:  (lat + Δlat, lng - Δlng)
右上:  (lat + Δlat, lng + Δlng)
左下:  (lat - Δlat, lng - Δlng)
右下:  (lat - Δlat, lng + Δlng)

二、PHP 實作代碼

[...]]
 */
function calculateBoundingBox(float $lng, float $lat, float $distanceKm = 3, float $earthRadius = 6371): array {
    // 角度半徑(弧度)
    $deltaRad = $distanceKm / $earthRadius;
    
    // 緯度跨度(度)
    $dLat = rad2deg($deltaRad);
    
    // 經度跨度(度)—— 依賴當前緯度餘弦
    $dLng = rad2deg($deltaRad / cos(deg2rad($lat)));
    
    $minLat = $lat - $dLat;
    $maxLat = $lat + $dLat;
    $minLng = $lng - $dLng;
    $maxLng = $lng + $dLng;
    
    return [
        'minLat' => $minLat,
        'maxLat' => $maxLat,
        'minLng' => $minLng,
        'maxLng' => $maxLng,
        'center' => ['lat' => $lat, 'lng' => $lng],
        'distanceKm' => $distanceKm,
        'corners' => [
            'topLeft'     => ['lat' => $maxLat, 'lng' => $minLng],
            'topRight'    => ['lat' => $maxLat, 'lng' => $maxLng],
            'bottomLeft'  => ['lat' => $minLat, 'lng' => $minLng],
            'bottomRight' => ['lat' => $minLat, 'lng' => $maxLng],
        ],
    ];
}

// 使用範例:台北 101
$result = calculateBoundingBox(121.5645, 25.0339, 3);
echo "緯度範圍: {$result['minLat']} ~ {$result['maxLat']}\n";
echo "經度範圍: {$result['minLng']} ~ {$result['maxLng']}\n";
print_r($result['corners']);
?>

三、資料庫查詢應用

$bbox = calculateBoundingBox($lng, $lat, 3);

$sql = "SELECT * FROM shops 
        WHERE latitude BETWEEN {$bbox['minLat']} AND {$bbox['maxLat']}
        AND longitude BETWEEN {$bbox['minLng']} AND {$bbox['maxLng']}
        AND status = 1";

// 進一步精確過濾(可選):Haversine 公式計算真實距離
$preciseSql = "SELECT *, 
    (6371 * ACOS(
        COS(RADIANS({$lat})) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS({$lng}))
        + SIN(RADIANS({$lat})) * SIN(RADIANS(latitude))
    )) AS distance_km
    FROM shops
    WHERE latitude BETWEEN {$bbox['minLat']} AND {$bbox['maxLat']}
    AND longitude BETWEEN {$bbox['minLng']} AND {$bbox['maxLng']}
    HAVING distance_km <= 3
    ORDER BY distance_km";

四、精度分析與誤差來源

因素影響誤差量級緩解方案
地球非球體(扁球體)極地/赤道半徑差 21km~0.3%使用 WGS84 橢球體模型
平面近似大範圍球面投影變形3km 內 < 1m範圍 > 50km 改用 PostGIS
浮點運算PHP float 64 位精度~1e-15可忽略
經度跨度計算cos(lat) 分母極地趨近 0高緯度放大極地專用投影

五、進階:Haversine 精確距離函數

function haversineDistance(float $lat1, float $lng1, float $lat2, float $lng2, float $R = 6371): float {
    $dLat = deg2rad($lat2 - $lat1);
    $dLng = deg2rad($lng2 - $lng1);
    
    $a = sin($dLat/2)**2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng/2)**2;
    $c = 2 * atan2(sqrt($a), sqrt(1-$a));
    
    return $R * $c;
}

// 資料庫中使用(MySQL 5.7+ 支援)
$sql = "SELECT *, 
    (6371 * ACOS(
        COS(RADIANS(?)) * COS(RADIANS(lat)) * COS(RADIANS(lng) - RADIANS(?))
        + SIN(RADIANS(?)) * SIN(RADIANS(lat))
    )) AS dist
    FROM shops
    HAVING dist <= ?
    ORDER BY dist";

六、生產級建議

  1. MySQL 空間索引:ALTER TABLE shops ADD SPATIAL INDEX idx_location (location),配合 ST_Distance_Sphere、MBRContains
  2. PostGIS:專業地理資料庫,支援多邊形、緩衝區、投影轉換
  3. Redis GEO:GEOADD、GEORADIUS、GEORADIUSBYMEMBER,極速記憶體級 LBS
  4. Elasticsearch Geo:geo_distance、geo_bounding_box,全文搜尋 + 地理過濾

七、結語

經緯度矩形邊界計算是 LBS 系統的基礎構建塊。球面幾何公式在 3km 範圍內誤差極小(<1m),完全滿足「附近商家」、「車輛調度」等業務需求。生產系統建議:「矩形粗篩(索引友善)+ Haversine 精篩(精確距離)」,並根據規模選擇 MySQL 空間索引、PostGIS、Redis GEO 或 Elasticsearch 專用引擎。

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