唯杰地图 logo唯杰地图
VJMAP
EN

coord-offset-gaode

vjmap/references/trouble/coord-offset-gaode.md 5.3 KB142 行
原文

本文回答:叠加高德底图后 CAD 坐标偏 100~500 米 —— 忘了做 WGS84 ↔ GCJ02 转换。

一、症状

  • 叠加高德卫星图,CAD 图形和实际地形错位 100~500 米
  • 叠加百度底图,偏差更大(BD09)
  • 天地图 / OSM 不偏

二、根因:国内坐标系加密

中国大陆地图服务强制使用不同坐标系:

服务 坐标系
天地图 / OpenStreetMap / Google 中国外 WGS84(国际标准)
高德 / 腾讯 / Google 中国 GCJ02(火星坐标系,WGS84 偏转加密)
百度 BD09(GCJ02 再加偏)

CAD 坐标 →(cmdTransform EPSG→4326 得真 WGS84;GeoProjection.toLngLat 出的只是渲染经纬度、无地理意义)→ 没转 GCJ02 直接丢到高德 → 偏了。

三、转换方案

方案 A:叠加底图时给瓦片用正确坐标

vjmap 的高德瓦片源:

// ⚠️ 注意:d.ts 里声明的 map.addRasterLayer(id, source, props, before?)
// 在发布包里并不存在(min.js 无此方法),正确写法是 addSource + addLayer:
map.addSource('gaode-sat', {
  type: 'raster',
  tiles: ['https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}'],
  tileSize: 256
});
map.addLayer({ id: 'gaode-sat', type: 'raster', source: 'gaode-sat' });
// 高德瓦片本身就是 GCJ02 投影;但 vjmap 默认用 WGS84 坐标给瓦片 → 偏

真正解决:走服务端 svc.webMapUrl({ tileCrs: 'gcj02', tileUrl: [...], ... }) 代理瓦片,由后端按 GCJ02→CAD 坐标系换算后再下发(包内没有 gaodeRasterTile 这类前端封装函数,正确写法见 internet-map/gaode 模式 3)。

方案 B:CAD 坐标整体偏移

如果用的是自建底图叠加,CAD 点要放到 GCJ02 画面上:

// ⚠️ map.toLngLat(cad) 出的是渲染经纬度(线性映射),不是 WGS84——CAD→WGS84 要走服务端
async function cadToDisplayLngLat(cadPt) {
  const [wgs] = await svc.cmdTransform(cadEpsg, "EPSG:4326", vjmap.geoPoint(cadPt)); // 真 WGS84
  return vjmap.transform.convert(wgs, vjmap.transform.CRSTypes.WGS84,
                                 vjmap.transform.CRSTypes.GCJ02);                    // → GCJ02
}

(vjmap 内置的 vjmap.transform.convert 等价于 wgs84ToGcj02,不必引第三方纠偏库。)

详见 coord/internet-basemap-align。

方案 C:直接用支持 WGS84 的底图

  • OpenStreetMap
  • 天地图
  • ArcGIS World Imagery

这些不需要转换。

四、百度 BD09

百度再加偏一次:

WGS84 → GCJ02 → BD09

建议避开百度底图;或用 bd09ToGcj02 + 上面 C/B 方案。

五、确认坐标系偏差

// 找一个已知地标:天安门广场
// WGS84: 116.3913, 39.9053
// GCJ02: 116.397, 39.9069
// BD09:  116.4033, 39.9153

// 在你的图上点一下,看输出:
map.on('click', e => console.log(e.lngLat));
// 如果在天安门位置点击但输出 116.39 → WGS84
// 输出 116.397 → GCJ02

六、瓦片坐标转换(低级方案)

瓦片 URL 里 x/y 是墨卡托格网坐标,不是经纬度;但瓦片里的内容是 GCJ02 渲染。

几个选项:

  1. 用 vjmap 内置高德源(自动处理)
  2. 自己写 transformRequest 拦截并处理偏移(复杂)
  3. 服务端预纠偏的瓦片代理

七、Marker / Popup 位置

Marker 用 setLngLat 设 WGS84 → 高德底图上显示偏。解法:

const gcj = vjmap.transform.convert(wgs84Pt, vjmap.transform.CRSTypes.WGS84,
                                    vjmap.transform.CRSTypes.GCJ02);
new vjmap.Marker().setLngLat(gcj).addTo(map);

注意:map.on('click') 返回的 lngLat 是在当前底图坐标系里;如果底图是 GCJ02,click 事件返回的是 GCJ02。

八、查询服务

  • 高德 API 返回 GCJ02
  • 百度 API 返回 BD09
  • ArcGIS / 天地图 返回 WGS84

调用哪个 API 就用哪个坐标系,必要时转回业务统一的 WGS84 存库。

九、高德瓦片的常见 URL

https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}    # 普通(不透明道路图,只能单独做底图)
https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}                               # 卫星
https://webst01.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}                               # 路网注记(半透明,压在卫星上用这个)

十、常见坑

  1. 偏移量看起来"差不多"但业务对不上:GCJ02 偏移在中国大陆为 100~500 米;看起来接近但工程精度不够。
  2. 部分地区不偏(近港澳台):GCJ02 偏移算法对境外/近境外区域返回 WGS84 原值;误以为"修好了"。
  3. 用了纠偏库但参数反了:gcjToWgs vs wgsToGcj 记清;测一个已知点。
  4. 高精度要求:GCJ02 纠偏本身有误差(米级);精准定位需专用 RTK / 测绘级数据。
  5. 境外地图:开源纠偏库可能对国内准、境外乱;加范围判断。

相关