两种高效计算 两个经纬度之间距离的方法--解决Haversine 公式性能慢的问题

本文介绍了Haversine公式用于计算经纬度间距离的原理和实现,以及针对大数据场景下的性能问题,提出了使用简化直线距离公式进行优化。对比两者,优化后在性能上有显著提升,但在精度上,短距离内差异不大,需根据具体需求权衡.
摘要由CSDN通过智能技术生成

目录

前言    

Haversine

原理

实现代码

优化后的距离计算

原理

代码

性能及精度对比


前言    

 最新开发的业务中,涉及到计算两个经纬度之间的距离。已知A点和B点的 经纬度,计算A点到B点之间的距离。最开始使用的是Haversine公式来进行计算,但上线后出现严重的性能问题,主要原因是业务数据量太大,每天PB级的数据量。

因此,需要研究一个更高效便捷的计算方法,下面将介绍Haversine公式和实现代码,以及新的计算公式&代码,并对他们的性能及精度进行分析。

Haversine

原理

Haversine 公式是一种用于计算球面上两个坐标点之间距离的数学公式,特别适用于地球上的球面距离计算。该公式基于球面三角学,利用球面上两点间的弧长来计算它们之间的距离。这个公式的形式如下:

总的来说,Haversine 公式通过计算球面上两点间的弧长,提供了一种相对精确的球面距离计算方法。这种方法在小球面(如地球)上非常常见,并在航海、导航等领域广泛应用。

实现代码

public class DistanceCalculator {

    // 地球半径,单位为千米
    private static final double EARTH_RADIUS = 6371.0;

    // 将角度转换为弧度
    private static double toRadians(double degree) {
        return degree * Math.PI / 180.0;
    }

    // 计算两个经纬度之间的距离,返回结果单位为千米
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        double dLat = toRadians(lat2 - lat1);
        double dLon = toRadians(lon2 - lon1);

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
                        Math.sin(dLon / 2) * Math.sin(dLon / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return EARTH_RADIUS * c;
    }

    public static void main(String[] args) {
        // 示例坐标:纬度 Latitude 1: 40.748817, 经度 Longitude 1: -73.985428
        // 示例坐标:纬度 Latitude 2: 34.052235, 经度 Longitude 2: -118.243683
        double distance = calculateDistance(40.748817, -73.985428, 34.052235, -118.243683);

        System.out.println("Distance between the two coordinates: " + distance + " km");
    }
}

如果对计算性能不是特别严苛,可以优先使用Haversine 公式来进行计算。但是对于TB级或者PB量级的数据业务来说,这个公式由于计算过于复杂,存在比较大的性能问题。

通过火焰图分析,主要是Math.atan2() 函数的计算开销比较大。

优化后的距离计算

原理

使用弧度计算的简单直线距离计算公式:

由于地球的半径很大,在一个很小的区域内(公里级)可以近似成一个平面,这里直接采用勾股定理来计算直线距离。虽然精度可能会略有降低,但是由于计算公式很简单,性能会有极大提升。

代码

public class SimpleDistanceCalculator {

    // 将角度转换为弧度
    private static double toRadians(double degree) {
        return degree * Math.PI / 180.0;
    }

    // 计算两个经纬度之间的简易直线距离,返回结果单位为千米
    public static double calculateSimpleDistance(double lat1, double lon1, double lat2, double lon2) {
        double dLat = toRadians(lat2 - lat1);
        double dLon = toRadians(lon2 - lon1);

        // 使用简化的直线距离公式
        double distance = Math.sqrt(dLat * dLat + dLon * dLon) * 60 * 1.852;

        return distance;
    }

    public static void main(String[] args) {
        // 示例坐标:纬度 Latitude 1: 40.748817, 经度 Longitude 1: -73.985428
        // 示例坐标:纬度 Latitude 2: 34.052235, 经度 Longitude 2: -118.243683
        double simpleDistance = calculateSimpleDistance(40.748817, -73.985428, 34.052235, -118.243683);

        System.out.println("Simple distance between the two coordinates: " + simpleDistance + " km");
    }
}

性能及精度对比

前者计算1千万次使用的时间为:4462ms

优化后计算1千万次使用的时间为:244ms

优化后耗时为原来的 5.4%。

精度上,在短距离(例如 5 公里以内),Haversine 公式和等矩形投影(简化后的方法)之间的精度差异可能不会很大。在这个范围内,球面和平面的差异相对较小,因此简化的方法通常能够提供足够的精度。

然而,具体的精度差异会受到多个因素的影响,包括具体的坐标位置、所使用的地球半径,以及计算时是否考虑了地球的椭球形状等。因此,很难给出一个具体的数字来表示它们之间的精度差异。

为了获取更准确的精度比较,最好的方法是使用实际的测试数据,并比较两种方法得到的结果。你可以选择一些已知距离的坐标点,分别使用 Haversine 公式和等矩形投影计算它们之间的距离,然后比较计算结果。

本人的一个实际路测,在5公里范围内,大约存在5%到15% 之间的差异。

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值