Skip to content

OpenLayers 适配器(@geoverse/editor-ol)

@geoverse/editor-ol 把 OpenLayers 的原生交互(Draw / Modify / Translate / Pointer)归一成 editor-core 的命令。它接收一个 ol/MapgeoverseGMap 继承自 ol/Map,可直接传入)。

安装

shell
pnpm add @geoverse/editor-core @geoverse/editor-ol geoverse ol

上手

ts
import { GMap } from '@geoverse/core-ol';
import { EditSession } from '@geoverse/editor-core';
import { OlEditorAdapter } from '@geoverse/editor-ol';

const map = new GMap({
  target: 'map',
  base: 'gd-vec',
  center: [118.18, 24.49],
  zoom: 15,
});
const crs = map.getView().getProjection().getCode();

const session = new EditSession({ crs, features: seed }); // seed 坐标须为工作 CRS(投影坐标)
const adapter = new OlEditorAdapter(map, {
  crs,
  // select 模式单击要素时回调;shift 多选。由你同步到 session 并高亮:
  onSelect: (id, additive) => {
    if (id) session.select([id], additive);
    else if (!additive) session.clearSelection();
    adapter.setSelection(session.getSelected());
  },
});

session.attach(adapter);
adapter.setMode('select');

工作 CRS

适配器几何按工作 CRS 原样进出(dataProjection === featureProjection)。OL 轨通常用视图投影(默认 EPSG:3857,单位米),变换/偏移的距离即米。seed 要素坐标也须是该投影坐标——可用 ol/projfromLonLat 从经纬度转换。

工具模式(adapter.setMode(mode)

模式操作归一命令
select单击要素选中,Shift 多选(经 onSelect 回调)
move在要素上拖拽整体平移TranslateCommand
transform选中后用包围盒手柄:拖角点缩放、拖顶部圆点旋转ScaleCommand / RotateCommand
modify拖动顶点句柄微调形状SetGeometryCommand
add-vertex在线/面边上单击插入节点AddVertexCommand
delete-vertex单击顶点删除(守最小顶点数)DeleteVertexCommand
split在线要素上单击一点打断SplitLineCommand
split-polygon在选中的面上画切割线(Draw 折线)SplitPolygonByLineCommand
merge依次单击两条相接的线合并MergeLinesCommand
draw-point / draw-line / draw-polygon绘制要素(线/面双击结束)AddFeatureCommand

transform 模式的手柄(gizmo)由适配器自动按当前选择集包围盒绘制;旋转/缩放在松手时落一个可撤销命令。

面拆分(split-polygon)的切割线约束

切割线必须完整贯穿外边界——从面的一侧外边界进、另一侧外边界出,才能把面分成两块。切割线可以顺带穿过洞(含洞面:穿到的洞按块裁回,各块保留所属洞),但穿洞并非必需(不碰洞的贯穿线照样切);反之,切割线若终止在面内或洞内(没穿出对侧外边界)则不构成切分,命令返回 SPLIT_NO_EFFECT 并在 UI 提示——这是按设计的正确行为,不是缺陷。

顶点句柄显示

句柄按模式 + 选择集分三档(详见核心概念 · 顶点句柄显示):modify / add-vertex / delete-vertex 显示实心可编辑句柄;select / move / transform 显示更淡的「幽灵」句柄(仅展示结构、不可拖);绘制/拆分/合并不显示既有顶点。有选择时只显示选中要素的句柄。整体平移与变换拖拽期间句柄临时隐藏,顶点拖拽则由 ol/Modify 自绘跟随句柄。

非模式操作(直接派发命令)

镜像、偏移、点/面合并、多点拆分、复制粘贴不依赖模式,作用于当前选择集,由应用层直接派发:

ts
import {
  MirrorCommand,
  OffsetCommand,
  MergePolygonsCommand,
} from '@geoverse/editor-ol';

const sel = session.getSelected();
session.engine.dispatch(new MirrorCommand(sel, 'horizontal'));
session.engine.dispatch(new OffsetCommand(sel[0], 40)); // 平行偏移 40m
session.engine.dispatch(new MergePolygonsCommand(sel)); // 面并集(JSTS)
adapter.setIssues(/* 上一步 EditResult.issues */ []); // 把校验问题喂回适配器以标红
session.copy(); // 复制选中
session.paste([60, 60]); // 粘贴(偏移 60m)

面/多面运算(合并不相邻面会得到 MultiPolygon,可再拆分;差集与挖洞为两个面的二元运算,第一个选中为目标 A):

ts
import {
  MergePolygonsCommand,
  SplitMultiPolygonCommand,
  DifferencePolygonsCommand,
  PunchHoleCommand,
  FillHoleCommand,
} from '@geoverse/editor-ol';

session.engine.dispatch(new MergePolygonsCommand([a, b])); // 不相邻 → MultiPolygon
session.engine.dispatch(new SplitMultiPolygonCommand(mpId)); // MultiPolygon → 多个 Polygon
session.engine.dispatch(new DifferencePolygonsCommand([a, b])); // 布尔 A−B(B 在内则成洞、跨界则切缺口)
session.engine.dispatch(new PunchHoleCommand([a, b])); // 结构挖洞:B 作为 A 的内环(B 须在 A 内部,A 须为单面)
session.engine.dispatch(new FillHoleCommand(a)); // 填洞:移除面的所有内环(PunchHole 的逆操作)

差集 vs 挖洞:差集是布尔运算(通用,B 跨边界会切缺口);挖洞是结构插入内环(非布尔,精确保留 A 外边界,要求 B 完全在 A 内部)。挖洞 ↔ 填洞互为逆操作。

面开洞 / 消洞(线驱动的「非分离切」,1→1;命令吃「面 id + 线」,通常经适配器 open-hole / close-hole 模式画线触发):

ts
import { OpenHoleCommand, CloseHoleCommand } from '@geoverse/editor-ol';

// 开洞(线驱动):切割线从外边界进入某个内环(洞)、不贯穿到对侧 → 洞经细通道连到外边界成凹湾
session.engine.dispatch(new OpenHoleCommand(id, cutLine)); // cutLine: GeoJSON LineString(工作 CRS = 视图投影,单位米)
// 消洞(一键,无需交互):把面外边界的所有凹湾自动封回内环洞(开洞的逆向能力,与面填洞对称)
session.engine.dispatch(new CloseHoleCommand(id));
// 通道宽度缺省按面 bbox 对角线 0.5% 自适应,可覆写:
session.engine.dispatch(new OpenHoleCommand(id, cutLine, { width: 20 })); // 20m 宽通道

// 适配器交互:选中含洞面 → open-hole 模式画切割线(双击结束);消洞是一次性命令、直接 dispatch 即可
adapter.setMode('open-hole');

开洞 vs 按线拆面:拆面须贯穿外环、把面切成 2 块;开洞的切割线从外边界进洞、不穿到对侧,面仍 1 块、洞环并入外环成凹湾。若切割线贯穿到对侧(切成多块),OpenHoleCommandOPEN_HOLE_SPLIT,提示改用「按线拆面」。

消洞是一次性命令(无需交互,类似面填洞):CloseHoleCommand(id) 用「凸包 − 外环」自动求出面上的所有凹湾并逐个封成内环洞——不必点选位置。面的外边界为凸、无凹湾时报 CLOSE_HOLE_NO_BAY(普通带洞面若只想去掉洞,请用 FillHoleCommand)。开洞 ↔ 消洞互为逆操作。

线/多线运算MergeLinesCommand 要求端点相接;不相接的多条线用 CombineLinesCommand 归并为 MultiLineString,可再拆分):

ts
import {
  MergeLinesCommand,
  CombineLinesCommand,
  SplitMultiLineStringCommand,
} from '@geoverse/editor-ol';

session.engine.dispatch(new MergeLinesCommand(a, b)); // 端点相接 → 单条 LineString
session.engine.dispatch(new CombineLinesCommand([a, b])); // 不要求相接 → MultiLineString
session.engine.dispatch(new SplitMultiLineStringCommand(mlsId)); // MultiLineString → 多条 LineString

常用命令已从 @geoverse/editor-ol 转出(TranslateCommandRotateCommandScaleCommandMirrorCommandOffsetCommandMergeLinesCommandCombineLinesCommandMergePointsCommandMergePolygonsCommandDifferencePolygonsCommandPunchHoleCommandFillHoleCommandSplitMultiPointCommandSplitMultiPolygonCommandSplitMultiLineStringCommandSplitPolygonByLineCommandAddFeaturesCommand),也可直接从 @geoverse/editor-core 引入。

拓扑标红

适配器按要素的 issues 给出红(error)/橙(warning)描边。引擎 dispatch 的返回值带 issues;把它喂回 adapter.setIssues(res.issues) 即可高亮(撤销/重做后传 [] 清除)。

吸附

拖动顶点(modify)或绘制(draw-*)时,落点会自动吸附到既有要素的顶点与边——底层用 ol/interaction/Snap 绑定编辑源。命中吸附点时该处显示橙色高亮环,光标也按模式给出可操作提示。默认开启,可在构造时关闭或调整像素容差,也可运行时切换:

ts
const adapter = new OlEditorAdapter(map, { snap: true, snapTolerance: 10 });
adapter.setSnap(false); // 运行时关闭

下一步

在线示例:仓库 examples/editor/pnpm dev:examples/editor/)。