> **本文回答**：叠加高德底图后 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 的高德瓦片源：

```js
// ⚠️ 注意：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](../internet-map/gaode.md) 模式 3）。

### 方案 B：CAD 坐标整体偏移

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

```js
// ⚠️ 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](../coord/internet-basemap-align.md)。

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

- OpenStreetMap
- 天地图
- ArcGIS World Imagery

这些不需要转换。

## 四、百度 BD09

百度再加偏一次：

```
WGS84 → GCJ02 → BD09
```

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

## 五、确认坐标系偏差

```js
// 找一个已知地标：天安门广场
// 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 → 高德底图上显示偏。解法：

```js
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. **境外地图**：开源纠偏库可能对国内准、境外乱；加范围判断。

## 相关

- 坐标系 → [coord/overview](../coord/overview.md)
- 互联网底图对齐 → [coord/internet-basemap-align](../coord/internet-basemap-align.md)
- 高德 → [internet-map/gaode](../internet-map/gaode.md)
- 百度 → [internet-map/baidu](../internet-map/baidu.md)
