唯杰地图 logo唯杰地图
VJMAP
EN

CadMapOverlay — 把 vjcad 叠到 vjmap 地图

vjcad/references/map-integration/cad-map-overlay.md 7.9 KB200 行
原文

文档:vjcad/doc/tutorial/map-overlay.md、vjcad/doc/ui/map-overlay.md 示例:vjcad/example/basic/05mapoverlay.js

UI 角度的 CadMapOverlay API 已在 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;不建议同一页面叠多个 CadMapOverlay
  • enableSelection: true 时如果你也开了 vjmap 的选择 / 弹层,两套交互会同时响应同一次点击,通常二选一

参考(官方文档 MCP)

  • 文档:vjcad/doc/tutorial/map-overlay.md、vjcad/doc/ui/map-overlay.md
  • 示例:vjcad/example/basic/05mapoverlay.js