唯杰地图 logo唯杰地图
VJMAP
EN

坐标系桥接(EPSG / 四参数 / 经纬度)

vjcad/references/map-integration/coords-bridge.md 8.2 KB183 行
原文

文档: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 自带"对应点 → 四参数"求解器:

  1. 在面板"手动 / 对应点"页签内
  2. 输入 ≥ 2 组 (CAD x, CAD y) ↔ (lng, lat)——经纬度 必须是 WGS84(面板内部按 WGS84→EPSG:3857 换算);从高德等 GCJ02 底图取的点要先转 WGS84,否则整体偏移数百米
  3. 勾选"不旋转"可锁 rotation = 0(默认勾选)
  4. 点击"计算四参数"

实现上是最小二乘求解 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~500m
  • fourParameterBefore 的 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