将 WebCAD 叠加到 vjmap 地图上,实现 CAD 与 GIS 的融合显示。
CadMapOverlay 是一个轻量级的 CAD 叠加层,可以添加到 vjmap 地图上,支持:
- 在地图上显示 CAD 实体
- 实体选择和交互
- 视图同步(拖动/缩放/旋转)
- 独立的坐标系管理
// 动态加载 vjmap(如果尚未加载)
const loadVjmap = () => {
return new Promise((resolve, reject) => {
if (window.vjmap) {
resolve(window.vjmap);
return;
}
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://vjmap.com/demo/js/vjmap/vjmap.min.css';
document.head.appendChild(link);
const script = document.createElement('script');
script.src = 'https://vjmap.com/demo/js/vjmap/vjmap.min.js';
script.onload = () => resolve(window.vjmap);
script.onerror = () => reject(new Error('vjmap 加载失败'));
document.head.appendChild(script);
});
};
const vjmap = await loadVjmap();
// 创建 vjmap 服务
const svc = new vjmap.Service(env.serviceUrl, env.accessToken);
// 打开地图
const res = await svc.openMap({
mapid: 'sys_zp',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle()
});
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
// 创建地图实例
const map = new vjmap.Map({
container: 'map',
style: svc.vectorStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 1,
pitch: 0,
renderWorldCopies: false
});
map.attach(svc, prj);
await map.onLoad();
const { CadMapOverlay, LineEnt, CircleEnt, TextEnt } = vjcad;
// 创建 CAD 叠加层
const cadOverlay = new CadMapOverlay({
bounds: mapExtent, // 地图范围
serviceUrl: env.serviceUrl, // 服务地址
accessToken: env.accessToken, // 访问令牌
themeMode: 0, // 主题:0=深色, 1=浅色
enableSelection: true, // 启用选择
smoothAnimation: true, // 平滑动画
onSelectionChanged: (selection) => {
if (selection.length === 0) {
console.log("选择已清空");
} else {
console.log(`选中 ${selection.length} 个实体`);
selection.forEach((ent, i) => {
console.log(` [${i + 1}] ${ent.type}`);
});
}
}
});
// 添加到地图
await cadOverlay.addTo(map);
const { LineEnt, CircleEnt, TextEnt } = vjcad;
// 获取 CAD 坐标范围
const bounds = cadOverlay.getCadBounds();
const [minX, minY, maxX, maxY] = bounds;
// 创建实体
const entities = [];
// 直线
const line = new LineEnt([minX + 100, minY + 100], [maxX - 100, maxY - 100]);
line.setDefaults();
line.color = 1;
entities.push(line);
// 圆
const centerX = (minX + maxX) / 2;
const centerY = (minY + maxY) / 2;
const circle = new CircleEnt([centerX, centerY], 50);
circle.setDefaults();
circle.color = 3;
entities.push(circle);
// 文字
const text = new TextEnt();
text.insertionPoint = [centerX, centerY + 100];
text.text = "WebCAD";
text.height = 20;
text.setDefaults();
text.color = 5;
entities.push(text);
// 批量添加实体
cadOverlay.addEntities(entities);
// 缩放到全图
cadOverlay.zoomToExtents();
// 显示/隐藏叠加层
cadOverlay.setVisible(true); // 显示
cadOverlay.setVisible(false); // 隐藏
// 获取 CAD 坐标范围
const bounds = cadOverlay.getCadBounds();
// 返回: [minX, minY, maxX, maxY]
// 启用/禁用选择
cadOverlay.enableSelection = true;
cadOverlay.enableSelection = false;
// 清除选择
cadOverlay.clearSelection();
// 监听选择变化(构造函数中配置)
const cadOverlay = new CadMapOverlay({
// ...
onSelectionChanged: (selection) => {
console.log(`选中 ${selection.length} 个实体`);
}
});
// 获取内部的 MainView 实例
const cadView = cadOverlay.getCadView();
// 可以使用完整的 WebCAD API
const Engine = cadView.Engine;
Engine.zoomExtents();
| 选项 | 类型 | 说明 |
|---|
bounds | GeoBounds | 地图范围(必需) |
serviceUrl | string | 后端服务地址 |
accessToken | string | 访问令牌 |
themeMode | number | 主题:0=深色, 1=浅色 |
enableSelection | boolean | 是否启用选择(默认 true) |
smoothAnimation | boolean | 是否启用平滑动画(默认 true) |
onSelectionChanged | function | 选择变化回调 |
| 值 | 行为 |
|---|
true | 使用 CSS transform 平滑过渡,动画结束后重绘 |
false | 实时重绘模式,响应更及时但可能有抖动 |
| 方法 | 说明 |
|---|
addTo(map) | 添加到 vjmap 地图 |
getCadView() | 获取内部 MainView 实例 |
addEntities(entities) | 添加实体数组 |
getCadBounds() | 获取 CAD 坐标范围 |
zoomToExtents() | 缩放到全图 |
setVisible(visible) | 设置可见性 |
clearSelection() | 清除选择 |
| 属性 | 类型 | 说明 |
|---|
enableSelection | boolean | 是否启用选择 |
const {
CadMapOverlay,
LineEnt, CircleEnt, TextEnt,
CadEventManager, CadEvents,
message
} = vjcad;
// === 1. 初始化 vjmap ===
const vjmap = await loadVjmap();
const svc = new vjmap.Service(env.serviceUrl, env.accessToken);
const res = await svc.openMap({
mapid: 'sys_zp',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle()
});
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
const map = new vjmap.Map({
container: 'map',
style: svc.vectorStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 1
});
map.attach(svc, prj);
await map.onLoad();
// === 2. 创建 CAD 叠加层 ===
const cadOverlay = new CadMapOverlay({
bounds: mapExtent,
serviceUrl: env.serviceUrl,
accessToken: env.accessToken,
themeMode: 0,
enableSelection: true,
onSelectionChanged: (selection) => {
message.info(`选中 ${selection.length} 个实体`);
}
});
await cadOverlay.addTo(map);
// === 3. 添加实体 ===
const bounds = cadOverlay.getCadBounds();
const [minX, minY, maxX, maxY] = bounds;
const avgSize = Math.min(maxX - minX, maxY - minY) / 20;
const entities = [];
// 随机创建 10 个实体
for (let i = 0; i < 5; i++) {
const x1 = minX + Math.random() * (maxX - minX);
const y1 = minY + Math.random() * (maxY - minY);
const x2 = minX + Math.random() * (maxX - minX);
const y2 = minY + Math.random() * (maxY - minY);
const line = new LineEnt([x1, y1], [x2, y2]);
line.setDefaults();
line.color = Math.floor(Math.random() * 7) + 1;
entities.push(line);
}
for (let i = 0; i < 5; i++) {
const cx = minX + Math.random() * (maxX - minX);
const cy = minY + Math.random() * (maxY - minY);
const circle = new CircleEnt([cx, cy], avgSize * (0.5 + Math.random()));
circle.setDefaults();
circle.color = Math.floor(Math.random() * 7) + 1;
entities.push(circle);
}
cadOverlay.addEntities(entities);
cadOverlay.zoomToExtents();
// === 4. 交互控制 ===
// 点击实体可选中
// ESC 取消选择
// 拖动/缩放/旋转地图,CAD 视图同步
- GIS + CAD 融合:在地图上叠加 CAD 图形
- 设备标注:在地理位置上标注设备信息
- 管线叠加:将 CAD 管线图叠加到地图上
- 规划展示:在地图上展示规划设计图