CadMapOverlay — 把 vjcad 叠到 vjmap 地图
文档:
vjcad/doc/tutorial/map-overlay.md、vjcad/doc/ui/map-overlay.md示例:vjcad/example/basic/05mapoverlay.js
UI 角度的
CadMapOverlayAPI 已在ui/map-overlay.md列出;本页聚焦 实战流程:vjmap 加载 → 地图初始化 → overlay attach → 添加实体 → 选择联动 → 控制面板。
1. 完整初始化流程(CDN 加载 vjmap)
const { CadMapOverlay, LineEnt, CircleEnt, TextEnt, message } = vjcad;
// 1) 动态加载 vjmap CSS + JS
const loadVjmap = () => new Promise((res, rej) => {
if (window.vjmap) return res(window.vjmap);
const css = document.createElement('link');
css.rel = 'stylesheet';
css.href = 'https://vjmap.com/demo/js/vjmap/vjmap.min.css';
document.head.appendChild(css);
const s = document.createElement('script');
s.src = 'https://vjmap.com/demo/js/vjmap/vjmap.min.js';
s.onload = () => window.vjmap ? res(window.vjmap) : rej(new Error('vjmap 未挂载'));
s.onerror = () => rej(new Error('vjmap 加载失败'));
document.head.appendChild(s);
});
const vjmap = await loadVjmap();
// 2) 创建 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()
});
if (res.error) throw new Error(res.error);
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
// 3) 创建 vjmap 地图
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();
map.addControl(new vjmap.NavigationControl(), 'top-right');
// 4) 创建 CadMapOverlay 并挂到 map 上
const cadOverlay = new CadMapOverlay({
bounds: mapExtent,
serviceUrl: env.serviceUrl,
accessToken: env.accessToken,
themeMode: 0, // 0 深色 / 1 浅色
enableSelection: true,
smoothAnimation: true, // CSS transform 平滑过渡
onSelectionChanged: sel => {
if (!sel.length) return message.info('选择已清空');
message.info(`选中 ${sel.length} 个实体`);
}
});
await cadOverlay.addTo(map);
至此 CAD 叠到地图上。cadOverlay 可作为 MainView 替身使用。
2. 在 CAD 范围内添加实体
const [minX, minY, maxX, maxY] = cadOverlay.getCadBounds();
const avgSize = Math.min(maxX - minX, maxY - minY) / 20;
const entities = [];
for (let i = 0; i < 5; i++) {
const line = new LineEnt(
[minX + Math.random() * (maxX - minX), minY + Math.random() * (maxY - minY)],
[minX + Math.random() * (maxX - minX), minY + Math.random() * (maxY - minY)]
);
line.setDefaults();
line.color = (i % 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 c = new CircleEnt([cx, cy], avgSize * (0.5 + Math.random()));
c.setDefaults();
c.color = (i % 7) + 1;
entities.push(c);
}
cadOverlay.addEntities(entities);
cadOverlay.zoomToExtents();
getCadBounds() 返回的是 CAD 内部坐标空间 的范围;不是经纬度。
3. 选择联动 + 控制面板
const ctrl = document.createElement('div');
ctrl.style.cssText = 'position:absolute;top:10px;left:10px;background:#fff;padding:10px;z-index:100;';
ctrl.innerHTML = `
<label><input type="checkbox" id="enableSel" checked> 允许选择</label>
<button id="btnZE">全图</button>
<button id="btnAdd">加实体</button>
<button id="btnClear">清选择</button>
<button id="btnToggle">显隐</button>
`;
document.getElementById('map').appendChild(ctrl);
let visible = true;
document.getElementById('enableSel').onchange = e =>
cadOverlay.enableSelection = e.target.checked;
document.getElementById('btnZE').onclick = () => cadOverlay.zoomToExtents();
document.getElementById('btnAdd').onclick = () => addRandomEntities();
document.getElementById('btnClear').onclick = () => cadOverlay.clearSelection();
document.getElementById('btnToggle').onclick = () => {
visible = !visible;
cadOverlay.setVisible(visible);
};
4. 拿到内部 MainView / Engine
const innerCadView = cadOverlay.getCadView(); // MainView 实例(无 .Engine 成员)
// Engine 是 vjcad 顶层单例:单视图时直接用全局 Engine;
// 页面有多个 CAD 视图时先 Engine.bindViewCache(innerCadView) 再取成员
Engine.zoomExtents();
// ⚠️ 叠加模式下 CAD 画布 pointer-events: none(disablePointerEvents),CadEvents.PointerDown 等指针事件不会触发;
// 点击坐标从地图事件取:
map.on('click', e => {
const p = cadOverlay.lngLatToCad(e.lngLat); // → {x, y} CAD 坐标
console.log('CAD 点:', p.x, p.y);
});
cadOverlay.addEntities 等于 Engine.addEntities。需要更细的命令系统时直接走 Engine.editor.executerWithOp(...)。
5. smoothAnimation 取舍
| 值 | 行为 | 适合 |
|---|---|---|
true(默认) |
CSS transform 平滑过渡,动画结束才重绘 | 美观,对性能宽容 |
false |
实时重绘 | 响应快但可能抖动 |
移动端 / WebGL 资源紧张推荐 true;高刷重绘场景 false。
6. 配置项速查
interface CadMapOverlayOptions { // d.ts ,仅这些字段
bounds: CadBoundsLike; // 必需,CAD 坐标范围([minX,minY,maxX,maxY] / GeoBounds / {min,max})
serviceUrl: string;
accessToken: string;
themeMode?: number; // 0 深色 / 1 浅色
themeColors?: ThemeColorsPartial;
minZoom?: number; // 默认 0
maxZoom?: number; // 默认 100
enableSelection?: boolean; // 默认 true
smoothAnimation?: boolean; // 默认 true
onSelectionChanged?: (sel: EntityBase[]) => void; // 实参是数组本身
}
// 无 workspace / transparent / backgroundColor / animationMode / fonts 等键
7. 常用方法
| 方法 | 说明 |
|---|---|
addTo(map) |
添加到 vjmap 地图 |
getCadView() |
内部 MainView |
getCadBounds() |
CAD 内部坐标范围 |
addEntities(entities) |
添加实体 |
zoomToExtents() |
全图 |
setVisible(boolean) |
显示 / 隐藏 |
clearSelection() |
清空选择 |
enableSelection (属性) |
开 / 关选择 |
红旗
- vjmap 必须 先
await map.onLoad()再cadOverlay.addTo(map),否则 overlay 找不到 canvas - 对齐由
map.attach(svc, prj)的投影决定(cadToLngLat就是map.toLngLat);bounds只用于缩放比取样和zoomToExtents(),填错不会拉伸实体,但会让zoomToExtents()飞错范围。建议直接用vjmap.GeoBounds.fromString(res.bounds) - 地图缩到太小(zoom < 1)时
smoothAnimation: true仍会让 CAD 实体出现"先平移再重绘"的延迟 cadOverlay.setVisible(false)只是隐藏;cadOverlay.remove()只摘 DOM、解绑地图事件,不释放内部 MainView——彻底卸载还要await cadOverlay.getCadView()?.destroy()Engine是全局单例、同一时刻只服务一个视图(Engine.currentDoc/currentSpace共用),每个 overlay 的视图同步都改写同一个Engine.currentSpace;不建议同一页面叠多个 CadMapOverlayenableSelection: true时如果你也开了 vjmap 的选择 / 弹层,两套交互会同时响应同一次点击,通常二选一
参考(官方文档 MCP)
- 文档:
vjcad/doc/tutorial/map-overlay.md、vjcad/doc/ui/map-overlay.md - 示例:
vjcad/example/basic/05mapoverlay.js