原创

MySQL以及Java根据经纬度计算距离

球面距离公式

球面距离公式是计算球面上两点间距离的公式。

举个例子:已知,设R为球体半径,点A ,纬度角β1 ,经度角α1 ;点B ,纬度角β2 ,经度角α2。求点A和点B两点间的距离?

公式:S=R·arccos[cosβ1·cosβ·2cos(α1-α2)+sinβ1·sinβ2]

公式参考:球面距离公式

地球半径

地球半径:6371.393千米。

计算坐标点

广州塔,经纬度:113.324553,23.106414

东方明珠电视塔,经纬度:121.499718,31.239703

关于在线经纬度获取的话,可以看看高德地图的:https://lbs.amap.com/tools/picker

Java版本

/**
 * 根据球面距离公式计算两个地点之间的距离
 *
 * <p>公式:S=R·arccos[cosβ1·cosβ·2cos(α1-α2)+sinβ1·sinβ2]
 */
@UtilityClass
public class DistanceUtil {

  /** 地球半径(单位:米) */
  private static final double EARTH_RADIUS = 6371393;

  /** 以度为单位的角度值乘以该常数以获得以弧度为单位的角度值。 */
  private static final double DEGREES_TO_RADIANS = 0.017453292519943295;

  /**
   * 根据公式进行计算
   *
   * @param longitude1 位置1经度
   * @param latitude1 位置1纬度
   * @param longitude2 位置2经度
   * @param latitude2 位置2纬度
   * @return 返回计算的距离,单位:米
   */
  public static double getDistance(
      Double longitude1, Double latitude1, Double longitude2, Double latitude2) {
    double radiansLongitude1 = toRadians(longitude1);
    double radiansLatitude1 = toRadians(latitude1);
    double radiansLongitude2 = toRadians(longitude2);
    double radiansLatitude2 = Math.toRadians(latitude2);

    final double cos =
        BigDecimal.valueOf(Math.cos(radiansLatitude1))
            .multiply(BigDecimal.valueOf(Math.cos(radiansLatitude2)))
            .multiply(
                BigDecimal.valueOf(
                    Math.cos(
                        BigDecimal.valueOf(radiansLongitude1)
                            .subtract(BigDecimal.valueOf(radiansLongitude2))
                            .doubleValue())))
            .add(
                BigDecimal.valueOf(Math.sin(radiansLatitude1))
                    .multiply(BigDecimal.valueOf(Math.sin(radiansLatitude2))))
            .doubleValue();

    double acos = Math.acos(cos);
    return BigDecimal.valueOf(EARTH_RADIUS).multiply(BigDecimal.valueOf(acos)).doubleValue();
  }

  /**
   * 参考:{@link Math#toRadians(double)}
   *
   * @param value value
   * @return {double}
   */
  private static double toRadians(double value) {
    return BigDecimal.valueOf(value).multiply(BigDecimal.valueOf(DEGREES_TO_RADIANS)).doubleValue();
  }

  /**
   * 测试
   *
   * <pre>
   * 广州塔,经纬度:113.324553,23.106414
   * 东方明珠电视塔,经纬度:121.499718,31.239703
   * </pre>
   *
   * @param args args
   */
  public static void main(String[] args) {
    double distance = getDistance(113.324553, 23.106414, 121.499718, 31.239703);
    System.out.println(distance);
  }
}

计算结果(单位:米):

1212391.2574948743

MySQL版本

开始写计算函数

有了前面球面距离计算公式基础之后,我们就可以一葫芦画瓢写出mysql版的函数了。

创建calculate_distance函数

这里我将计算距离结果单位设置为米。

CREATE FUNCTION calculate_distance ( longitude1 DOUBLE, latitude1 DOUBLE, longitude2 DOUBLE, latitude2 DOUBLE ) RETURNS DOUBLE 
BEGIN
    RETURN 6371393 * ACOS( 
        COS( RADIANS( latitude1 ))  
        * COS( RADIANS( latitude2 )) 
        * COS( RADIANS( longitude2 - longitude1 )) 
        + SIN( RADIANS( latitude1 )) 
        * SIN( RADIANS( latitude2 )) 
    );
END;

说明: MySQL中的RADIANS函数

将度数值转换为弧度值。

mysql> SELECT RADIANS(180);
+-------------------+
| RADIANS(180)      |
+-------------------+
| 3.141592653589793 |
+-------------------+
1 row in set (0.03 sec)

MySQL计算示例

示例sql查询两点的距离,实际情况你可以根据自己数据库中的字段去调整sql,在此我仅仅是演示:

mysql> SELECT calculate_distance(113.324553, 23.106414, 121.499718, 31.239703);
+------------------------------------------------------------------+
| calculate_distance(113.324553, 23.106414, 121.499718, 31.239703) |
+------------------------------------------------------------------+
|                                               1212391.2574948743 |
+------------------------------------------------------------------+
1 row in set (0.06 sec)

计算结果跟前面Java版本的一样。

结果大概都是1212391米。

对比验证计算结果

高德地图验证结果

高德地图经纬度计算在线地址

广州塔到东方明珠电视塔的距离:1213675 米。跟我们公式计算(Java版本&MySQL版本)的误差大概是:1284 米。

百度地图验证计算结果

百度地图经纬度计算在线地址

广州塔到东方明珠电视塔的距离:1212315.87 米。跟我们公式计算(Java版本&MySQL版本)的误差大概是:75.13 米。

结尾

毕竟这是计算的直线距离,不同的计算方法、坐标系(Sphere坐标系、WGS84坐标系)、精度等等因素都会影响计算的结果。

如果要计算驾车、步行这种分类计算距离的,建议可以去看看使用高德地图、百度地图、腾讯地图的API。

正文到此结束
本文目录