3、坐标系与投影:WGS84、GCJ02、BD09坐标系区别、坐标转换库的使用
做轨迹回放,第一个坑往往不是算法,而是坐标系。
我记得刚入行那会儿,拿到一批GPS数据直接往地图上怼,结果轨迹飘到了河对岸。当时我还以为是设备坏了,折腾了半天才发现——数据是WGS84,地图用的是GCJ02。嗯,坐标没对齐,啥都白搭。
3.1 三大坐标系,到底差在哪儿?
说白了,国内做地图开发,你绕不开这三个坐标系:
| 坐标系 | 全称 | 谁在用 | 特点 |
|---|---|---|---|
| WGS84 | World Geodetic System 1984 | GPS设备、Google Earth | 全球通用,原始卫星数据 |
| GCJ02 | 国测局坐标 | 高德、腾讯、Google中国 | WGS84加偏移,国内强制使用 |
| BD09 | 百度坐标 | 百度地图 | GCJ02再加一次偏移,百度独有 |
你想想看,GPS芯片拿到的原始数据是WGS84,但国内地图厂商出于合规要求,必须用GCJ02。百度更狠,在GCJ02基础上又加密了一层。所以同一个点,三个坐标系能差出几百米。
核心结论:轨迹回放时,数据源和底图必须用同一个坐标系。否则轨迹就是歪的。
3.2 坐标转换,怎么搞?
我在项目中遇到过最头疼的事:客户给了100万条历史轨迹,全是WGS84,但展示平台用的是百度地图。一条条转?不现实。这时候就得靠坐标转换库了。
3.2.1 proj4js —— 专业级投影转换
proj4js是前端GIS的瑞士军刀。它支持各种投影定义,适合做WGS84和GCJ02之间的互转,也能处理UTM、墨卡托等投影。
// 安装
npm install proj4
// 定义投影
const proj4 = require('proj4');
// WGS84 转 GCJ02(需要自定义投影参数)
proj4.defs('GCJ02', '+proj=longlat +ellps=WGS84 +no_defs');
// 转换示例
const [lng, lat] = proj4('WGS84', 'GCJ02', [116.397428, 39.90923]);
console.log(`转换后:${lng}, ${lat}`);
我的习惯:proj4js适合做批量转换,性能稳定。但要注意,它本身不内置GCJ02和BD09的加密算法,需要自己定义偏移参数。
3.2.2 coordtransform —— 国产轻量级方案
如果你只需要在国内坐标系之间互转,coordtransform更直接。它内置了WGS84、GCJ02、BD09的互转算法,开箱即用。
// 安装
npm install coordtransform
// 使用示例
const coordtransform = require('coordtransform');
// WGS84 转 GCJ02
const gcj02 = coordtransform.wgs84togcj02(116.397428, 39.90923);
console.log(`GCJ02:${gcj02[0]}, ${gcj02[1]}`);
// GCJ02 转 BD09
const bd09 = coordtransform.gcj02tobd09(gcj02[0], gcj02[1]);
console.log(`BD09:${bd09[0]}, ${bd09[1]}`);
// 反过来也行
const backToWgs84 = coordtransform.gcj02towgs84(gcj02[0], gcj02[1]);
避坑指南:我曾经在项目里直接用coordtransform转BD09到WGS84,结果发现百度地图的偏移是动态的,不同区域偏移量不一样。后来我改用百度官方提供的坐标转换API,才解决了精度问题。
3.3 坐标系转换的核心逻辑
为了让你更直观地理解,我画了一张图:
3.4 实战中的选择建议
我个人习惯这样选:
- 数据源是WGS84,底图是高德/腾讯 → 用coordtransform转GCJ02,简单直接
- 数据源是WGS84,底图是百度 → 先转GCJ02,再转BD09,两步走
- 需要高精度投影转换 → 用proj4js,自定义投影参数
- 批量处理大量轨迹点 → 建议后端用Python的pyproj或coordTransform_py,前端只做展示
最后提醒一句:坐标转换不是100%精确的。GCJ02和BD09的加密算法是单向的,反向转换会有几十米的误差。如果你的轨迹回放要求厘米级精度,建议直接用WGS84原始数据,配合支持WGS84的底图(比如天地图)。
嗯,坐标系这块儿就聊到这儿。下一节我们聊聊轨迹数据的清洗和预处理——那又是另一个坑了。