【代码】Haversine 距离计算的Java实现

2026-02-25 18:50:31  阅读 18 次 评论 0 条


1、介绍

  • Haversine 距离用于计算地球上两点之间的大圆距离

  • 当考虑地球真实曲率时,它特别适用于计算两个经纬度坐标之间的距离


image.png

2、Java实现

public class HaversineUtil {
    /**
     * Great-circle distance on a spherical Earth using the haversine formula.
     * 返回两点间球面最短距离(大圆距离)。
     *
     * 注意:
     * - 结果适用于大多数业务距离计算(附近/范围/配送),属于“球体近似”。
     * - 单位:公里(km)
     */
    public static double haversineDistanceKm(double lat1, double lon1, double lat2, double lon2) {
        // 平均地球半径(km),用 6371.0 也可以
        final double R_KM = 6371.0088;

        validateLatLon(lat1, lon1);
        validateLatLon(lat2, lon2);

        // 归一化经度到 [-180, 180)
        lon1 = normalizeLonDeg(lon1);
        lon2 = normalizeLonDeg(lon2);

        // 计算差值(经度差做最短角差归一化,避免跨国际日期变更线时误差)
        double dLatDeg = lat2 - lat1;
        double dLonDeg = shortestDeltaLonDeg(lon1, lon2);

        double dLat = Math.toRadians(dLatDeg);
        double dLon = Math.toRadians(dLonDeg);

        double rLat1 = Math.toRadians(lat1);
        double rLat2 = Math.toRadians(lat2);

        double sinDLat = Math.sin(dLat / 2.0);
        double sinDLon = Math.sin(dLon / 2.0);

        double a = sinDLat * sinDLat
                + Math.cos(rLat1) * Math.cos(rLat2) * sinDLon * sinDLon;

        // 浮点保护:防止极端情况下 a 略超出 [0,1] 导致 NaN
        a = Math.min(1.0, Math.max(0.0, a));

        double c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
        return R_KM * c;
    }

    /**
     * Great-circle distance on a spherical Earth using the haversine formula.
     * 返回两点间球面最短距离(大圆距离)。
     *
     * 注意:
     * - 结果适用于大多数业务距离计算(附近/范围/配送),属于“球体近似”。
     * - 单位:米(m)
     */
    public static double haversineDistanceMeters(double lat1, double lon1, double lat2, double lon2) {
        return haversineDistanceKm(lat1, lon1, lat2, lon2) * 1000.0;
    }

    /** 校验纬度/经度输入是否合理 */
    private static void validateLatLon(double lat, double lon) {
        if (!Double.isFinite(lat) || !Double.isFinite(lon)) {
            throw new IllegalArgumentException("lat/lon must be finite numbers (not NaN/Infinity).");
        }
        if (lat < -90.0 || lat > 90.0) {
            throw new IllegalArgumentException("lat out of range [-90, 90]: " + lat);
        }
    }

    /** 把经度归一化到 [-180, 180) */
    private static double normalizeLonDeg(double lon) {
        // 结果落在 [-180, 180)
        lon = ((lon + 180.0) % 360.0 + 360.0) % 360.0 - 180.0;
        return lon;
    }

    /** 计算两经度的最短角差(单位:度),结果落在 [-180, 180] */
    private static double shortestDeltaLonDeg(double lon1, double lon2) {
        double d = lon2 - lon1;
        // 归一化到 (-180, 180]
        d = ((d + 180.0) % 360.0 + 360.0) % 360.0 - 180.0;
        return d;
    }
}



微信扫码查看本文
本文地址:https://www.yangguangdream.com/?id=2271
版权声明:本文为原创文章,版权归 编辑君 所有,欢迎分享本文,转载请保留出处!
NEXT:已经是最新一篇了

发表评论


表情

还没有留言,还不快点抢沙发?