摘要
本片文章主要是推导球面距离公式的过程,需要您了解一些高中数学的三角函数的基本公式,简单的向量运算,如果你还记得的话,或者有兴趣的话可以接着往下看;事情的起因是在项目的代码中看到有一段根据手机定位的经纬度数据计算两个地理位置的距离。
export function calcDistance(point1, point2) {
if (!point1 || !point2) return null;
const EARTH_RADIUS = 6378.137;
const { longitude: lng1, latitude: lat1 } = point1;
const { longitude: lng2, latitude: lat2 } = point2;
const radLat1 = (lat1 * Math.PI) / 180.0;
const radLat2 = (lat2 * Math.PI) / 180.0;
const radDiff = radLat1 - radLat2;
const b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0;
const distance =
2 *
Math.asin(
Math.sqrt(
Math.pow(Math.sin(radDiff / 2), 2) +
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
)
);
return distance * EARTH_RADIUS;
}
下面让我来解释一下上面这段代码的意思,
假设地球半径为,经度longitude
用希腊字母表示;纬度latitude
用希腊字母表示,那么P1
的坐标就是;而P2
的坐标就是,那么代码的核心就是下面这个公式:
其中
这个公式就是传说中的的半正矢公式Haversine formula
,然后我就很好奇为啥这个公式是这样的,纠结这个公式是怎么来的,光是根据球面坐标系和这个公式,并不能理解,它并不像欧几里得空间上的距离那么直观。
下面我们根据空间直角坐标系来定义一下球面坐标系,球面上的任意一点用经纬度来转换可以用下面的公式来表示:
方法一:从弦长求大圆距离
用欧几里得空间距离求解,对于球面上的两点和两点之间的距离可以表示为:
那么转换成球面坐标以后就是将球面公式(1)代入到欧式距离公式中,则就有:
等价于
等价于
等价于
等价于
即
接下来我们把问题转化到平面几何上,参照下图,的长度我们可以通过上门的公式求出,现在我们需要求出,现在已知,假设,那么
由上图显然:
这就等价于
所以
最终得到
以上是最大圆距离(Creat-circle distance
)的一种表现形式。
方法二:用向量的余弦夹角公式求解大圆距离
用球面坐标表示和的坐标,可以得到
那么向量和向量的夹角的余弦:
那么
由此可知
最终得到最大圆距离(Creat-circle distance
)的另一种表现形式,一般网上找到的Creat-circle distance
指的就是这个公式
注:这个公式的余弦函数在地球上距离较近的两点,有计算精度丢失的问题,因此在此基础上我们可以利用余弦定理等价变换成另一种易于计算机较精确计算的公式,半正矢函数公式Haversine
公式
方法三、用半正矢公式定义的距离
由余弦定理可知
将代入上式得到:
由正弦定义可知:
这等价于
即
所以
最终得到
这便是开篇代码中用的Haversine
公式推导的距离。
总结三个公式
对于球面上的两个点和,已知他们的经度和维度,就会有以下三种距离
其实正半矢公式是余弦公式的一个转换,只是在相同纬度或者比较接近的两个位置距离求解的情况下更适用于计算机的精确计算,因为不容易丢失精度,但是由于摩尔定律,目前的64位的机器应该表现的越来越好,具体是否有所改善有待于实验。说到底求解的过程其实就是,当知道了两点的经度和纬度,然后求出球心到两点形成的两个向量的夹角,用夹角和地球半径就求出了球面距离;还有一种思路就是,直接在直角坐标系下求出两点的欧式距离,然后利用毕达哥拉斯定理(装逼说法,其实就是勾股定理)求出两个向量的夹角,然后同理求出球面距离。
参考文献
https://zh.wikipedia.org/wiki/%E5%A4%A7%E5%9C%86%E8%B7%9D%E7%A6%BB