唯杰地图 logo唯杰地图
VJMAP
EN

地图叠加(CadMapOverlay)

vjcad/references/ui/map-overlay.md 11.0 KB209 行
原文

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

提示:map-integration/ 完整子文档见同名目录。本页只聚焦 UI 集成层。

1. 用途

CadMapOverlay 是把 vjcad 视图作为透明图层 叠加到 vjmap 地图上的组件。⚠️ 只支持 vjmap.Map 且已 map.attach(svc, prj):坐标换算与视图同步全靠 map.toLngLat / map.fromLngLat,非 vjmap 的地图对象没有这两个方法(只会 console.warn 并把坐标原样返回)。常见场景:

  • 在卫星 / 矢量地图上叠加图纸
  • 联动选择:图上点选 = 地图弹层
  • 在地理坐标和 CAD 内部坐标间转换
  • 用 vjmap 的图层 / 标记 / 路径分析能力

2. 最小示例

import { CadMapOverlay, DrawingManagerService } from 'vjcad';
// 前提:vjmap 地图已按官方示例初始化——
//   const res = await svc.openMap({ mapid, mapopenway: vjmap.MapOpenWay.GeomRender, style: vjmap.openMapDarkStyle() });
//   const mapExtent = vjmap.GeoBounds.fromString(res.bounds);   // CAD 坐标范围
//   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();

const cadOverlay = new CadMapOverlay({
    bounds: mapExtent,                    // CadBoundsLike:图纸的 CAD 坐标范围(不是经纬度)
    serviceUrl: 'http://127.0.0.1:27660/api/v1',
    accessToken: 'your-token',
    themeMode: 0,                         // 0 深色,1 浅色
    enableSelection: true,
    onSelectionChanged: entities => {     // 实参就是 EntityBase[],不是 {entities}
        console.log('选中:', entities.length, '个');
    }
});

await cadOverlay.addTo(map);              // 返回 Promise<this>;此时才创建并初始化内部 MainView

// 要显示服务端图纸:必须在 addTo 之后,用 overlay 自己的 MainView 打开(overlay 本身没有 loadMap)
const drawingManager = new DrawingManagerService();
const r = await drawingManager.openDrawing({ type: 'imports', mapid: 'demo.dwg', version: 'v1' });
if (r.success) {
    const f = new File([r.webcadJson], 'drawing.webcad', { type: 'application/json' });
    await cadOverlay.getCadView().openDbDoc(f, r.webcadData);   // 完整流程见 file/server.md
}
cadOverlay.zoomToExtents();               // = map.fitBounds(cadBounds, {padding:50, duration:1000})

addTo 内部创建一个 viewMode:'viewer' 的 MainView(backgroundAlpha:0、disablePointerEvents:true、showCrosshair:false),渲染的是 Engine 当前文档——图纸打开要单独走 DrawingManagerService.openDrawing + openDbDoc(见 file-operations),且必须在 addTo 之后(之前 overlay 的 MainView 还不存在,Engine.view 不是它),CadMapOverlay 没有 loadMap / closeMap。

地图 ↔ CAD 坐标换算直接调用 vjmap 的 map.toLngLat / map.fromLngLat(由 map.attach(svc, prj) 的投影决定),不是 overlay 按 bounds 自己做线性插值;bounds 是 CAD 坐标范围,用于 zoomToExtents/getCadBounds 等,应与 vjmap 打开该图得到的 res.bounds 一致。

3. CadMapOverlayOptions 关键字段

interface CadMapOverlayOptions {           // d.ts 字段,仅以下这些
    bounds: CadBoundsLike;                 // 图纸的 CAD 坐标范围(如 vjmap.GeoBounds.fromString(res.bounds))
    serviceUrl: string;                    // WebCAD 服务地址
    accessToken: string;
    themeMode?: number;                    // 0 深色(默认),1 浅色
    themeColors?: ThemeColorsPartial;      // UI chrome 主题色覆盖
    minZoom?: number;                      // 最小显示级别(默认 0)
    maxZoom?: number;                      // 最大显示级别(默认 100)
    enableSelection?: boolean;             // 允许点选实体(默认 true)
    onSelectionChanged?: (selection: EntityBase[]) => void;
    smoothAnimation?: boolean;             // true(默认)动画中 CSS transform,结束 redraw;false 实时 redraw
}

没有 workspace / backgroundColor / transparent / syncWithMap / animationMode / enableHover / showGrips / fonts / onLoad / onHover / onMapMove——这些字段写在 options 里会被静默忽略。视图跟随是硬编码行为(move/zoom/rotate 监听),不需要开关;minZoom/maxZoom 控制 overlay 在哪个缩放区间显示。

4. 关键方法

class CadMapOverlay {
    constructor(options: CadMapOverlayOptions);
    addTo(map: any, insertId?: string | HTMLElement): Promise<this>;  // insertId 可换容器
    remove(): void;                             // 卸载(移除 DOM + 解绑事件)
    setVisible(visible: boolean, isDisplay?: boolean): void;
    //   isDisplay=true → style.display 显隐;否则走 _add/_remove 真正挂载/卸载

    // 视图
    zoomToExtents(): void;                      // map.fitBounds(cadBounds, {padding:50})
    updateBounds(bounds: CadBoundsLike): void;  // 改 bounds 并重算
    getCadBounds(): number[];                   // [x1,y1,x2,y2]
    redraw(): void;
    getCadView(): MainView | null;              // 拿到底层 MainView 后可调 setTheme 等

    // 实体 / 选择(都直接转发到 Engine)
    addEntities(entities: EntityBase | EntityBase[]): void;  // = Engine.addEntities
    getSelection(): EntityBase[];               // = Engine.editor.PreSS
    setSelection(entities: EntityBase[]): void; // 注意:吃实体对象数组,不是 handle
    clearSelection(): void;

    // 坐标转换(返回对象,不是数组)
    cadToLngLat(x: number, y: number): { lng: number; lat: number };
    lngLatToCad(lng: number, lat: number): { x: number; y: number };
    lngLatToCad(lngLat: { lng: number; lat: number }): { x: number; y: number };

    // 公共字段:minZoom / maxZoom / enableSelection / smoothAnimation 可直接读写
}

不存在:isVisible / loadMap / closeMap / zoomToBounds / flyTo / removeEntities / setAlpha / setThemeMode。删除实体走 Engine.eraseEntities;改主题经 getCadView().setTheme(...) / setThemeColors(...)。

5. 选择联动

cadOverlay = new CadMapOverlay({
    enableSelection: true,
    onSelectionChanged: entities => {          // 实参是 EntityBase[]
        if (entities.length === 0) return popup?.remove();
        const e = entities[0];
        // EntityBase 没有通用 center 属性;取包围盒中心
        const box = Engine.getBoundsByEntities([e]);
        const { lng, lat } = cadOverlay.cadToLngLat(box.center.x, box.center.y);
        popup = new vjmap.Popup({ closeOnClick: false })
            .setLngLat([lng, lat])
            .setHTML(`<b>${e.entityType}</b><br/>图层: ${e.layer}`)   // entityType 是 getter,不要加 ()
            .addTo(map);
    }
});

6. 坐标转换实战

import { Point2D, BoundingBox } from 'vjcad';

// 地图点击 → 找到 CAD 实体
map.on('click', e => {
    const { x, y } = cadOverlay.lngLatToCad(e.lngLat.lng, e.lngLat.lat);
    // BoundingBox 构造收两个 Point2D 角点,不是 4 个数字
    const hits = Engine.spatialIndexManager.search(new BoundingBox(
        new Point2D(x - 1, y - 1), new Point2D(x + 1, y + 1)
    ));
    if (hits[0]) cadOverlay.setSelection([hits[0]]);   // 传实体对象,不是 id 字符串
});

// CAD 实体 → 在地图上画 marker
ents.forEach(e => {
    const box = Engine.getBoundsByEntities([e]);
    const { lng, lat } = cadOverlay.cadToLngLat(box.center.x, box.center.y);
    new vjmap.Marker().setLngLat([lng, lat]).addTo(map);
});

7. 实体添加 / 删除

const line = new LineEnt([0, 0], [100, 100]);
line.setDefaults();
line.color = 1;
cadOverlay.addEntities([line]);     // 等价 Engine.addEntities,自动 redraw

// 删除没有 overlay.removeEntities——直接走引擎
Engine.eraseEntities([line]);
cadOverlay.redraw();

addEntities 走的是 vjcad 引擎(内部就是 Engine.addEntities + redraw)。

8. 视图同步(smoothAnimation)

跟随是硬编码的(监听地图 move/zoom/rotate + zoomend),没有 syncWithMap/animationMode 开关;只有渲染策略可选:

smoothAnimation 说明
true(默认) 拖动/缩放中用 CSS transform 平滑跟手,moveend 后 redraw
false 每次 move/zoom 事件实时 redraw——响应即时但可能抖动

9. 多图叠加(不建议)

vjcad 按单视图设计:Engine 的文档集合、当前文档、选择集、命令、事件都是全局静态的,每个 overlay 的 addTo 都会新建一个 MainView 并让 Engine.view 指向最后初始化的那个。所以多个 CadMapOverlay 并不独立——它们共享文档 / 选择 / 撤销,addEntities、openDbDoc 等操作落在同一个 Engine 上。需要多张图同屏,用 vjmap 自己的图层(WMS / 瓦片)叠其余图纸,只保留一个 CadMapOverlay 做可编辑层。

// overlay 视图固定 backgroundAlpha:0(构造时写死),无需自己传 transparent
// 想调透明度,对 getCadView() 的 DOM 操作,没有 setAlpha
cadOverlay.getCadView()?.style.setProperty('opacity', '0.6');

10. 主题切换

// 构造时用 themeMode / themeColors;运行期走底层 MainView
cadOverlay.getCadView()?.setThemeColors({ /* ThemeColorsPartial */ });
cadOverlay.getCadView()?.resetThemeColors();
// 没有 overlay.setThemeMode——themeMode 只在构造时生效

地图换主题时同步换 CAD 主题,避免黑底白线 + 白底地图的对比错乱。

红旗

  • bounds 错了(与 mapsize 不一致)→ CAD 内容会被拉伸 / 偏移;建议用 vjmap 的工具计算图纸地理范围
  • overlay 透明来自内部 backgroundAlpha:0(写死),无 transparent/backgroundColor 选项
  • 多个 CadMapOverlay 并不独立:map._webcadOverlays 只是登记表,Engine 是单视图全局状态(见第 9 节),后 addTo 的接管 Engine.view;remove() 只把 MainView 从 DOM 摘下并解绑地图事件,不销毁 MainView——要彻底释放先 await cadOverlay.getCadView()?.destroy() 再 remove()
  • cadToLngLat / lngLatToCad 只是转调 vjmap 的 map.toLngLat / fromLngLat(投影由 map.attach(svc, prj) 决定);地图不是 attach 过的 vjmap.Map 时会退化为原样返回坐标
  • setSelection / getSelection 与 Engine.editor 的选择集是同一份(PreSS/ssSetFirst)——不要和 vjmap 侧自己的选择逻辑混用
  • addTo(map) 必须在 vjmap 地图完全 load 之后调用;返回 this 可链式
  • 地图 zoom 超出 [minZoom, maxZoom) 区间时 overlay 自动卸载/重挂(_checkVisible)

参考(官方文档 MCP)

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