首先讲述一下为什么要用半正矢函数来计算,原因是因为,经纬度坐标是基于地球表面上的球面几何,而不是平面几何。直接使用平面几何公式(如欧几里得距离)计算两点间的距离,会因为地球的球面形状而产生较大的误差。球面上的两点距离是沿弧线的,而不是直线的距离。Haversine 公式通过半正矢函数计算角距离,再结合地球半径计算弧长,适应了球面的特性。
尽管半正矢函数(Haversine)在某些情况下已经不再是最精确的选择,但它依然适合用于计算经纬度之间的距离,尤其在以下几个方面:
1. 计算简便且效率高
Haversine公式的计算过程相对简单,公式只涉及到三角函数(正弦、余弦等)和基本的算数运算。对于大多数常规应用,计算速度非常快,尤其是当需要处理大量的地理数据时,Haversine函数能够在不增加过多计算负担的情况下提供快速的结果。
2. 足够的精度(在短距离情况下)
对于计算短距离(通常在几十公里范围内)的经纬度,Haversine公式已经足够精确。尽管它假设地球是一个完美的球体,而不是椭球体,这种假设在短距离的情况下不会引入显著误差。地球的曲率在较小范围内对结果的影响微乎其微,因此Haversine公式可以在这种情况下满足大多数应用的需求。
3. 适用于大多数常规应用
Haversine公式是大多数简单地理应用中常用的公式,特别是当计算精度要求不高时。很多场景下,比如城市范围内的导航、距离估算等,Haversine公式的精度已经足够满足需求,而其计算简便、实现容易,成为了广泛采用的标准方法。
4. 误差较小
尽管现代的算法(如Vincenty公式)提供了更高的精度,但Haversine公式在地理距离较短的情况下,误差非常小。对于短距离(几十公里以内),误差通常不会超过1公里,很多应用场景中并不需要超高精度,因此Haversine公式足够实用。
Haversine 公式推导
地球表面上两点的弧长距离可以用以下公式计算:
Φ₁和Φ₂表示两点的纬度(弧度表示)。
ΔΦ是两点纬度的差值。
Δλ是两点经度的差值。
然后使用以下公式计算弧长:
R 是地球半径(通常取 6371 公里)。arcsin 反正弦函数,用于将结果映射回角度值。
在 MaxCompute 上实现
MaxCompute 支持 SQL 提供了足够的函数来实现上述公式,例如 SIN、COS 和 POWER 等数学函数。
示例数据坐标取自于(链接:某德地图坐标)
id | latitude1 | longitude1 | latitude2 | longitude2 |
---|---|---|---|---|
1 | 23.108748 | 113.453471 | 23.120604 | 113.448547 |
SELECT
id,
6371 * 2 * ASIN(SQRT(
POWER(SIN((RADIANS(latitude2) - RADIANS(latitude1)) / 2), 2) +
COS(RADIANS(latitude1)) * COS(RADIANS(latitude2)) *
POWER(SIN((RADIANS(longitude2) - RADIANS(longitude1)) / 2), 2)
)) AS distance_km
FROM
locations;
结果图
同时用某德地图测算,计算结果准确!
使用 Python 实现 Haversine 公式
基于python的计算还是针对于点对点的方式,如果数据体量多建议还是使用SQL来实现,这样执行效率更为高效。
import math
def haversine(lat1, lon1, lat2, lon2):
"""
计算两点之间的地理距离(单位:公里)
:param lat1: 点 1 的纬度
:param lon1: 点 1 的经度
:param lat2: 点 2 的纬度
:param lon2: 点 2 的经度
:return: 距离(公里)
"""
# 将角度转换为弧度
lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
# Haversine 公式
dlat = lat2 - lat1
dlon = lon2 - lon1
a = math.sin(dlat / 2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2)**2
c = 2 * math.asin(math.sqrt(a))
# 地球半径(公里)
r = 6371
return c * r
# 示例
point1 = (23.108748, 113.453471)
point2 = (23.120604, 113.448547)
distance = haversine(*point1, *point2)
print(f"两点之间的距离是 {distance:.2f} 公里")
输出结果: