你手机里的GPS,正在用三种不同的方式欺骗你——而你写代码时,大概率只用过最蠢的那种。
一个用Termux在Android上写位置闹钟的开发者最近发现:当他用勾股定理算两个GPS点的距离时,结果和实际差了几十公里。不是代码错了,是地球根本不是平的。
第一个陷阱:把经纬度当直角坐标系
新手写定位功能,十有八九会踩这个坑:
把纬度和经度直接代入勾股定理,算出来的"距离"单位是"度"——而1度经度在赤道是111公里,到了北极几乎归零。
这位开发者最初就是这么写的:`sqrt((lat2-lat1)^2 + (lon2-lon1)^2)`。代码跑得通,结果完全不能用。这就像用卷尺量地图上的直线,却忘了地图是球面拍扁的。
问题在于:经纬度是角度,不是长度。地球表面没有统一的"网格单位",任何直接把经纬度当XY坐标算的公式,本质上都是在假设地球是个圆柱体。
第二个陷阱:Haversine公式的"完美球体"幻觉
稍微查过资料的程序员会换用Haversine公式——它用球面三角学算大圆距离,确实比勾股定理靠谱多了。但这里有个隐蔽的假设:地球是个正球体。
实际上地球是个扁球体,赤道半径6378公里,极半径6357公里,差了21公里。对于短距离导航,Haversine的误差通常在0.5%以内,看起来够用了。
但"够用"和"正确"是两回事。当你的位置闹钟要在500米外触发,而Haversine给你算出了520米,用户就会在公交车上多睡20米——或者提前被吵醒。
更麻烦的是,这个误差方向不固定。取决于你在北半球还是南半球、往东走还是往西走,同一个公式会给出不同方向的偏差。
Geodesic距离:把地球当成真正的地球
真正的解决方案叫Geodesic距离(测地线距离)——沿着地球真实表面两点间的最短路径。想象一根绷紧的绳子贴在地球仪上,那就是测地线。
计算它需要用到椭球体模型,比如WGS-84——这才是GPS卫星实际使用的参考系。代码复杂度陡增:要解椭圆积分,要迭代逼近,要处理赤道隆起和极地扁平。
这位开发者最终用了Python的`geopy`库,一行代码调包解决。但搞清楚背后原理后,他意识到一个反直觉的事实:哪怕只是坐公交打盹这种"短距离"场景,地球曲率的影响也比你想象的大。
在纬度40度的地方(北京、纽约、马德里),东西方向走1公里,Haversine和真实测地线的偏差约为15厘米。听起来很小?但如果你的闹钟阈值设成200米,叠加GPS本身的10-20米误差,系统已经在误差边缘跳舞了。
为什么这件事值得程序员较真
位置服务有个特点:用户感知极强,容错极低。地图偏了50米,你可能找不到咖啡店;闹钟晚了30秒,你可能坐过站。
更深层的问题是:我们太容易把"能跑"当成"对"。勾股定理能跑,Haversine能跑,Geodesic也能跑——但三者给出的数字,在物理意义上完全不同。
这位开发者在博客结尾说,他写这个闹钟是为了"在公交车上安心睡觉"。最后他确实睡成了,但不是因为代码完美,而是因为公交速度够慢、GPS刷新够频繁、阈值设得够宽松。
如果下次你写定位功能,会为了那0.3%的精度差异多引入一个依赖库吗?还是说,"差不多就行"才是工程务实的真谛?
热门跟贴