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

Java 計算經緯度距離與矩形範圍查詢:Haversine 公式實作

     編輯:Java 相關知識

地理座標距離計算核心邏輯

基於球面三角學的 Haversine 公式 計算兩點間大圓距離,適合 GPS 定位、LBS 服務、地理圍欄等場景。

核心工具類實作


public final class GeoUtils {
    private static final double EARTH_RADIUS_KM = 6378.137; // 赤道半徑 (km)
    private static final double METERS_PER_DEGREE_LAT = 111_000; // 近似值

    private GeoUtils() {} // 工具類禁止實例化

    /**
     * 計算兩點間距離 (單位:公尺)
     */
    public static double distanceInMeters(double lat1, double lon1, double lat2, double lon2) {
        double radLat1 = Math.toRadians(lat1);
        double radLat2 = Math.toRadians(lat2);
        double dLat = radLat1 - radLat2;
        double dLon = Math.toRadians(lon1) - Math.toRadians(lon2);

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.asin(Math.sqrt(a));
        return c * EARTH_RADIUS_KM * 1000; // 轉公尺
    }

    /**
     * 依中心點與半徑計算外接矩形範圍 (經緯度邊界)
     * @return double[] {minLon, minLat, maxLon, maxLat}
     */
    public static double[] boundingBox(double centerLon, double centerLat, double radiusMeters) {
        if (centerLon == 0 && centerLat == 0) {
            // 極點特殊處理:經度跨度極大,實際應用建議拋出異常或回傳全球範圍
            return new double[] {-180, -90, 180, 90};
        }

        // 緯度跨度固定
        double latDelta = radiusMeters / METERS_PER_DEGREE_LAT;
        // 經度跨度隨緯度變化:cos(lat) 修正
        double lonDelta = radiusMeters / (Math.abs(Math.cos(Math.toRadians(centerLat))) * METERS_PER_DEGREE_LAT);

        return new double[] {
            centerLon - lonDelta, centerLat - latDelta,
            centerLon + lonDelta, centerLat + latDelta
        };
    }

    /** 度轉弧度 */
    private static double toRad(double deg) {
        return deg * Math.PI / 180.0;
    }
}

資料庫空間查詢優化

搭配 MySQL 空間索引或 PostgreSQL PostGIS:


-- MySQL 5.7+ 空間索引查詢
SELECT * FROM shops
WHERE ST_Distance_Sphere(
    point(lng, lat),
    point(121.5654, 25.0330)
) <= 1000; -- 1 公里內

-- 矩形範圍預過濾 (效能優化)
SELECT * FROM shops
WHERE lng BETWEEN 121.55 AND 121.58
  AND lat BETWEEN 25.02 AND 25.04
  AND ST_Distance_Sphere(point(lng, lat), point(121.5654, 25.0330)) <= 1000;

精度與效能權衡建議

方法精度效能適用場景
Haversine高 (球面模型)中一般 LBS、導航、圍欄
Vincenty 公式極高 (橢球體模型)低測量、精密導航
平面近似 (歐氏距離)低 (小範圍可接受)極高極小範圍、嵌入式裝置
MySQL ST_Distance_Sphere高 (資料庫層)高 (索引支援)大量資料空間查詢

常見坑點提醒

  • 經緯度順序:lng, lat (x, y) vs lat, lng (y, x) —— API 文件必須確認
  • 極點/日期變更線邊界條件:跨越 ±180° 經度需特殊處理
  • 單位一致性:半徑單位 (km/m) 與回傳單位必須對齊
  • 浮點數精度:資料庫儲存建議用 DECIMAL(10,7) 或 DOUBLE
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved