一、项目概述
MxCAD 与 Mapbox 结合项目是一个创新性的解决方案,旨在将在线CAD编辑功能与地图服务无缝集成。该项目通过自定义的Mapbox版本支持中国国家大地坐标系(CGCS2000),并结合MxCAD强大的在线CAD编辑能力,实现了在地图上直接加载、编辑和管理 CAD 图纸的功能。
核心技术栈包括:
- 修改版 Mapbox GL JS(支持 CGCS2000)
- MxCAD 在线 CAD 编辑引擎
- WebAssembly 技术
- TypeScript/JavaScript
二、Mapbox修改版支持CGCS2000 坐标系
CGCS2000 坐标系介绍
CGCS2000(China Geodetic Coordinate System 2000)是中国国家大地坐标系,是中国测绘基准的重要组成部分,用于替代原有的北京54坐标系和西安80坐标系。在国内 GIS 应用中,支持CGCS2000是必不可少的功能。
基于@cgcs2000/mapbox-gl 的坐标系扩展
项目使用了自定义的@cgcs2000/mapbox-gl
包,这是对标准 Mapbox GL JS 的扩展,主要增加了对 CGCS2000 坐标系的支持。在代码中的引用方式:
typescript
import {
type AnyLayer,
Map as _Map,
type AnySourceData,
LngLat,
Point,
} from "@cgcs2000/mapbox-gl";
import mapboxgl from "@cgcs2000/mapbox-gl";
我们对修改后的js进行了处理,使其支持CGCS2000坐标系和mxcad的交互。
坐标转换与投影实现
在使用时我们依然要扩展了 Map 类,扩展的方法都是必须的,用于配合 mxcad 实现坐标的转换和交互:
typescript
// 扩展重写Map类
export class Map extends _Map {
public dom_mousePos(event: any) {
return this.dom_mousePos_imp(this.getCanvasContainer(), event);
}
public lnglat_to_mercator(lng: any, lat: any): any {
let pt = mapboxgl.MercatorCoordinate.fromLngLat([lng, lat], 0);
return pt;
}
public mercator_to_lnglat(x: number, y: number, z: number): any {
let mecatorcoord = new mapboxgl.MercatorCoordinate(x, y, z);
return mecatorcoord.toLngLat();
}
public mercatorCoordinate_from_LngLat(
lngLat: number[],
modelAltitude: number
): any {
return mapboxgl.MercatorCoordinate.fromLngLat(lngLat as any, modelAltitude);
}
protected getScaledPoint(
el: HTMLElement,
rect: ClientRect,
e: MouseEvent | WheelEvent | Touch
) {
const scaling =
el.offsetWidth === rect.width ? 1 : el.offsetWidth / rect.width;
return new Point(
(e.clientX - rect.left) * scaling,
(e.clientY - rect.top) * scaling
);
}
protected dom_mousePos_imp(el: HTMLElement, e: MouseEvent | WheelEvent) {
const rect = el.getBoundingClientRect();
return this.getScaledPoint(el, rect, e);
}
}
三、MxCAD 与 Mapbox 的结合实践
MxCAD在线CAD编辑引擎与Mapbox集成
MxCAD编辑引擎,通过WebAssembly技术实现了高性能的CAD渲染和编辑功能,关键集成代码:
typescript
import { MxMap } from "mxcad";
let mx_map = new MxMap();
// 设置坐标点对齐,将CAD坐标系与地图坐标系对齐
// 图纸中的中心在地址上的位置,单位经纬度
let mapOrigin = [116.42787, 39.93232];
// 小=右,大=下
// CAD图纸中的中心中,CAD图纸单位
let cadOrigin = [506411.1543, 307348.2786];
// 1 CAD单位与米的比例 这里 1 cad单位是1m
let meterInCADUnits = 1;
mx_map.setCoordinatePointAlignment(mapOrigin, cadOrigin, meterInCADUnits);
const style = {
version: 8,
sources: {
tianditu: {
type: "raster",
tiles: [
"http://t0.tianditu.gov.cn/img_c/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=c&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=天地图的密钥",
],
tileSize: 256,
maxzoom: 18,
},
},
layers: [
{
id: "tianditu-layer",
type: "raster",
source: "tianditu",
minzoom: 0,
maxzoom: 18,
}
],
};
let map = new Map({
// Mapbox GL JS 进行地图渲染的 HTML 元素,或该元素的字符串 id 。该指定元素不能有子元素。
container: "map",
// crs: 'EGSP:4490',
// 地图最小缩放级别(0-24)。
minZoom: 0,
// 地图最大缩放级别(0-24)。
maxZoom: 24,
// 地图初始化时的地理中心点
center: mapOrigin,
// 地图初始化时的层级
zoom: 16,
// 地图的 Mapbox 配置样式
style: style,
});
// 你需要打开的图纸
let cadFile = new URL("../../public/demo/line3.dwg.mxweb", import.meta.url).href;
// 在地图加载完成后初始化MxCAD
map.on("style.load", async function () {
// 设置canvas ID
map.getCanvas().id = "myCanvas";
// 创建MxCAD实例
mx_map.create(map, {
locateFile: (fileName: string) => {
return new URL(
`../../node_modules/mxcad/dist/wasm/${mode}/${fileName}`,
import.meta.url
).href;
},
fileUrl: cadFile,
middlePan: true,
viewBackgroundColor: load_local_title
? { red: 0, green: 0, blue: 0 }
: { red: 255, green: 255, blue: 255 },
});
});
坐标系统对齐实现
坐标系统对齐是 MxCAD 与 Mapbox 结合的核心。我们通过setCoordinatePointAlignment
方法将 CAD 坐标系与地图坐标系进行对齐:
typescript
// 图纸中的中心在地址上的位置,单位经纬度
let mapOrigin = [116.42787, 39.93232];
// CAD图纸中的中心中,CAD图纸单位
let cadOrigin = [506411.1543, 307348.2786];
// CAD单位与米的比例
let meterInCADUnits = 1;
// 设置坐标点对齐
mx_map.setCoordinatePointAlignment(mapOrigin, cadOrigin, meterInCADUnits);
这样设置后,当用户在地图上点击时,可以获取对应的 CAD 坐标:
typescript
map.on("click", async function (e) {
let { lng, lat } = e.lngLat;
// 获取墨卡托坐标
let pt = mapboxgl.MercatorCoordinate.fromLngLat([lng, lat], 0);
// 转换为CAD坐标
let ptCAD = mx_map.mercatorCoord2CAD(pt.x, pt.y);
console.log("CAD坐标:", JSON.stringify(ptCAD));
});
四、天地图CGCS2000加载实现
天地图服务介绍
天地图是中国国家测绘地理信息局主办的国家地理信息公共服务平台,提供了基于 CGCS2000 坐标系的地图服务。在本项目中,我们集成了天地图作为底图服务。
天地图key申请流程
- 访问天地图开发者平台:lbs.tianditu.gov.cn/
- 注册并登录开发者账号
- 选择地图API
- 申请key
- 在"控制台"页面选择"创建应用"
- 填写应用名称、应用类型等信息
- 提交后获取应用 key
- 使用获取的 key 替换代码中的 tk 参数

天地图CGCS2000图层配置
在项目中,我们通过以下方式配置天地图图层:
typescript
const style = {
version: 8,
sources: {
tianditu: {
type: "raster",
tiles: [
"http://t0.tianditu.gov.cn/img_c/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=c&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=",
],
tileSize: 256,
maxzoom: 18,
},
},
layers: [
{
id: "tianditu-layer",
type: "raster",
source: "tianditu",
minzoom: 0,
maxzoom: 18,
}
]
} as mapboxgl.Style;
注意:在实际使用时,需要将上述代码中的tk=
后面添加您申请的天地图key。
天地图提供了多种图层类型,常用的包括:
img_c
: 影像底图vec_c
: 矢量底图cva_c
: 矢量注记cia_c
: 影像注记
可以根据需求选择不同的图层类型。
集成mxcad在线CAD项目地图模式
刚刚我们介绍了从0到1的MxCAD与Mapbox结合实现,但是所有CAD的功能都要从头开发, 所以我们提供了在线CAD项目集成方案, 同时可以启动地图模式, 简单开发一个扩展插件轻松集成 MxCAD 与 Mapbox 结合实现在线地图 CAD 编辑系统。
开发前我们一定要下载MxDraw云图开发包然后解压可以看到一个exe程序点击运行点击"打开MxCAD代码开发目录" 就可以看到dist目录了,查看详情。
扩展插件开发
通过开发一个简单的插件,我们可以快速集成MxCAD与Mapbox。以下是一个基本的插件代码示例:
javascript
import { MxCADPluginBase, MxCADUI, MxMap } from "mxcad";
import { MxFun } from "mxdraw";
import * as mapboxgl from "mapbox-gl";
import { Map } from "mapbox-gl";
class MxCADPlugin extends MxCADPluginBase {
constructor() {
super()
this.map_default_data = {
/** 地图与CAD图纸的对齐位置 */
mapOrigin: [116.42787, 39.93232],
/** CAD图纸与地图的对齐点 */
cadOrigin: [506411.1543, 307348.2786],
meterInCADUnits: 1,
/** mapbox地图token */
mapbox_accessToken: '',
/** 需要打开的cad图纸 */
openFile: new URL("../demo/line3.dwg.mxweb", import.meta.url).href,
/** 栅格瓦片图层列表 */
rasterTileLayerList: []
}
}
}
let mxcadui;
// cad应用加载开始
MxFun.on("mxcadApplicationStart", (mxcaduiimp) => {
mxcadui = mxcaduiimp;
mxcadui.init(new MxCADPlugin());
});
let mx_map;
let map;
// 初始化gis
MxFun.on("mxcadApplicationInitMap", () => {
mx_map = mxcadui.mxmap;
map = mx_map.getMapbox()
map.getCanvas().id = "mxcad"
// 可以在这里设置地图样式
// map.setStyle({
// version: 8,
// sources: {
// tianditu: {
// type: 'raster',
// tiles: [
// 'http://t0.tianditu.gov.cn/img_c/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=c&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=密钥'
// ],
// tileSize: 256,
// maxzoom: 18
// }
// },
// layers: [
// {
// id: 'tianditu-layer',
// type: 'raster',
// source: 'tianditu',
// minzoom: 0,
// maxzoom: 18
// }
// ]
// })
map.on("click", async function (e) {
let { lng, lat } = e.lngLat;
let pt = mapboxgl.MercatorCoordinate.fromLngLat(
[lng, lat],
0
);
console.log("经纬度坐标:", JSON.stringify([lng, lat]));
let ptCAD = mx_map.mercatorCoord2CAD(pt.x, pt.y);
console.log("CAD坐标:", JSON.stringify(ptCAD))
});
});
插件部署与配置
1.插件文件结构
将上述JavaScript代码保存为一个index.js文件,放置在dist/plugins/mapPlugin/
目录下(目录名可自定义)。
2.配置config.json
在dist/plugins/config.json
文件中添加插件配置:
json
{
"plugins": \[
"loginPlugin",
{"name": "pluginCodeEdit", "isAfterLoad": true, "dir": true, "version": "1.0.0"},
{"name": "pluginIdentifyPattern", "isAfterLoad": true, "dir": true, "version": "1.0.0"},
{"name": "pluginBaseTemplate", "isAfterLoad": false, "dir": true, "version": "1.0.0"},
{"name": "mapPlugin", "isAfterLoad": false, "dir": true, "version": "1.0.0"}
]
}
注意:
- 插件名称应与目录名一致
isAfterLoad
表示是否在主应用加载完成后再加载插件dir
设置为true表示插件在独立目录中
启动地图模式
访问应用时,在URL后添加?map=true
参数即可启动地图模式,例如:
ini
http://your-domain.com/mxcad/?map=true
集成到现有项目
您可以通过以下方式将MxCAD地图模式集成到现有项目中:
- 使用iframe嵌入
html
<iframe src="http://your-domain.com/mxcad/?map=true" width="100%" height="600px"></iframe>
2.页面通信
通过postMessage实现父页面与iframe中的MxCAD应用通信:
js
// 父页面发送消息
const mxcadFrame = document.getElementById('mxcad-frame');
mxcadFrame.contentWindow\.postMessage({
type: 'COMMAND',
command: 'ZOOM\_TO',
data: { center: \[116.42787, 39.93232], zoom: 16 }
}, '\*');
// 在MxCAD插件中接收消息
window\.addEventListener('message', (event) => {
if (event.data.type === 'COMMAND') {
// 处理来自父页面的命令
handleCommand(event.data);
}
});
function handleCommand(data) {
if (data.command === 'ZOOM\_TO' && map) {
map.flyTo({
center: data.data.center,
zoom: data.data.zoom,
essential: true
});
}
}
通过这种方式,您可以在自己的项目中轻松集成MxCAD与Mapbox结合的在线地图CAD编辑系统,同时保留对地图和CAD操作的完全控制能力,同时集成了mxcad在线CAD项目的各种功能。
坐标系统选择注意事项
在集成MxCAD与Mapbox时,坐标系统的选择非常重要:
1.CGCS2000与WGS84的区别
- 本项目中采用的是CGCS2000坐标系
- 默认的Mapbox使用的是WGS84坐标系
- 两种坐标系在中国区域的偏差约为几米到几十米不等
2.坐标系选择建议
- 如果您的项目需要精确对应中国国内地理位置,建议使用修改版的Mapbox CGCS2000
- 使用CGCS2000时,所有经纬度输入和输出都是CGCS2000坐标系下的值
- 如果对精度要求不高或主要用于国际项目,可以使用默认的Mapbox(WGS84)+ 补丁方式
- 使用默认WGS84时,无需替换为修改版的Mapbox库
3.坐标转换
如果需要在两种坐标系统之间进行转换,可以使用相关转换工具或库,例如proj4js:
js
// 使用proj4js进行坐标转换示例
import proj4 from 'proj4';
// 定义坐标系
proj4.defs('EPSG:4326', '+proj=longlat +datum=WGS84 +no\_defs'); // WGS84
proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no\_defs'); // CGCS2000
// WGS84转CGCS2000
const wgs84Point = \[116.3912, 39.9073]; // 北京某点WGS84坐标
const cgcs2000Point = proj4('EPSG:4326', 'EPSG:4490', wgs84Point);
console.log('CGCS2000坐标:', cgcs2000Point);
选择合适的坐标系对于项目的精确定位至关重要,特别是在涉及到工程测量、规划设计等高精度应用场景中。