目前基于LBS的服务有很多,这些服务往往会需求大量的两个经纬度点之间的导航距离或者导航路径。如果无法正确的获得这些结果,我们就要进行兜底操作:通过直线距离乘以某个倍数或者折线距离代替导航距离。这就需要我们计算两个经纬度点之间的直线距离。

谷歌地图给出的计算公式为:
$$\mathcal{S} = 2 \times arcsin\left( \sqrt {\sin ^2 \left({a\over 2}\right) + \cos(Lat1) \times \cos(Lat2) \times \sin ^2 \left({b \over 2}\right)} \right) \times 6378.137$$
其中各个变量的意思是:

  • \(a\): 起止点纬度对应弧度的差;
  • \(b\): 起止点经度对应弧度的差;
  • \(Lat1\): 起点的纬度对应的弧度;
  • \(Lat2\): 终点的纬度对应的弧度;
  • \(6378.137\): 单位千米,地球赤道半径,地球平均半径是\(6371.004\)(维基百科)。这个值和要计算的经纬度是否有关?网上还有很多人用\(6371008.8\)米。

Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class linearDistance() {
private static double EARTH_RADIUS = 6378.137; // 单位千米
// 角度转换成弧度
private static double getRadian(double degree) {
return degree * Math.PI / 180.0;
}
// 直线距离计算
public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
double radLat1 = getRadian(lat1);
double radLat2 = getRadian(lat2);
double a = radLat1 - radLat2;
double b = getRadian(lng1) - getRadian(lng2);
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
s = s * EARTH_RADIUS;
return s * 1000; // 单位米
}
}

  其中角度转弧度的计算:\(360^\circ = 2\pi \),那么\(x^\circ = x\cdot \pi/180\)。其中\(\pi\)在Java中是\(Math.PI\)。另外地球半径不太统一,有待确认。

Python实现

1
2
3
4
5
6
7
8
9
10
11
import math
def getDistance(lat1, lng1, lat2, lng2):
EARTH_RADIUS = 6371008.8 # 单位米
SCALE = 1000000.0
radLat1 = math.radians(lat1 / SCALE)
radLat2 = math.radians(lat2 / SCALE)
a = radLat1 - radLat2
b = math.radians(lng1 / SCALE) - math.radians(lng2 / SCALE)
s = 2 * math.asin(math.sqrt(math.pow(math.sin(a / 2), 2) + math.cos(radLat1) * math.cos(radLat2) * math.pow(math.sin(b / 2), 2)))
s = s * EARTH_RADIUS
return s

  其中经纬度都是精确六位数的整数,为了明确地球半径的不同,我们这里使用了另一个地球半径数字。希望能找到比较官方的验证哪个半径比较合适的方法。

总结

  本文通过Java和Python分别在不同格式的输入下实现了计算两个经纬度点之间的直线距离的方法。