LBS(位置服務)常見需求:給定中心點經緯度,計算周邊 N 公里矩形邊界,用於資料庫範圍查詢(WHERE lat BETWEEN min AND max)。本文推導球面幾何公式,提供 PHP 實作與精度分析,適用於附近商家、車輛調度、地理圍欄等場景。
R = 6371 km距離 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)
[...]]
*/
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 | 高緯度放大 | 極地專用投影 |
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";
ALTER TABLE shops ADD SPATIAL INDEX idx_location (location),配合 ST_Distance_Sphere、MBRContainsGEOADD、GEORADIUS、GEORADIUSBYMEMBER,極速記憶體級 LBSgeo_distance、geo_bounding_box,全文搜尋 + 地理過濾經緯度矩形邊界計算是 LBS 系統的基礎構建塊。球面幾何公式在 3km 範圍內誤差極小(<1m),完全滿足「附近商家」、「車輛調度」等業務需求。生產系統建議:「矩形粗篩(索引友善)+ Haversine 精篩(精確距離)」,並根據規模選擇 MySQL 空間索引、PostGIS、Redis GEO 或 Elasticsearch 專用引擎。