地图定位为什么偏了 500 米?GPS 与火星坐标实测

把北京一处 WGS-84 坐标 39.904200, 116.407400 输进这个页面,它算出的 GCJ-02 是 39.905603, 116.413642。

两个数字一减,位置被挪走了 554.85 米,方向 73.66°。

广州更远,620.97 米;乌鲁木齐最近,也有 265.02 米。

这不是算错了,是国内地图的通行规则。

在线案例与源码:atomgit.com/deli007/dem...

一、为什么值得看:那 500 米偏在哪

手机 GPS 拿到的坐标叫 WGS-84,是国际通用的坐标系。

高德、腾讯这些国内地图用的是 GCJ-02,俗称火星坐标。

两者原点不同,中间隔着一层加密偏移。

把 GPS 原始坐标直接丢给国内地图,点会落在几百米外。

反过来,把地图上取到的坐标交给需要 WGS-84 的设备,误差一样存在。

做定位打卡、轨迹回放、无人机航点、跨平台数据对账的人,迟早会撞上这件事。

于是我把这层偏移做成了一个单页网页:输入任意坐标,看它偏多少、偏到哪个方向。

二、先给结论:8 个城市的偏移实测

页面预置了 8 个城市,点一下就把 WGS-84 坐标填进去重算。

下表的偏移距离和方位角都是页面实时算出来的,方位角以正北为 0、顺时针增大。

城市 WGS-84 纬度,经度 GCJ-02 纬度,经度 偏移距离 方位角
广州 23.129100, 113.264400 23.126423, 113.269730 620.97 m 118.64°
深圳 22.543100, 114.057900 22.540383, 114.063014 605.91 m 119.91°
北京 39.904200, 116.407400 39.905603, 116.413642 554.85 m 73.66°
上海 31.230400, 121.473700 31.228458, 121.478223 481.25 m 116.66°
西安 34.341600, 108.939800 34.340044, 108.944456 461.12 m 112.03°
成都 30.572800, 104.066800 30.570346, 104.069305 363.30 m 138.68°
拉萨 29.650000, 91.100000 29.647300, 91.101568 336.28 m 153.21°
乌鲁木齐 43.825600, 87.616800 43.826805, 87.619650 265.02 m 59.62°

极差 355.95 米,最大和最小差了 1.34 倍。

方向也散得厉害。广州朝东南偏东,乌鲁木齐朝东北,拉萨朝东南。

三、准备环境:进入码道 Web

浏览器打开码道 Web 版:devcloud.cn-north-4.huaweicloud.com/chat?source...

登录后直接在对话窗口输入需求,不用装软件。

码道有三种使用方式:WebUI 浏览器对话、TUI 终端命令行和桌面 IDE 插件。本文用 WebUI 版演示。

四、需求原文:把要验的东西先写死

给码道的需求里,关键的几条是这些。

  1. 输入 WGS-84 经纬度,输入即算,实时显示 GCJ-02、偏移距离和方位角。
  2. 北京、上海、广州、深圳、成都、西安、拉萨、乌鲁木齐 八个预设按钮。
  3. GCJ-02 转回 WGS-84 用迭代逼近,最多 10 次,显示往返误差。
  4. 一行一个城市的对照表,按偏移距离从大到小排。
  5. canvas 画出原始点、偏移点和两点连线,连线旁标注偏移米数。
  6. 页面上放一个「运行自检」按钮,内置至少 8 条断言,点击后显示通过数和失败原因。
  7. 输入非法时给中文提示,不抛异常、页面不崩。
  8. 深色科技风,中文界面。

第 6 条是有意写死的:页面上有可点开的断言,文章里的测试数字才有出处。

五、页面上有哪几块

左侧是输入与结果面板,上面填经纬度,下面列 GCJ-02、偏移距离、方位角、反解结果和往返误差。

右上角是偏移可视化,红点是原始点,蓝点是偏移点,中间那条虚线就是这 500 米。

右侧下方是 8 城对照表和自检断言。

六、代码亮点:椭球、多项式与反向迭代

WGS-84 转 GCJ-02 的公开实现,核心是 Krasovsky 1940 椭球的参数再加两组正余弦多项式。

页面里长半轴取 6378245.0,偏心率平方取 0.00669342162296594323。

多项式的自变量是经度减 105、纬度减 35,这是国内流传最广的一版拟合写法。

反向转换没有解析解,只能迭代。先假设 WGS-84 等于 GCJ-02,正算一次,用误差修正,再算一次。

页面最多迭代 10 次,或者两次误差都小于 1e-9 就停。

北京 3 次就收敛,其余点位 4 次。

距离用 Haversine,地球半径取 6371000 米;方向用初始方位角公式,正北为 0。

canvas 用等距投影,经度方向乘纬度余弦,画面比例才不会失真。

七、验证与踩坑

先说页面自检,9 条断言全部通过。

断言 实测值
北京偏移在 300~700 米之间 554.85 米
WGS84→GCJ02→WGS84 往返误差 < 0.01 米 最大 0.00000006 米
所有城市偏移 > 100 米 最小 265.02 米
方位角落在 [0,360) 全部在范围内
纬度偏移量 < 0.01° 最大 0.002717°
经度偏移量 < 0.01° 最大 0.006242°
GCJ02→WGS84→GCJ02 闭合误差 < 1e-6° 最大 4.974e-13°
纬度影响经度方向偏移 北京 0.006242°、广州 0.005330°、差 0.000913°
城市之间偏移差异显著 极差 355.95 米

第二步,我拿 Python 把 8 个城市又算了一遍。

用同一套 Krasovsky 参数和多项式实现正向转换,再用 Haversine 算距离、用初始方位角算方向。

8 个城市的 GCJ-02 经纬度与页面逐位一致,偏移距离差 0.00 米,方位角差 0.00°。

这一步的意义在于,页面的数字不是自己说对就对的,它被另一套代码复算过。

第三步,三处真实边界,我留着没改。

第一处是境外坐标。页面没有中国范围判断,东京 35.6895, 139.6917 也被算出 377.60 米的偏移,方位 85.14°。

赤道上的 0,0 更离谱,给出 1749.86 米;悉尼 354.36 米。

GCJ-02 只在中国境内生效,境外本该原样返回,标准实现会先判断坐标是否落在国内。

第二处是极点。纬度填 90 能通过校验,因为代码拦的是大于 90。

这时候余弦变成 0,经度那一项直接爆炸:页面算出 GCJ-02 经度 101052942616760.53,往返误差变成 NaN,迭代 10 次也没收敛。

第三处是失败态下的画面。输入非法值后,左侧数字面板会退回「等待有效输入...」,但右侧 canvas 还留着上一次有效的图形。

输入纬度 91,报错是「纬度超出范围,应在 -90 ~ 90 之间。」,而画布上仍是上一站的 265.0 m 与 59.6°。

八、本地复现与亲手点三下

页面是单文件,把 index.html 下载下来双击就能打开,没有依赖也没有后端。

想用本地服务的方式打开,在文件所在目录跑一行命令。

bash 复制代码
python -m http.server 8080
# 浏览器打开 http://127.0.0.1:8080/index.html

亲手点三下,顺序建议这样。

  1. 看关键数值:点「北京」,偏移距离应该是 554.85 米,方位角 73.66°。
  2. 换一个状态:点「广州」,数字跳到 620.97 米和 118.64°,连线方向跟着转。
  3. 看失败路径:纬度改成 91,应该看到「纬度超出范围,应在 -90 ~ 90 之间。」;改成 abc,看到「输入包含非数字字符,请只输入小数(如 39.9042)。」

九、用码道的体会

这一轮里,需求写清楚比写得漂亮重要。

「页面上要有自检按钮,内置至少 8 条断言」这一句,换来了交付时可以直接点开的 9 条断言。

「反向转换用迭代逼近,显示往返误差」这一句,把最容易糊弄过去的一步变成了可验证的数字。

另一条体会是边界要自己主动试。

境外坐标、极点、失败态画布这三处,都是交付之后我自己试出来的。

如果不主动喂极端输入,页面看上去是完美的。

十、直接下载试玩

十一、下一步

一个明显的改进是把境外判断补上,让中国以外的坐标原样返回。

再往后可以把偏移场画成一片箭头图,一眼看出全国各地方向的走向。

相关推荐
TK泰妞1 小时前
节日促销前,TikTok 商品视频应该提前准备什么?
前端·人工智能·节日
CCIE-Yasuo1 小时前
网络安全-学习web开发-列表、标签
前端·学习·web安全
小大宇1 小时前
Vue知识点-ElementPlus-06 单选+多选+下拉+日期选择
前端·vue.js·elementui
用户11481867894841 小时前
Nuxt 3 学习版 Demo:跟着做一遍,从跑起来到自己会改
前端
hiahiahia1231 小时前
Streaming Model Output
开发语言·前端
paopaokaka_luck2 小时前
基于springboot+vue的益暖微光公益服务平台(DeepSeek公益助手、善款信息追溯、志愿服务积分闭环、ECharts运营分析、活动状态自动流转)
java·前端·javascript·spring boot·mysql·架构·echarts
栀椩2 小时前
Electron 内嵌页面跨域问题(CORS/PNA)解决全记录
前端·javascript·electron
asaotomo2 小时前
截图识题、AI 讲解、笔记沉淀:Chrome 扩展体验
前端·chrome·笔记
前端架构师-老李2 小时前
HTML与CSS基础第五课—HTML的空格、换行、横线相关知识
前端·css·html