Python 经纬度距离计算:从鸿蒙签到案例拆解 Haversine
浏览鸿蒙开发实践中心的员工签到案例时,看到它使用经纬度和 Haversine 公式判断员工与签到点的距离。下面把其中的距离计算单独拿出来,用 Python 标准库做一个可运行的小实验。本文只验证球面距离函数,没有部署案例中的 App 或后端。
从坐标到距离
经纬度的单位通常是度,而三角函数使用弧度。Haversine 先通过两点纬度、经度差计算球面中心角,再乘地球半径,得到沿球面的近似距离。这里使用平均半径 6,371,000 米,结果单位为米。真实地球并非完美球体,因此这适合一般距离估算,不能直接当作精密测绘结果。
一个可运行的实现
函数接收两个点,参数顺序为纬度、经度。先拒绝越界坐标和 NaN 等非有限值,再计算中心角。浮点误差有时会使中间值略微越过 0 或 1,因此在开平方前将其约束到有效范围。
from math import asin, cos, isfinite, radians, sin, sqrt
EARTH_RADIUS_M = 6_371_000
def distance_m(lat1, lon1, lat2, lon2):
values = (lat1, lon1, lat2, lon2)
if not all(isinstance(v, (int, float)) and not isinstance(v, bool) and isfinite(v) for v in values):
raise ValueError('坐标必须为有限数字')
if not (-90 <= lat1 <= 90 and -90 <= lat2 <= 90 and -180 <= lon1 <= 180 and -180 <= lon2 <= 180):
raise ValueError('纬度应在[-90,90],经度应在[-180,180]')
p1, p2 = radians(lat1), radians(lat2)
dp, dl = p2 - p1, radians(lon2 - lon1)
a = sin(dp / 2) ** 2 + cos(p1) * cos(p2) * sin(dl / 2) ** 2
return 2 * EARTH_RADIUS_M * asin(sqrt(min(1.0, max(0.0, a))))
if __name__ == '__main__':
assert distance_m(0, 0, 0, 0) == 0
assert 111_000 < distance_m(0, 0, 1, 0) < 111_300
assert abs(distance_m(31.2, 121.5, 30.2, 120.5) - distance_m(30.2, 120.5, 31.2, 121.5)) < 1e-8
assert 20_000_000 < distance_m(0, 0, 0, 180) < 20_100_000
for sample in [(91, 0, 0, 0), (0, 181, 0, 0), (float('nan'), 0, 0, 0)]:
try:
distance_m(*sample)
raise AssertionError('invalid coordinates accepted')
except ValueError:
pass
print('7 checks passed')
保存为 distance_demo.py 后用 Python 运行。如果断言全部通过,会输出 7 checks passed。测试覆盖同一点距离为零、赤道附近相差一纬度约 111 公里、交换两点顺序、对跖点,以及三种非法输入。它们能发现单位、参数范围和数值稳定性的常见错误,但不能证明所有输入都正确。
怎样用于签到判断
服务端可先校验签到点配置和员工位置,再比较 distance_m 的返回值与允许半径。要约定边界:例如距离小于或等于 100 米算在范围内;计算过程不要先把距离四舍五入,否则边界附近可能误判。存储时应保留足够精度,并在日志中使用适当的隐私保护措施。
距离接近阈值时,还应考虑定位精度、坐标系差异、位置上报时间和定位权限;坐标缺失或精度不足时,应返回明确提示,而不是默认为签到成功。距离函数也不能独立证明位置真实或取代服务端权限校验。
小结
这个练习把案例里的一个计算点拆成了独立函数:统一单位、校验输入、处理浮点边界,再用可重复的断言检查关键性质。资料来源:华为云鸿蒙开发实践中心《使用 Agent 生成鸿蒙端云一体员工签到 App》;文中 Python 示例为本次独立编写。
- 点赞
- 收藏
- 关注作者
评论(0)