不能直接用double做经纬度距离计算,因地球是椭球体、经纬度为球面坐标,平面欧氏距离忽略曲率与经度随纬度收缩,赤道误差达百米、高纬更甚;且double浮点误差叠加本初子午线不连续性会导致定位抖动,应使用geographiclib等高精度测地线库。

为什么不能直接用 double 做经纬度距离计算
地球是椭球体,经纬度是球面坐标,直接套用平面欧氏距离公式(比如 sqrt((lat1-lat2)^2 + (lon1-lon2)^2))在赤道附近误差可能达几百米,高纬度地区更夸张——例如在哈尔滨(北纬45°),经度差1度实际只有约78公里,但按平面算会当成111公里。更关键的是,double 虽有约15位有效数字,但当处理微小位移(如围栏边界偏移几厘米)或跨本初子午线(经度从179.9°跳到-179.9°)时,浮点累积误差和不连续性会让碰撞判定反复抖动。
用 geographiclib 库做高精度测地线距离判断
比手写 Vincenty 或 Haversine 更可靠:它基于 Karney 2013 年的完整椭球解法,支持 WGS84,单点距离误差
- 安装:
sudo apt-get install libgeographic-dev(Ubuntu)或从 geographiclib.sourceforge.io 源码编译 - 核心调用只用两行:
Geodesic::WGS84().Inverse(lat1, lon1, lat2, lon2, s12, azi1, azi2),其中s12是米制测地线距离 - 对多边形围栏,不能只测点到顶点距离——必须用射线法(Ray Casting)+ 测地线边判断:每条边用
Geodesic::WGS84().GenDirect分段采样,再用球面几何判断点是否在“边的左侧” - 注意:
Geodesic::WGS84()是无状态对象,可全局复用,别每次 new
围栏多边形怎么表达才不怕跨国际日期变更线或极点
用“地理坐标系下的闭合环”而非“经纬度数值序列”。直接存 vector<pair>></pair> 会出问题:当围栏横跨180°经线(如阿留申群岛周边),按数值排序的点序会把整个太平洋当成内部;极点附近,经度失去意义。
- 统一转为三维单位球面坐标:
x = cos(lat)*cos(lon), y = cos(lat)*sin(lon), z = sin(lat),所有运算在 R³ 中进行,天然规避经度跳变 - 判断点是否在球面多边形内,用球面三角形面积法:将待测点与每条边构成球面三角形,求和所有面积,若总和 ≈ 多边形面积(允许 1e-6 弧度² 误差),则在内部
- 存储时额外保存一个参考中心点(如质心),用于后续局部投影优化(见下一条)
实时检测怎么兼顾精度和性能(尤其在嵌入式或高频更新场景)
每帧都跑完整测地线计算太重。合理分层:先粗筛,再精判。
立即学习“C++免费学习笔记(深入)”;
- 第一层:用
GeographicLib::GeoCoords的Distance快速估算(基于球面近似,误差 2×围栏半径的点 - 第二层:对候选点,用中心点做方位角正交投影(
TransverseMercator),把局部区域映射成平面,用标准 O(n) 射线法判断——投影半径控制在 50km 内时,平面误差 - 第三层:仅对投影后落在边界 10 米内的点,触发完整
Geodesic::Inverse验证,避免因投影畸变误判 - 缓存:围栏顶点的三维坐标、投影参数、外接球半径预计算好,别在检测循环里重复算
真正难的不是算法本身,而是围栏定义时的坐标系混淆——比如把 GCJ-02 偏移坐标当 WGS84 输入,或混合使用度分秒和十进制度。精度再高,输入错了就全废。上线前务必用已知坐标的实地信标点交叉验证。


















