JAVA 根据经纬度获取两点之间的距离

前(fei)言(hua):

最近接到“获取附近**”的需求,作为面向百度编程的程序猿,浏览了诸多有关“根据经纬度计算距离”的博文——其中复杂的,只贴出了实现代码,没有说明实现思路,然后想通过代码理解的话,似乎也行不通,毕竟代码都已经被优化为剩下数行了;简单的,直接拿着勾股定理就开干,实在让人内牛满面。

所以呢,在这里将给出:简单的思路注意点,基于java的源码结尾彩蛋

先上测试结果吧:

比网上多数看不懂的算法要准确一些,虽然依然跟百度地图上的测量结果稍有出入,不过已经足够应付一般的需求了~

思路:

1. 地球本身是个不规则的球体,这里将其看着一个规制球体

2. 半径取平均值:6371.393千米 (戳这里还有测量方法说明)

3. 计算公式采用“球面距离公式”:S=R·arccos[cosβ1cosβ2cos(α1-α2)+sinβ1sinβ2](戳这里有具体说明)

4. 对公式的理解:设需要求距离的两点为A、B,球心为O。可以分解成3步:

    ① cosβ1cosβ2cos(α1-α2)+sinβ1sinβ2——求∠AOB的余弦值,这里是难点,得到这部分公式的推导过程比较复杂;

    ② arccos[∠AOB的余弦值]——求∠AOB的反余弦值,值域为[0,π],本质是 ∠AOB角度 / 180° * π;

    ③ ∠AOB的反余弦值,等价于 ∠AOB角度 / 180° * π,这里集合弧长公式很好理解

注意:

1. 经纬度,在本质上是角度

2. Java中的Math类提供的sin和con方法的参数是弧度,而不是角度

3. 公式里面的“arccos”是反余弦算法,在Math类中为acos()方法

源码:

    private static final double EARTH_RADIUS = 6371393; // 平均半径,单位:m

    /**
     * 通过AB点经纬度获取距离
     * @param pointA A点(经,纬)
     * @param pointB B点(经,纬)
     * @return 距离(单位:米)
     */
    public static double getDistance(Point2D pointA, Point2D pointB) {
        // 经纬度(角度)转弧度。弧度用作参数,以调用Math.cos和Math.sin
        double radiansAX = Math.toRadians(pointA.getX()); // A经弧度
        double radiansAY = Math.toRadians(pointA.getY()); // A纬弧度
        double radiansBX = Math.toRadians(pointB.getX()); // B经弧度
        double radiansBY = Math.toRadians(pointB.getY()); // B纬弧度

        // 公式中“cosβ1cosβ2cos(α1-α2)+sinβ1sinβ2”的部分,得到∠AOB的cos值
        double cos = Math.cos(radiansAY) * Math.cos(radiansBY) * Math.cos(radiansAX - radiansBX)
                + Math.sin(radiansAY) * Math.sin(radiansBY);
//        System.out.println("cos = " + cos); // 值域[-1,1]
        double acos = Math.acos(cos); // 反余弦值
//        System.out.println("acos = " + acos); // 值域[0,π]
//        System.out.println("∠AOB = " + Math.toDegrees(acos)); // 球心角 值域[0,180]
        return EARTH_RADIUS * acos; // 最终结果
    }

测试代码:附上百度地图坐标拾取系统,方便各位测试

   public static void main(String[] args) {
        // 北京 东单地铁站
        Point2D pointDD = new Point2D.Double(116.425249, 39.914504);
        // 北京 西单地铁站
        Point2D pointXD = new Point2D.Double(116.382001, 39.913329);
        System.out.println(getDistance(pointDD, pointXD));
        System.out.println();

        // 北京 天安门
        Point2D pointTAM = new Point2D.Double(116.403882, 39.915139);
        // 广州 越秀公园
        Point2D pointGZ = new Point2D.Double(113.272422,23.147387);
        System.out.println(getDistance(pointTAM, pointGZ));
        System.out.println();

        // 四川大学
        Point2D pointSCDX = new Point2D.Double(104.090539,30.636951);
        // 成都南站
        Point2D pointCDNZ = new Point2D.Double(104.074238,30.612572);
        System.out.println(getDistance(pointSCDX, pointCDNZ));
        System.out.println();
    }

彩蛋:SQL的实现——传入的经纬度 是 东单地铁站的

-- 北京 东单 116.425249, 39.914504
SELECT
	`name`,
	(
		6371393 * ACOS(
			COS(RADIANS(39.914504)) * COS(RADIANS(latitude)) * COS(RADIANS(116.425249 - longitude))
			+ SIN(RADIANS(39.914504)) * SIN(RADIANS(latitude))
		)
	) AS distance, longitude, latitude
FROM
	tbl_address
WHERE
	`name` LIKE '北京%'
ORDER BY
	distance;

运行结果:

结语:

这个方法应该是十分好理解的,唯一比较难的是公式中第一步那部分。

本来这里可以把完整的推到过程写出来,但由于过程比较繁杂,所以就算了。如果觉得有需要,可以在评论区提要求,我后面有空会补上。

另外,如果这篇博文对你有帮助,请在评论区举起你的小手,这将成为我继续写详细博客的动力!!谢谢!!!

最后,转载请指明出处~

  • 17
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值