坐标系桥接(EPSG / 四参数 / 经纬度)
文档:
vjcad/doc/tutorial/web-map-overlay.md、vjcad/doc/tutorial/map-overlay.md示例:vjcad/example/basic/05mapoverlay.js
地图集成中最容易踩的坑就是 坐标系。本页梳理 vjcad 集成里出现的所有坐标空间,及其相互转换方式。
1. 涉及的几个坐标空间
| 空间 | 例子 | 用途 |
|---|---|---|
| CAD 内部坐标 | [100, 200] |
vjcad 引擎绘制 / 实体属性 |
| CAD 投影坐标 | EPSG:4509 等 CGCS2000 / 西安 80 / 北京 54 | 传统国家测绘投影 |
| Web Mercator | EPSG:3857 | 互联网瓦片标准 |
| WGS84 经纬度 | EPSG:4326 | GPS、谷歌地图、天地图 |
| GCJ02 经纬度 | "火星坐标" | 高德、腾讯(国内偏移) |
| BD09 | 百度坐标 | 百度地图(在 GCJ02 基础上再加密) |
2. 转换路径全景
flowchart LR
CAD[CAD 坐标 WCS] -- srs:EPSG 服务端 proj4 --> WGS84
CAD -- fourParameterBefore 四参数 --> WebMerc[EPSG:3857]
WebMerc -- 反算经纬度 --> WGS84
WGS84 -- tileCrs=gcj02 时服务端加偏 --> GCJ02
GCJ02 -- GCJ02↔BD09 需 gcoord 等 --> BD09
enableWebMap 的换算全在服务端:每块瓦片的 CAD bbox 按 srs(优先)或 fourParameterBefore 换成 WGS84,再按 tileCrs 抓瓦片;mapbounds 不参与。你要做的只是 声明 CAD 的坐标系(自动推断 EPSG 或手动给四参数)。
3. 自动模式(推荐):知道 EPSG
Engine.enableWebMap({
tileCrs: 'gcj02',
tileUrl: [...],
mapbounds: 'minX,minY,maxX,maxY', // TS 必填,不参与对齐
srs: 'EPSG:4509'
});
srs |
含义 |
|---|---|
EPSG:4326 |
WGS84 经纬度(很少在 CAD 内部用) |
EPSG:3857 |
Web Mercator |
EPSG:4509 |
CGCS2000 6 度带,中央经线 117°E(无带号前缀,X 为 6 位) |
EPSG:4526 |
CGCS2000 3 度带第 38 带(X 带 38 前缀) |
EPSG:4499 |
CGCS2000 6 度带第 21 带(X 带 21 前缀) |
EPSG:2362 |
西安 80 3 度带第 38 带 |
EPSG:2415 |
北京 54 3 度带第 39 带 |
详细 EPSG 范围见 web-map-overlay 第 5 节。
4. 手动模式:四参数(2D Helmert)
WebMercator(x, y) = Rotation(rotation) · Scale(scale) · CAD(x, y) + (dx, dy)
Engine.enableWebMap({
/* ... */
fourParameterBefore: 'dx,dy,scale,rotation'
// 如:'12727039.38, 3579066.69, 1, 0'
});
四个值含义:
| 参数 | 含义 | 单位 |
|---|---|---|
dx |
X 平移 | m |
dy |
Y 平移 | m |
scale |
缩放比例 | 倍数 |
rotation |
旋转角 | 弧度 |
何时用四参数?
- 图纸用了自定义本地坐标(厂区 / 工地)
- 已知一组对应点,能算出线性变换
- 不在标准 EPSG 列表里的老坐标系
5. 用对应点反算四参数
vjcad 命令面板 WEBMAPOVERLAY 自带"对应点 → 四参数"求解器:
- 在面板"手动 / 对应点"页签内
- 输入 ≥ 2 组
(CAD x, CAD y) ↔ (lng, lat)——经纬度 必须是 WGS84(面板内部按 WGS84→EPSG:3857 换算);从高德等 GCJ02 底图取的点要先转 WGS84,否则整体偏移数百米 - 勾选"不旋转"可锁
rotation = 0(默认勾选) - 点击"计算四参数"
实现上是最小二乘求解 2D Helmert:4 个未知量,每组对应点提供 2 个方程,因此最少 2 组点(4 方程,4 未知);推荐 4~10 组点取均匀分布的位置。
6. CAD 内部坐标 ↔ 经纬度
通过 CadMapOverlay 的内部映射:
// CAD 内部坐标 → 地理经纬度(返回对象 {lng, lat},不是数组)
const { lng, lat } = cadOverlay.cadToLngLat(cadX, cadY);
// 经纬度 → CAD 内部坐标(返回对象 {x, y};首参可传 lng 数字或 {lng,lat} 对象)
const { x: cadX, y: cadY } = cadOverlay.lngLatToCad(lng, lat);
注意:
cadToLngLat/lngLatToCad只是转调 vjmap 的map.toLngLat/map.fromLngLat,换算由map.attach(svc, prj)绑定的投影决定,与 overlay 的bounds无关。CAD 图在 vjmap 里通常用vjmap.GeoProjection(mapExtent)打开,得到的是把图纸范围线性铺到墨卡托上的 伪经纬度,不是真实地理坐标。
7. 与 vjmap 的 GeoProjection 配合
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
const lngLat = prj.toLngLat([cadX, cadY]); // CAD 坐标 → 伪经纬度
const cad = prj.fromLngLat([lng, lat]); // 伪经纬度 → CAD 坐标
map.attach(svc, prj) 之后 cadOverlay.cadToLngLat 与 prj.toLngLat 是同一套换算,精度没有差别,都只用于在这张 vjmap 地图上定位。
需要 真实经纬度(CAD 图是标准投影坐标)时,用服务端坐标转换 Engine.service.cmdTransform(srs, crs, points, fourParameter?, isInverseFourParamter?)(返回 [[x, y], ...]):
const pts = await Engine.service.cmdTransform(
'EPSG:4509', 'EPSG:4326',
[new GeoPoint(cadX, cadY)] // GeoPoint 或数组;可选第 4 参四参数 "dx,dy,scale,rotation"
);
const [lng, lat] = pts[0]; // WGS84 经纬度
8. GCJ02 / WGS84 / BD09 互转
vjcad 不内置坐标加密 / 解密;常用 gcoord 等库:
// CDN 加载 gcoord 后
const wgs84 = gcoord.transform([lng, lat], gcoord.GCJ02, gcoord.WGS84);
const gcj02 = gcoord.transform([lng, lat], gcoord.WGS84, gcoord.GCJ02);
const bd09 = gcoord.transform([lng, lat], gcoord.GCJ02, gcoord.BD09);
WebMap 集成中常见组合:
| 瓦片源 | tileCrs |
对应点经纬度(四参数求解用) |
|---|---|---|
| 高德 | 'gcj02' |
WGS84(服务端按 WGS84→GCJ02 拉瓦片;高德拾取的点先转 WGS84) |
| 天地图 | 'wgs84' |
WGS84 |
| 谷歌(境外) | 'wgs84' |
WGS84 |
| 百度 | 不支持(tileCrs 只有 'gcj02' | 'wgs84') |
— |
9. CadMapOverlay 的 bounds
CadMapOverlay 的 bounds 是 CAD 坐标范围:扁平四元组 [minX, minY, maxX, maxY]、带 toArray() 的对象(如 vjmap.GeoBounds)或 {min:{x,y}, max:{x,y}},不是经纬度对:
const cadOverlay = new CadMapOverlay({
bounds: mapExtent, // 或 [minX, minY, maxX, maxY](CAD 坐标)
serviceUrl, accessToken
});
它只用来计算 CAD 视图缩放比(在范围中心取样)和 zoomToExtents() 的范围;CAD 与地图的对齐由 map.attach(svc, prj) 的投影决定。
10. 需要真实地理位置时
CadMapOverlay 没有 srs 选项,叠出来的是 vjmap 伪经纬度坐标系下的位置。要让 CAD 落在真实底图(高德 / 天地图)上,用 Engine.enableWebMap({ srs | fourParameterBefore, ... }) 在 vjcad 里铺底图;要换算单点真实经纬度用 Engine.service.cmdTransform(见第 7 节)。
红旗
srs错配:把图纸坐标系 EPSG 写成了相邻带号 → 整张图横向偏移几公里tileCrs错配:CAD 与瓦片不在同一坐标系(GCJ02 / WGS84 弄反)→ 偏移 100~500mfourParameterBefore的rotation是弧度(不是度);写成30而不是30 * Math.PI / 180会"地图歪了"rotation ≠ 0时底图只是近似:服务端只把每块瓦片 bbox 的两个角点按四参数换算成经纬度范围,再按轴对齐矩形取图,不会旋转瓦片;建议保持 0(面板默认勾选"不旋转")CadMapOverlay的bounds是 CAD 坐标范围、不是经纬度,也不决定对齐;它没有srs,真实地理定位走enableWebMap/cmdTransform- BD09 / GCJ02 / WGS84 三套坐标 不能用减法直接转换,必须走加密 / 解密算法(gcoord 等)
- vjcad 内部不会主动校验 EPSG 是否存在;填错的 EPSG 通常表现为"地图不显示"或"完全错位"
- 测量场景下,建议用 4 个 GCP(角点 + 中心)做对应点求四参数;2 个点容易过拟合
参考(官方文档 MCP)
- 文档:
vjcad/doc/tutorial/web-map-overlay.md、vjcad/doc/tutorial/map-overlay.md - 示例:
vjcad/example/basic/05mapoverlay.js