gis-view-webgl2 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +255 -3
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +65 -0
- package/docs/GisView.md +1 -1
- package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +58 -0
- package/docs/demos/ChannelDemo.vue +566 -6
- package/docs/demos/CopyMergeDemo.vue +57 -6
- package/docs/demos/LabelDemo.vue +67 -11
- package/docs/demos/api-index.js +13 -3
- package/docs/format/03-/346/213/220/347/202/271/350/243/205/351/245/260/344/270/216/346/240/267/345/274/217.md +4 -2
- package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +5 -4
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +7 -2
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +21 -5
- package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +100 -0
- package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +38 -0
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +5 -5
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +58 -8
- package/docs/manual/10-/351/200/232/351/201/223/347/274/226/350/276/221/344/270/216/346/226/260/345/242/236.md +27 -2
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +45 -1
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +7 -1
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +8 -0
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +28 -0
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +3 -0
- package/docs/manual/21-/350/257/264/346/230/216/344/270/216/345/270/270/350/247/201/351/227/256/351/242/230.md +1 -1
- package/docs/steps/README.md +8 -6
- package/docs/steps/selftest-demos.html +194 -29
- package/docs/steps/selftest-docs.html +7 -6
- package/docs/steps/selftest-features.html +213 -1
- package/docs/steps/selftest-labels.html +249 -4
- package/docs//346/216/245/345/217/243/345/215/217/350/256/256.md +17 -11
- package/package.json +2 -2
|
@@ -213,6 +213,106 @@ if (moved.length) {
|
|
|
213
213
|
- `clearMovedPoints()` 不传引用 = 全部对账;传一个或一批只清这些(写法与 `removePoints` 一致:id / 下标 / `{ id }` / `{ index }`,认不出来的跳过并记一条提醒);空数组是「没有目标」,什么都不做;返回实例本身,可以链式调。
|
|
214
214
|
- 这本账是**只读差异**,不落历史、不发事件:`getMovedPoints()` 本身不改数据,读到的记录会一直留着,直到 `clearMovedPoints` 或整份换数据。
|
|
215
215
|
|
|
216
|
+
## 要素显隐与变化对账
|
|
217
|
+
|
|
218
|
+
### 要素隐藏与恢复(setFeatureVisible / hideFeatures / showFeatures)
|
|
219
|
+
|
|
220
|
+
临时收起一个节点、一条线路或整条通道时,**不要删数据再重建**:`visible` 本来就是一个数据字段,组件提供三个入口把「翻字段 + 收集结果 + 合成一步历史」做成一件事:
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
view.hideFeatures('channel', 'c1') // 整条通道与成员回线一起退场
|
|
224
|
+
view.showFeatures('channel', 'c1') // 数据 / 分组 / 拐点原样回来
|
|
225
|
+
view.setFeatureVisible('point', ['p-1', 'p-2'], false)
|
|
226
|
+
view.setFeatureVisible('line', { index: 3 }, true)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
| `kind` | `visible: false` 时发生什么 | 恢复时 |
|
|
230
|
+
| --- | --- | --- |
|
|
231
|
+
| `'point'` | 节点本身不画;绑在它身上的线路不再跟随它(线路有 `path` / `start` / `end` 时按自己的几何照画),以它为端点的锚定通道整条收起 | 节点回来,绑定线与通道重新接上 |
|
|
232
|
+
| `'line'` | 只收起这一条线路;通道声明、分组、骨架与其它回线都不动(要连整条通道一起收用 `'channel'`) | 这一条回线按原声明回来 |
|
|
233
|
+
| `'channel'` | 整条通道与全部成员回线一起退场;数据、分组、拐点与编辑状态都保留 | 成员回线、分组与编辑过的拐点原样恢复 |
|
|
234
|
+
|
|
235
|
+
- `refs` 收单个或一批:id / 下标 / `{ id }` / `{ index }`,也认 `'selection'`;通道的 `'selection'` 会按选中回线反查它们所属的整条通道。重复引用会去重。
|
|
236
|
+
- 返回 `{ ok, reason, kind, visible, indexes, ids, items, skipped }`:`indexes` / `ids` / `items` 按实际改到的数据顺序给出;认不出的引用逐条进 `skipped`,不影响其余条目。类型 / `visible` 写错或一个目标都没找到时 `ok: false`,其余情况是部分成功也返回 `true`。
|
|
237
|
+
- 整次调用收在一个 `batch` 里:只记一条历史、只发一条 `batch` 数据变更。`hideFeatures` / `showFeatures` 只是 `setFeatureVisible(..., false / true)` 的快捷写法。
|
|
238
|
+
- 也可以继续用 `updatePoint` / `updateLine` / `updateChannel` 写 `visible`;三个统一入口负责批量收集、一步历史与统一读数,适合做「隐藏选中 / 显示选中」这类按钮。
|
|
239
|
+
- **和删除的区别**:隐藏只改 `visible`,要素仍在数据里,`getPoint` / `getLine` / `getChannel` 仍查得到,`remove*` 才会真的删数据并清掉相关引用。
|
|
240
|
+
|
|
241
|
+
### 通道变化对账(getChangedChannels / clearChangedChannels)
|
|
242
|
+
|
|
243
|
+
和节点位置对账同一套回路:建视图(或整份换通道声明)时记一份基准,之后 `getChangedChannels()` 一趟扫出「相对上次对账变过什么」,发完接口再 `clearChangedChannels()` 把当前声明记成新基准:
|
|
244
|
+
|
|
245
|
+
```js
|
|
246
|
+
const changedChannels = view.getChangedChannels()
|
|
247
|
+
if (changedChannels.length) {
|
|
248
|
+
api.patch('/gis/channels/changes', { channels: changedChannels.map(one => ({ id: one.id, groups: one.groups, reasons: one.reasons })) })
|
|
249
|
+
.then(() => view.clearChangedChannels(changedChannels.map(one => one.id)))
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
每一项的读数是 `{ index, id, item, groups, groupsChanged, waypointsChanged, membersChanged, endpointsChanged, visibilityChanged, fieldsChanged, fields, reasons }`:
|
|
254
|
+
|
|
255
|
+
| 标记 / 原因 | 什么变了 |
|
|
256
|
+
| --- | --- |
|
|
257
|
+
| `membersChanged` / `members` | 通道的成员池变了(顶层 `lineIds` 与各分组里的回线一起算) |
|
|
258
|
+
| `groupsChanged` / `groups` | 分组结构变了:显式 `groups` 的开 / 关、分组数量、分组 id 或各组 `lineIds` 变了 |
|
|
259
|
+
| `waypointsChanged` / `waypoints` | 某个分组的骨架 `path` 变了:加 / 拖 / 删分组拐点都算 |
|
|
260
|
+
| `endpointsChanged` / `endpoints` | 解析后的通道两端 `from` / `to` 变了(包括从第一条回线继承出来的端点) |
|
|
261
|
+
| `visibilityChanged` / `visibility` | `visible` 在 `true` / `false` 之间翻过;缺省与 `true` 视为同一档 |
|
|
262
|
+
| `fieldsChanged` / `fields` | 除 `id` / `lineIds` / `groups` / `path` / `from` / `to` / `visible` 之外的声明字段变了,`fields` 列出字段名(名称、宽度、取宽系数、内收、`offsetMode`、提示等) |
|
|
263
|
+
|
|
264
|
+
`groups` 是当前生效分组的快照(没写 `groups` 的通道按「整组」折算,`id` 是通道 id,`lineIds` 是成员、`path` 是骨架)。几个口径:
|
|
265
|
+
|
|
266
|
+
- **新加通道只补账、不报变化**:基准里没有的通道读一次账本会登记进去,下一次再改才算;**删掉的通道从账本清掉**,不会留在返回里。
|
|
267
|
+
- **整份换通道声明 = 重新对账**:`updateData({ channels })`、`updateOptions` 里换 `channels`、`importData` 等整份替换之后,以新声明为基准,读出来是空表。
|
|
268
|
+
- **读本身不改基准**:`getChangedChannels()` 可以轮询 / 反复读,同一条变化会一直留着,直到 `clearChangedChannels` 或整份换通道声明。
|
|
269
|
+
- **空数组是「没有目标」**:`clearChangedChannels([])` 什么都不做;不传 / `null` 才是全部对账。传一个或一批引用只清这些,写法与 `removePoints` 一致(id / 下标 / `{ id }` / `{ index }`),认不出来的跳过并记一条提醒;返回实例本身可链式调用。
|
|
270
|
+
- **它看的是声明,不是实时几何**:拖动节点只改坐标、不改 `from` / `to` 的 id,所以不进通道账本;拖 / 加 / 删分组拐点改的是组 `path`,会进 `waypoints`。节点位置变化仍用 `getMovedPoints` / `clearMovedPoints`。
|
|
271
|
+
- **删除同步清账**:`removeChannel` 与 `removeLines` 连带删掉的通道会当场从账本摘掉,下一次 `getChangedChannels()` 不会再返回它们;没删的通道若因摘成员而变了,仍按 `members` / `groups` / `waypoints` 正常报。
|
|
272
|
+
|
|
273
|
+
#### 只看某一类变化:`getChangedChannels(options?)`
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
// 只关心拐点:分组结构的新增 / 删除 / 重排不入账,但已有分组的 path 真变了仍报 waypoints
|
|
277
|
+
const waypointChanges = view.getChangedChannels({ reasons: ['waypoints'], ignoreGroups: true })
|
|
278
|
+
|
|
279
|
+
// 只要分组增删,不要成员和字段变化
|
|
280
|
+
const groupChanges = view.getChangedChannels({ reasons: ['groups'] })
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
- `reasons` 是白名单,可写 `groups` / `members` / `waypoints` / `endpoints` / `visibility` / `fields` 的任意组合。返回项的 `reasons` 与各个 `*Changed` 标记会一起收窄,未选中的原因对应标记为 `false`、`fields` 清空。
|
|
284
|
+
- `reasons: []` 表示什么都不取,直接返回空数组,不扫数据;传非数组会遵守「提醒一次、忽略这次筛选」的约定,按全部原因查询。
|
|
285
|
+
- `ignoreGroups: true` 时不报 `groups`,也不把整组新增 / 删除 / 重排折算成 `waypoints`。只有基准与当前都存在的同一分组(按 `id` 配对)`path` 真变了,才报 `waypoints`;缺省 `path` 与空数组归一到同一档,不会因为分组声明刚落数据而误报。
|
|
286
|
+
- 这两个选项只影响这一次查询,不修改基准;之后照常 `clearChangedChannels()` 对账。
|
|
287
|
+
|
|
288
|
+
### 线路变化对账(getChangedLines / clearChangedLines)
|
|
289
|
+
|
|
290
|
+
只有节点和线路、没有通道的场景用这一对方法。口径与通道账本相同:建视图 / 整份换线路时记基准,`getChangedLines(options?)` 返回最近变化,发完接口再 `clearChangedLines(refs?)` 对账。
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
const changedLines = view.getChangedLines()
|
|
294
|
+
if (changedLines.length) {
|
|
295
|
+
api.patch('/gis/lines/changes', { lines: changedLines.map(one => ({ id: one.id, reasons: one.reasons })) })
|
|
296
|
+
.then(() => view.clearChangedLines(changedLines.map(one => one.id)))
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const geometryOnly = view.getChangedLines({ reasons: ['path'] })
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
每项返回 `{ index, id, item, path, start, end, from, to, visible, pathChanged, endpointsChanged, visibilityChanged, fieldsChanged, fields, reasons }`:
|
|
303
|
+
|
|
304
|
+
| 标记 / 原因 | 什么变了 |
|
|
305
|
+
| --- | --- |
|
|
306
|
+
| `pathChanged` / `path` | 线路自己的 `path` / `start` / `end` 变了;通道骨架变化不走这里 |
|
|
307
|
+
| `endpointsChanged` / `endpoints` | 线路的 `from` / `to` 绑定变了 |
|
|
308
|
+
| `visibilityChanged` / `visibility` | `visible` 在 `true` / `false` 之间翻过;缺省与 `true` 视为同一档 |
|
|
309
|
+
| `fieldsChanged` / `fields` | 除 `id` / `start` / `end` / `path` / `from` / `to` / `visible` 之外的声明字段变了,`fields` 列出字段名 |
|
|
310
|
+
|
|
311
|
+
- **新加线路只补账、不报变化**;`removeLine` / `removeLines` 删掉的线路同步清账,下一次查询不再返回。`removeChannel` 连带删除的成员回线也一起清账。
|
|
312
|
+
- **整份换线路 = 重新对账**:`updateData({ lines })`、`updateOptions` 里换 `lines`、`importData` 等整份替换之后以新声明为基准;读取本身不重置,`clearChangedLines()` 全部对账,传一个 / 一批引用只清这些,空数组无操作。
|
|
313
|
+
- `reasons` 白名单的规则与 `getChangedChannels` 相同:只写 `path` / `endpoints` / `visibility` / `fields`,空数组直接返回空表,非数组忽略并提醒一次。
|
|
314
|
+
- **点和线路各管一段**:拖动节点只改节点坐标,仍用 `getMovedPoints` / `clearMovedPoints`;线路数组里的 `path` / `start` / `end` / 端点绑定由 `getChangedLines` 负责。通道里的回线只比较自己的声明,分组骨架的增删与拐点变化仍由 `getChangedChannels` 负责。
|
|
315
|
+
|
|
216
316
|
## 按方法新增要素(节点 / 线路 / 通道 / 分组 / 拐点)
|
|
217
317
|
|
|
218
318
|
都能加,**全是内置方法** —— 不用自己拼好整份数据再 `updateData`。新增类的方法都往数组末尾追加(老要素的下标一个都不动),返回值说明这一次落地了什么,同时照常发一条 `dataChange`(`options.emit: false` 只落地不通知),开着历史时记一步(可撤销)。
|
|
@@ -593,6 +593,44 @@ view.resolvePointMerge(false) // 取消:拖动的节点回原位,数据不
|
|
|
593
593
|
|
|
594
594
|
删节点用 `view.removePoints('a')` / `view.removePoints({ index: 2 })` / 数组:绑在它上面的线路一并删掉,声明了它当端点的整条通道(含成员回线)跟着删。
|
|
595
595
|
|
|
596
|
+
## 节点拆分(splitPoint)
|
|
597
|
+
|
|
598
|
+
`splitPoint` 是 `mergePoints` 的逆操作:把一个节点按业务需要拆成两份或更多,原来连在它身上的线路与通道两端按分配重新挂到新节点上。它不弹对话框,直接按规则落地;整次调用只记一条历史、只发一条 `splitPoint` 数据变更。
|
|
599
|
+
|
|
600
|
+
```js
|
|
601
|
+
const split = view.splitPoint('hub', [
|
|
602
|
+
{ name: '枢纽(主)' }, // 第一份:沿用原下标与 id
|
|
603
|
+
{ x: 116.42, y: 39.91, name: '枢纽(东)', lines: ['line-east'] },
|
|
604
|
+
{ x: 116.38, y: 39.89, name: '枢纽(西)', lines: ['line-west'], channelTo: ['corridor-1'] }
|
|
605
|
+
], { label: '拆分枢纽' })
|
|
606
|
+
|
|
607
|
+
if (split.ok) {
|
|
608
|
+
console.log(split.pointIds, split.linesChanged, split.channelsChanged)
|
|
609
|
+
api.post('/gis/points/split', split.changes) // 拆分后的节点、受影响的线路与通道端点,以及删除项
|
|
610
|
+
}
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
每份节点定义可以直接写节点字段(`id` / `x` / `y` / `name` / 样式 / 业务字段),也可以放进 `item`;另外三项是从节点字段里摘出来的分配指令,不会写进节点数据:
|
|
614
|
+
|
|
615
|
+
| 字段 | 作用 |
|
|
616
|
+
| --- | --- |
|
|
617
|
+
| `lines` | 分到这一份的线路(id / 下标 / `{ id }` / `{ index }`,单个或一批)。只能分配真的连在来源节点上的线路(`from` / `to` 等于来源 id,或 `start` / `end` / `path` 顶点与来源坐标重合) |
|
|
618
|
+
| `channels` | 两端都分到这一份的受影响通道;把同一条通道写进两份时,按出现顺序当作 `from` 端、`to` 端各去一份 |
|
|
619
|
+
| `channelFrom` / `channelTo` | 只把通道的起点 / 终点分到这一份;同一条通道的同一端不能分配给两份 |
|
|
620
|
+
|
|
621
|
+
落地规则:
|
|
622
|
+
|
|
623
|
+
- **第一份接在原下标上**,缺省沿用来源节点的 id;其余几份追加到 `points` 末尾。`options.keepSourceId: false` 时第一份也生成 `${来源 id}-split-1`。没写 id 的其余份依次生成 `${来源 id}-split-2` / `-3`…,撞上现有 id 时自动加序号;显式写了重复 id 会整次失败。
|
|
624
|
+
- **节点字段从来源继承,再按这一份的字段覆盖**;坐标缺省继承来源。至少给两份,坐标必须是有效数字,否则 `ok: false` 且 `reason` 说明原因。
|
|
625
|
+
- **线路默认跟第一份**:没写进任何一份 `lines`、但连在来源节点上的线路,全部改挂到第一份;同一条线路显式分给两份会整次失败。认不出 / 没连在来源上的引用进 `skipped.lines`,不影响其余条目。
|
|
626
|
+
- **通道端点默认跟第一条被分配过的成员回线**:例如通道里唯一一条回线分给了第二份,通道的 `from` 会自动改到第二份节点,而不是留在第一份;成员都没显式分配时才回落到第一份。显式写了 `channels` / `channelFrom` / `channelTo` 的按显式分配来。
|
|
627
|
+
- **几何一起改**:线路的 `from` / `to` 改到对应新节点;没有 id 绑定、但 `start` / `end` / `path` 顶点与来源坐标重合的端点 / 顶点也改到新坐标。通道端点按新声明重新锚定,成员回线由通道骨架推导,不用逐条改。
|
|
628
|
+
- **拆完清理退化数据**:两端落到同一份、变自环或零长度的线路删掉;两端因此相同的通道删掉;成员回线全被删空的通道也删掉。删除项分别进 `removedLineIds` / `removedLineIndexes` 与 `removedChannelIds`。
|
|
629
|
+
- **返回的 `channelsChanged` 只数端点被改写的通道**;被删掉的通道单列在 `removedChannels` / `removedChannelIds`。`changes` 给 `{ removedPoint, points, lines, removedLines, channels, removedChannels }`:`points` 是拆分后的全部节点,`lines` / `channels` 是仍存在且端点被改写的线路 / 通道,删除项单列;可直接作为后端事务输入。`skipped` 说明哪些线路 / 通道引用没认出来。
|
|
630
|
+
- **撤销**:不用逐条回滚,`view.undo()` 一步回到拆分前;重做一步恢复拆分结果。
|
|
631
|
+
|
|
632
|
+
`CopyMergeDemo.vue` 的「拆分节点」一节可以直接点按钮看效果:选中一个节点,按两份拆开,读数会列出新节点 id、改绑的线路与通道端点,以及这一步的变更集。
|
|
633
|
+
|
|
596
634
|
## 删除(remove)
|
|
597
635
|
|
|
598
636
|
删除走的是「**先看清楚要删什么,确认之后才动数据**」这条路:Delete 键(`shortcuts.remove`,默认 `['delete', 'backspace']`)与 `requestRemove()` 都先发一份载荷(`onRemove` 回调 / `'remove'` 事件,`phase: 'pending'`),载荷里把这次要删的拐点 / 线路 / 节点、各自的连带影响与可选的删除范围列全;使用方拿着它弹自己的确认框,确认才删、取消数据一个字节都不动。
|
|
@@ -126,12 +126,12 @@ view.selectFeatures('station-7', { mode: 'remove' }) // 与 deselect('sta
|
|
|
126
126
|
|
|
127
127
|
- `deselect(refs)` 的写法与 `selectFeatures` **完全一样**(节点 / 通道 / 线路 / 混合一批,拾取载荷与搜索命中也能直接喂进来),只做「去掉这几条」:其余选中原样留着,认不出来的条目跳过(与 `patchPoints` 的 `skipped` 一个态度)。不传(或传 `null`)等于取消全部(`selectFeatures(null)` 也是这个意思);传空数组是「没有目标」,什么都不做。返回实例本身,可以接着链式调。
|
|
128
128
|
- 「加 / 减 / 取反」三种并入选中集的方式还是 `options.mode`:`selectFeatures(refs, { mode: 'remove' })` 与 `deselect(refs)` 等价,`'add'` / `'toggle'` 见上一节。
|
|
129
|
-
- **画布上的手动取消**:单击空白处与 Esc 都是清空全部(Esc 先逐层退出复制预览 → 待确认合并 → 通道草稿,最后一层才是清空);**Ctrl / ⌘ + 单击**已经选中的要素是取反取消(节点一直是单个取反;回线在 `channelSelectMode` 为 `'group'` / `'channel'` 时整组 / 整条通道一起取反,`'line'` 模式下 Ctrl + 单击留给加点与换选中);通道右键交互框里的「取消该分组选中」就是 `deselect({ channelId, groupId })`。要「取消全部」时这三种写法等价:`selectFeatures(null)` / `selectFeatures([])` / `deselect()
|
|
129
|
+
- **画布上的手动取消**:单击空白处与 Esc 都是清空全部(Esc 先逐层退出复制预览 → 待确认合并 → 通道草稿,最后一层才是清空);**Ctrl / ⌘ + 单击**已经选中的要素是取反取消(节点一直是单个取反;回线在 `channelSelectMode` 为 `'group'` / `'channel'` 时整组 / 整条通道一起取反,`'line'` 模式下 Ctrl + 单击留给加点与换选中);通道右键交互框里的「取消该分组选中」就是 `deselect({ channelId, groupId })`。要「取消全部」时这三种写法等价:`selectFeatures(null)` / `selectFeatures([])` / `deselect()`。按住加点快捷键点击通道回线时,新增拐点后的选中与高亮也按 `channelSelectMode` 摊成一条 / 一组 / 整条通道。
|
|
130
130
|
- 想先确认再取消:`isSelected(ref)` 问单个、`getSelections()` 取原始条目、`getSelectionData()` 一次取全(`count` / `ids` / `bounds`)。
|
|
131
131
|
|
|
132
132
|
## 线路拐点
|
|
133
133
|
|
|
134
|
-
写 `waypointEditable: true`(缺省关)后,线路的中间顶点会画出可交互的拐点:按住 Ctrl 点击线路在点击位置插入拐点,单击选中,拖动即移动顶点、线路随之变成折线,双击或选中后按 Delete 删除。节点很密时 Ctrl 点击会先比较点到节点与点到线路的屏幕距离:**线路更近就按加点处理**,比节点远才走 Ctrl
|
|
134
|
+
写 `waypointEditable: true`(缺省关)后,线路的中间顶点会画出可交互的拐点:按住 Ctrl 点击线路在点击位置插入拐点,单击选中,拖动即移动顶点、线路随之变成折线,双击或选中后按 Delete 删除。节点很密时 Ctrl 点击会先比较点到节点与点到线路的屏幕距离:**线路更近就按加点处理**,比节点远才走 Ctrl 多选节点,所以贴着节点云也能给线路补拐点。通道回线加点后的高亮范围与普通点选共用 `channelSelectMode`:`'line'` 只高亮点中的一条、`'group'` 连这一组、`'channel'` 整条通道。
|
|
135
135
|
|
|
136
136
|
需要完全停掉拐点编辑时写 `features: { waypoint: false }` 或 `waypointEditable: false`:Ctrl + 点击、`addWaypoint` / `addWaypoints` / `addChannelGroupWaypoint`、通道右键框「加拐点」、拖动已有拐点以及双击 / Delete 删除都会一起变成无操作,数据与历史都不动。拐点只有这一道总闸,没有 `waypointAdd` 之类的细分开关;`shortcuts.addWaypoint` 只决定加点用哪个组合键,不改变总闸。运行时也可以 `view.setFeatures({ waypoint: false })`,`getFeatures().waypoint` 读当前有效值。
|
|
137
137
|
|
|
@@ -155,11 +155,11 @@ const view = createGisView(container, {
|
|
|
155
155
|
拐点就是 `path` 的中间顶点:首尾两端始终是线的端点(`start` / `end` 或绑定的 `from` / `to`),所以顶点数不足 3 的线没有拐点;首次加点按离点击位置最近的线段插入进 `path`。`addWaypoint(lineId, [x, y])` 与 `removeWaypoint(lineId, pathIndex)` 可以在代码里增删,删到只剩两个顶点时写法还原为 `start` / `end`。拖动中的 `phase` 依次是 `start`、`move`、`end`,每次载荷里的 `line` 都是该线路的最新数据。
|
|
156
156
|
|
|
157
157
|
整份换一条线的拐点用 `setLineWaypoints(id, [[x, y], …])`(**只写中间拐点**、不含首尾端点:写进数据时两端按节点 / `start` / `end` 补齐,传单个坐标 = 只留一个拐点、空数组等于去掉全部拐点回到两端直连;通道里的回线改的是它所在分组的骨架),通道骨架用 `setChannelPath(channelId, [[x, y], …])`(同一套写法,只写中间拐点,两端由节点坐标补齐)—— 分过组的通道写的是**第一组**的骨架(`groups[].path` 优先于顶层 `path`;「第一组」按引擎的过滤口径算 —— `lineIds` 空的分组声明会被整组丢掉,跳过它们取第一个真在用的组),要改别的组用 `updateChannel(id, { groups })` 整份声明覆盖。
|
|
158
|
-
`onWaypointChange` 的载荷给 `phase` / `lineIndex` / `pathIndex` / `coordinate` / `group_id` / `lineIndexes` 与最新的 `line` —— `line.path` 可以直接写回业务数据,`lineIndexes`
|
|
158
|
+
`onWaypointChange` 的载荷给 `phase` / `lineIndex` / `pathIndex` / `coordinate` / `group_id` / `lineIndexes` 与最新的 `line` —— `line.path` 可以直接写回业务数据,`lineIndexes` 说明这一个拐点动了哪些回线(通道里是整组的成员)。通道分组用 `'vertex'` 拖动时,`line.path` 只是被操作那条回线的派生几何,后端还要同时保存 `getChannelGroupWaypoints()` 给出的 `path` 与 `waypointOffsets`。导出侧见 [数据格式](../数据格式.md) 的「拐点」一节(`path` / `group_route` / `parallel_groups` 三处写法)。
|
|
159
159
|
|
|
160
160
|
拖动中间态(`start` / `move`)只更新线路数据与引擎读数,**不再逐帧写** `getOptions().channels` 里那份声明副本:`add` / `remove` / `end` 才是落地,声明与导出文档在那一刻跟上(一次拖动因此在历史里只留一条记录);拖动中要看实时骨架用 `getChannelGroups()` / `getChannelGroupWaypoints()`,它们读的本来就是实时几何。
|
|
161
161
|
|
|
162
|
-
通道内的回线按**分组**各记一份拐点:给某条回线加点 / 拖点 /
|
|
162
|
+
通道内的回线按**分组**各记一份拐点:给某条回线加点 / 拖点 / 删点只会改它所在分组的骨架,同一通道里其它分组的线路一根拐点都不动(见「通道分组」)。拖动分组的拐点还有两种模式(`channelWaypointDragMode`):缺省 `'parallel'` 改共用骨架、组内回线保持平行;`'vertex'` 只平移同组同一 `pathIndex` 的拐点,相邻顶点与其它分组不动,结果记在 `groups[].waypointOffsets`。
|
|
163
163
|
|
|
164
164
|
### 拐点样式(waypointStyle / waypointSelectStyle)
|
|
165
165
|
|
|
@@ -206,7 +206,7 @@ view.getParallelGroups()
|
|
|
206
206
|
// ]
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
一个通道的每个**分组**各成一组:`group_id` 取分组 id(没拆分过就是通道的 `id`,拆分出来的分组是 `通道 id::2` 这样的写法;没写 `id` 时用内容签名),`member_line_ids` 是这一分组里的回线,`group_route` 是它们共用的骨架(两端节点 +
|
|
209
|
+
一个通道的每个**分组**各成一组:`group_id` 取分组 id(没拆分过就是通道的 `id`,拆分出来的分组是 `通道 id::2` 这样的写法;没写 `id` 时用内容签名),`member_line_ids` 是这一分组里的回线,`group_route` 是它们共用的骨架(两端节点 + 每个拐点),同一分组的回线只记一份。分组写了 `waypointOffsets` 时,`group_route` 是已经叠上刚性偏移的实际路由。通道外的线路自己成组,组内只有它一条。集合只列**当前还有拐点**的组:删掉最后一个拐点后线路回到两端点,这条组也跟着退出去;换一批线路数据时通道外的组一起作废,通道的组跟着骨架一起保留。`onWaypointChange` 载荷里的 `group_id` 就是这个组号,拐点属于哪个集合一眼可见(分组见「通道分组」)。
|
|
210
210
|
|
|
211
211
|
---
|
|
212
212
|
|
package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md
CHANGED
|
@@ -6,6 +6,26 @@
|
|
|
6
6
|
|
|
7
7
|
一个通道表示一条走廊里并行敷设的多条回线(二 / 三 / 四 / 五回线都行)。回线本身照常写进 `lines`,通道只描述它们怎么排布。通道两端可以绑在节点上:声明 `from` / `to` 之后,**两端必须有节点,通道才显示**——骨架的首尾顶点就是这两个节点的坐标,拖动节点整条通道跟着走,节点被删掉或设成 `visible: false` 时通道与它的回线一起消失。
|
|
8
8
|
|
|
9
|
+
### 通道显隐(visible)
|
|
10
|
+
|
|
11
|
+
通道声明上可以直接写 `visible`(缺省 `true`):
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const channels = [
|
|
15
|
+
{ id: 'c1', lineIds: ['l1', 'l2'], visible: false }, // 数据里先收起
|
|
16
|
+
{ id: 'c2', lineIds: ['l3', 'l4'] }
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
view.updateChannel('c2', { visible: false }) // 只改这一条:整条通道与成员回线退场
|
|
20
|
+
view.updateChannel('c2', { visible: true }) // 原样恢复
|
|
21
|
+
view.hideFeatures('channel', ['c1', 'c2']) // 批量隐藏:一条历史、一条 batch 数据变更
|
|
22
|
+
view.showFeatures('channel', 'c1')
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`visible: false` 是「整条通道退场」,不是删通道:通道声明、成员表、分组、骨架拐点、编辑标记都留在数据里,成员回线跟着通道一起不画;改回 `true` 时按原声明重新接上。它和两端节点缺失走同一条隐藏通路 —— 端点节点写了 `visible: false` 时,以它为端点的通道也会整条隐藏,节点恢复后自动回来。要真正删掉通道与成员回线用 `removeChannel`;要隐藏整条通道用 `updateChannel` / `hideFeatures`。
|
|
26
|
+
|
|
27
|
+
逐条回线也可以写 `lines[].visible: false`:那只收起这一条回线,通道声明、分组、骨架与其它回线都不变;整条通道一起收用 `channel` 上的 `visible` 或 `hideFeatures('channel', ...)`。
|
|
28
|
+
|
|
9
29
|
### 通道选中模式(channelSelectMode)
|
|
10
30
|
|
|
11
31
|
点中通道里的一条回线时选中什么由 `channelSelectMode` 决定(缺省 `'line'`):
|
|
@@ -20,7 +40,36 @@
|
|
|
20
40
|
view.updateOptions({ channelSelectMode: 'channel' }) // 点一下回线 = 选中整条通道
|
|
21
41
|
```
|
|
22
42
|
|
|
23
|
-
Ctrl / ⌘ + 点击在 `'group'` / `'channel'` 下是**整批取反**:这一组 /
|
|
43
|
+
Ctrl / ⌘ + 点击在 `'group'` / `'channel'` 下是**整批取反**:这一组 / 这一条通道都在选中集里时一起取消,否则一起选上(与单击走同一套摊开规则)。按住 `shortcuts.addWaypoint`(默认也是 Ctrl / ⌘)点击通道回线加点时,加完由这一档决定选中并高亮一条、一组还是整条通道。框选、接口选中(`select` / `selectFeatures`)与「按通道写法的接口选中」不受影响。改这一项只重画一帧、不重排场景(`updateOptions({ channelSelectMode })`)。
|
|
44
|
+
|
|
45
|
+
### 分组拐点拖动模式(channelWaypointDragMode)
|
|
46
|
+
|
|
47
|
+
拖动通道分组里的一个拐点时,`channelWaypointDragMode` 决定这一组的骨架怎么变(缺省 `'parallel'`):
|
|
48
|
+
|
|
49
|
+
| 值 | 拖动效果 |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `'parallel'` | 沿用共用骨架 + 平行偏移。骨架顶点跟着鼠标走,拐角处的相邻顶点可能随 miter 一起微调,组内回线保持平行等距 |
|
|
52
|
+
| `'vertex'` | 只平移当前分组同一 `pathIndex` 的拐点:组内每条回线都按相同的世界坐标增量走,相邻拐点、其它顶点与其它分组都不动 |
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
view.updateOptions({ channelWaypointDragMode: 'vertex' }) // 下一次拖动生效
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`'vertex'` 的结果不直接改写分组公共的 `path`,而是写进分组的刚性偏移 `groups[].waypointOffsets`:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
{
|
|
62
|
+
id: 'corridor-1-b',
|
|
63
|
+
lineIds: ['c-3', 'c-4'],
|
|
64
|
+
path: [[..., ...], [..., ...]], // 原骨架(中间拐点)
|
|
65
|
+
waypointOffsets: { '1': [12.5, -8] } // 骨架第 1 个顶点的世界坐标增量
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **序号按骨架算**:键是分组骨架的顶点序号(含首尾端点,从 0 起),但只接受中间拐点;`'1'` 表示含端点后的第二个顶点。值是 `[dx, dy]` 或 `{ x, y }`,组内所有成员回线都叠在各自平行偏移之后。
|
|
70
|
+
- **只属于这一组**:偏移不改其它分组,也不影响通道外的普通线路;普通线路拖动永远只动自己的 `path`。
|
|
71
|
+
- **不会自动清空**:切回 `'parallel'` 或再次 `updateOptions` 只换后续拖动的行为,已有 `waypointOffsets` 仍按数据继续生效。要把一组恢复成普通平行骨架,可把该组偏移拖回零,或重写分组声明(例如 `updateChannel(channelId, { groups })`,组项不写 `waypointOffsets` / 写空对象)后重新落数据。
|
|
72
|
+
- **读数与导出**:`getChannelGroups()` / `getChannelGroupWaypoints()` / `getChannels()` 都带 `waypointOffsets`;`getParallelGroups()` 与 `exportData({ format: 'test' | 'view', parts: ['waypoint'] })` 的 `group_route` 已经把偏移叠到骨架上,导出的是实际路由,不是旧 `path`。
|
|
24
73
|
|
|
25
74
|
### 由数组批量生成:createChannels
|
|
26
75
|
|
|
@@ -114,7 +163,7 @@ console.log(view.getChannels()) // 骨架、每条回线的偏移与推导出来
|
|
|
114
163
|
|
|
115
164
|
通道的配置项没有重复的,成对出现的都是「**全局默认 + 逐通道覆盖**」:`channelWidth` / `width`、`channelNodeWidth` / `nodeWidth`、`channelInset` / `inset`、`channelTangentInset` / `tangentInset`,单个通道不写就取 `options` 上的同名值;`groups` / `path` / `from` / `to` 分别管分组、骨架与两端锚定,各管一件事(`path` 是可选骨架,不写时由锚点或第一条回线补出)。要留意的是各自的**生效条件**(不是冗余,而是只在特定组合下才有意义):`offsetMode: 'tangent'`(默认)时 `inset` / `channelInset` 不参与计算;`nodeWidth` 不为 0 且两端都锚在节点上时 `width` 只作兜底(宽度按节点尺寸算,改它看不到变化);`tangentInset` 只对两端锚定的通道有效(没锚定时切点就是骨架端点,无所谓往哪收);只有一条回线的通道不排偏移(回线就是骨架),`nodeWidth` 与 `tangentInset` 对它的回线位置没有影响。
|
|
116
165
|
|
|
117
|
-
|
|
166
|
+
拐点挂在骨架上:`'parallel'` 模式下拖动通道内任一回线的拐点,整条通道的所有回线一起折线,回线之间保持平行等距;`'vertex'` 模式下只平移当前分组同一序号的拐点(见「分组拐点拖动模式」)。`insertWaypoint` / `removeWaypoint` 增删的也是骨架顶点(首尾两端由节点或原点决定,删不掉)。拐点只显示选中线路的规则对通道一样适用:通道内**任一回线被选中,整条通道的拐点都显示**,这样拖动时能看清整条走廊。拖动不做任何限制:拐点可以拖到两端节点之外、折回去、拐到任意一侧,拖动端点节点时中间拐点也保持原样,骨架怎么走回线就怎么平行偏移(`channelPathFor` 就是骨架的纯平行偏移)。`onWaypointChange` 载荷里的 `coordinate` 与 `line` 都是被操作的那条回线的最新几何(`line.path` 是推导后的折线);通道分组还要一起保存 `waypointOffsets`,只写 `line.path` 或只写公共 `path` 会丢掉 `'vertex'` 拖动结果。`getChannels()` 返回运行时状态:`{ id, channelId, channelIndex, lineIndexes, skeleton, waypointOffsets, offsets, paths, from, to, anchored, spacing }`——`anchored` 为 `true` 表示两端都落在节点上,`spacing` 是按两端节点尺寸算出来的相邻回线中心间距(分组之后 `offsets` 是这一组在整条通道里的那几条车道,`spacing` 仍是整条通道的间距),需要自有覆盖层或回写业务数据时用它。
|
|
118
167
|
|
|
119
168
|
两端锚定的通道与节点是联动的:拖动端点节点,骨架首尾跟着走(中间拐点保留),所有回线一起重算;节点被删掉或设成不可见时,通道与它的回线都从这一帧开始不参与渲染,节点回来后通道恢复,之前编辑过的拐点还在。
|
|
120
169
|
|
|
@@ -130,7 +179,8 @@ view.getChannelGroups('corridor-1')
|
|
|
130
179
|
// lineIndexes: [0, 1, 2, 3], lineIds: ['c-1', 'c-2', 'c-3', 'c-4'],
|
|
131
180
|
// anchored: true, edited: true,
|
|
132
181
|
// skeleton: [[116.2, 39.3], [116.7, 39.6], [117.2, 39.15]],
|
|
133
|
-
// path: [[116.7, 39.6]] // 可以直接写回 groups[].path
|
|
182
|
+
// path: [[116.7, 39.6]], // 可以直接写回 groups[].path
|
|
183
|
+
// waypointOffsets: {} // 'vertex' 拖出来的刚性偏移
|
|
134
184
|
// }]
|
|
135
185
|
|
|
136
186
|
// 拆成两组:每组最少一条;裸数组也行(分组 id 自动取 通道 id / 通道 id::2)
|
|
@@ -172,23 +222,23 @@ view.updateChannel('corridor-1', { name: '东干线', lineIds: ['c-1', 'c-2', 'c
|
|
|
172
222
|
- **一次拆几条走 `addChannelGroup(channelId, lineIds, options?)`**:`moveChannelLine` 一次只挪一条,要把两三回一起单独成组用这一条 —— **数据里没写 `groups` 的通道照样能拆**(没写就是「整条通道一组」,这几条从第一组里挪出去,被挪空的分组整组消失),新分组缺省照抄来源组当前的骨架(拆出来那一组画面不会跳),`options.path` 自己给这一组的中间拐点、`path: []` 要一条直连;组数 / 回线数到上限返回 `{ ok: false, reason }`,认不出的回线进 `skipped`、不影响其余条目。
|
|
173
223
|
|
|
174
224
|
- **合并时拐点跟随目标分组**:被并入的线路读的是目标分组的骨架,目标分组没有拐点就一条都不带(它们原来的拐点随之作废),目标分组自己的拐点原样保留;没参与合并的分组不受影响(拐点与编辑标记都留着)。
|
|
175
|
-
- 分组项可以写成 `{ id, lineIds, path }` 显式给这一组一份声明:给了 `path` 就按它重建(想主动清掉某组的拐点就写 `path: []`),只写 `id` + `lineIds` 则沿用同 id
|
|
225
|
+
- 分组项可以写成 `{ id, lineIds, path, waypointOffsets? }` 显式给这一组一份声明:给了 `path` 就按它重建(想主动清掉某组的拐点就写 `path: []`),只写 `id` + `lineIds` 则沿用同 id 分组当前的骨架与偏移;要清掉 `'vertex'` 偏移而不动骨架,重写声明时省略 `waypointOffsets`(空对象同义),只写 `id` + `lineIds` 会把当前偏移一起沿用。
|
|
176
226
|
- **分组变化另有专用事件 `channelGroupChange` / `onChannelGroupChange`**:拆分 / 合并 / 挪线 / 新建组与整份换声明除了照旧发 `dataChange('channelGroups')`(说「数据变了」),还会发这一条(说「变了什么」):`action.type` 为 `splitGroups` / `mergeGroups` / `moveLine` / `addGroup` / `setGroups`,`action.groupId` / `groupIds` / `lineIds` 指到具体分组与回线,`item` / `groups` 与 `dataChange` 那份是**同一个引用**(不想区分动作就照旧只接 `onDataChange`)。分组拐点写回声明那条路不发这一条 —— 它属于 `onWaypointChange`。
|
|
177
227
|
#### 拐点 / 分组同步后端(推荐)
|
|
178
228
|
|
|
179
229
|
这两类事件都不要逐帧提交,也不要把事件里的 action 当成完整数据:
|
|
180
230
|
|
|
181
231
|
- **拐点**:`onWaypointChange` 的 `phase` 是 `add` / `select` / `remove` / `start` / `move` / `end`。只有 `add`、`remove`、`end` 会改变并落地数据;`start` / `move` 是拖动中间态,`select` 只改选中,三者都不应发写请求。拖动一次会在松手时落成一批可撤销的历史,同步也只在 `end` 发一条。
|
|
182
|
-
- **通道拐点**:事件载荷里的 `line`
|
|
183
|
-
- **分组**:`onChannelGroupChange` 的 `action.type` 可能是 `splitGroups` / `mergeGroups` / `moveLine` / `addGroup` / `setGroups`;动作只是审计信息,声明以 `getChannelGroups(channelId)` 返回的整份 `groups` 为准(每项含 `id` / `lineIds` / `path`)。不要根据这个 action 自行猜「删了哪一组」:被挪空的分组会整组消失,按整份快照覆盖最稳。
|
|
232
|
+
- **通道拐点**:事件载荷里的 `line` 是**被操作那条回线**经过平行与刚性偏移后的几何,不能拿它覆盖整组骨架。要整份覆盖时,用 `channelId` + `groupId`(载荷里也可能是 `group_id`)调 `getChannelGroupWaypoints(channelId, groupId)`,提交返回的 `path` 与 `waypointOffsets`。只提交 `path` 会把 `'vertex'` 拖动结果丢掉。独立线路没有通道字段,才用 `PATCH /gis/lines/{id}/path`。
|
|
233
|
+
- **分组**:`onChannelGroupChange` 的 `action.type` 可能是 `splitGroups` / `mergeGroups` / `moveLine` / `addGroup` / `setGroups`;动作只是审计信息,声明以 `getChannelGroups(channelId)` 返回的整份 `groups` 为准(每项含 `id` / `lineIds` / `path` / `waypointOffsets`)。不要根据这个 action 自行猜「删了哪一组」:被挪空的分组会整组消失,按整份快照覆盖最稳。
|
|
184
234
|
- **空 path 合法**:删掉分组最后一个拐点时 `path` 就是 `[]`。这正是「回到直连」的完整声明,必须照常发请求;不要用 `if (!path.length) return` 把删除动作吞掉。
|
|
185
235
|
|
|
186
|
-
Demo 的「通道与数据」页直接接了两类假接口:通道分组拐点走 `PATCH /gis/channels/{id}/groups/waypoints`,独立线路走 `PATCH /gis/lines/{id}/path`,分组声明走 `PATCH /gis/channels/{id}/groups`。请求头带 `Idempotency-Key` / `If-Match`,body 带 `channelId` / `groupId` / 整份 `path` 或整份 `groups`,外层再补 `baseVersion` 与 `opId`;假后端延迟 420ms,按 `opId` 幂等,并模拟 200 / 网络失败 / 500 / 409。成功响应统一为 `{ code, message, requestId, traceId, data: { operation, version, idempotent, transactionId, result } }`:分组接口的 `result` 回最新 `groups` / `groupCount` / `lineCount`,拐点接口回 `path` / `pointCount`。失败时本地编辑仍在,面板给出「重试最近失败请求」,同 `opId` 重试会命中幂等缓存;409 后用新版本 rebase,再生成新的 `opId` 重新提交;「重放最近成功请求」复用原 `opId`,版本不会前进。完整请求 / 响应字段见[编辑接口协议](../接口协议.md)。
|
|
236
|
+
Demo 的「通道与数据」页直接接了两类假接口:通道分组拐点走 `PATCH /gis/channels/{id}/groups/waypoints`,独立线路走 `PATCH /gis/lines/{id}/path`,分组声明走 `PATCH /gis/channels/{id}/groups`。请求头带 `Idempotency-Key` / `If-Match`,body 带 `channelId` / `groupId` / 整份 `path` + `waypointOffsets` 或整份 `groups`,外层再补 `baseVersion` 与 `opId`;假后端延迟 420ms,按 `opId` 幂等,并模拟 200 / 网络失败 / 500 / 409。成功响应统一为 `{ code, message, requestId, traceId, data: { operation, version, idempotent, transactionId, result } }`:分组接口的 `result` 回最新 `groups` / `groupCount` / `lineCount`,拐点接口回 `path` / `pointCount`。失败时本地编辑仍在,面板给出「重试最近失败请求」,同 `opId` 重试会命中幂等缓存;409 后用新版本 rebase,再生成新的 `opId` 重新提交;「重放最近成功请求」复用原 `opId`,版本不会前进。完整请求 / 响应字段见[编辑接口协议](../接口协议.md)。
|
|
187
237
|
|
|
188
238
|
生产环境还要加两件事:**按通道串行**(同一条通道的请求不能并发覆盖,拖动期间只保留最后一个 `end`),以及**服务端版本校验**。整份覆盖接口应返回新版本号;客户端收到 409 时先拉取该通道最新声明,再让用户决定 rebase 还是放弃本地这次编辑。稳定 id 优先于数组下标,`groupId` 与 `lineIds` 都按声明里的 id 提交。
|
|
189
239
|
|
|
190
240
|
- **加点 / 删点 / 拖点只作用于同一分组**:同一分组共用一条骨架,其余分组的线路不动。
|
|
191
|
-
- 分组结果会写回 `channels[].groups`(`{ id?, lineIds, path? }`),下次 `updateOptions` / 换数据按它重建;也可以一开始就手写 `groups` 而不是 `lineIds` + `path`。
|
|
241
|
+
- 分组结果会写回 `channels[].groups`(`{ id?, lineIds, path?, waypointOffsets? }`),下次 `updateOptions` / 换数据按它重建;也可以一开始就手写 `groups` 而不是 `lineIds` + `path`。
|
|
192
242
|
- `getChannelMembers()` 与鹰眼图都按分组各成一份:`channelCircuits: false` 时每个分组各画偏移最靠中间的那一根回线。
|
|
193
243
|
|
|
194
244
|
---
|
|
@@ -26,10 +26,35 @@ view.addChannelGroupWaypoint()
|
|
|
26
26
|
- 不传坐标时按「分开分组」的默认位置落点:取这一组最长的一段骨架,在它的中点沿法线往外让一档 —— 第 1 组原地不动、第 2 组让一档、第 3 组再往外一档,档距是这一组的回线跨度 + 一个回线间距,所以拉开的几组既不会重合也不会贴在一起。被拉走的组仍然按自己在整条通道里的那几条车道相对骨架平行偏移(组内照样等距),拖走、拖回来都不影响别的分组。
|
|
27
27
|
- 分组拐点只挂在**这一组的骨架**上:加 / 删 / 拖都只动这一组的线路与拐点。两种加点入口的差别只在怎么给坐标:`insertWaypoint(lineIndex, ...)`(实例层 `addWaypoint(id, ...)`)从回线进去、把坐标投影到该回线所属分组的骨架上;`addChannelGroupWaypoint()` 从分组进去、缺省位置就是上面那个「分开分组」的偏移落点。`removeWaypoint(lineIndex, pathIndex)` 与 `removeChannelGroupWaypoint()` 同理,删的都是这一组骨架上的顶点。
|
|
28
28
|
- 增删都会发 `onWaypointChange`,载荷带 `channelId` / `groupId` 与代表回线(这一组的第一条)的 `line`;实例层收到之后会把这一组的声明同步回 `channels[].groups`,下次 `updateOptions` 不会把分组拐点弹回整条通道。`addChannelGroupWaypoint()` 还会顺手把这一组选出来(多选,整组高亮)。
|
|
29
|
-
-
|
|
29
|
+
- **存的是公共拐点与分组偏移**:一个分组只有一份骨架,加 / 删 / 拖都只写这一份(数据里是 `channels[].groups[].path`);`'vertex'` 模式额外把同组同序号的刚性增量记在 `channels[].groups[].waypointOffsets`。每条回线「自己的」拐点不落数据 —— 它是公共骨架按这条回线所在车道偏移、再叠分组刚性偏移推出来的只读读数(与图上画的一致)。想「让某条回线单独拐」就把它拆成新分组(`moveChannelLine(id, lineId, 'newGroup')` 或 `addChannelGroup`),新的那一组才多出一份公共骨架。
|
|
30
30
|
- 区分这两层读数用 `getChannelGroupWaypoints(channelId, groupId)`:公共那一份是 `path` / `skeleton`(返回里 `scope: 'group'`,给后端 `channel.group.path` 就是它,组内不必按回线逐条发),各自那一份是 `lines[].path` / `lines[].waypoints`(`derived: true`,只读)。加 / 删 / 拖之后照公共那一份重新拼整份 `path` 覆盖即可;各自的那份不用回写。
|
|
31
31
|
- 右键交互框里的「加拐点 / 删拐点」按钮走的是同一条路径:`runChannelAction('add-waypoint' | 'remove-waypoint', { channelId, groupId, coordinate?, pathIndex? })`。
|
|
32
32
|
|
|
33
|
+
#### 拖动模式:parallel / vertex
|
|
34
|
+
|
|
35
|
+
拖动分组拐点时,`channelWaypointDragMode` 决定骨架怎么改(缺省 `'parallel'`;普通线路始终只动自己的拐点):
|
|
36
|
+
|
|
37
|
+
- `'parallel'`:保留现有的共用骨架 + 平行偏移逻辑。拖动骨架顶点,组内回线按各自车道重新平行推导,拐角处的相邻顶点可能随 miter 一起微调。
|
|
38
|
+
- `'vertex'`:只平移当前分组同一 `pathIndex` 的拐点。组内每条回线都按相同世界增量移动这一序号,相邻拐点、其它顶点与其它分组一个都不动;结果记在分组的 `waypointOffsets` 上,不重建公共 `path`。
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
view.updateOptions({ channelWaypointDragMode: 'vertex' })
|
|
42
|
+
|
|
43
|
+
// 拖过之后,分组声明会写成:
|
|
44
|
+
{
|
|
45
|
+
id: 'corridor-1-b',
|
|
46
|
+
lineIds: ['c-3', 'c-4'],
|
|
47
|
+
path: [[100, 80], [260, 80]],
|
|
48
|
+
waypointOffsets: { '1': [42, 30] }
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- `waypointOffsets` 的键是**分组骨架顶点序号**(含首尾端点,从 0 起),只接受中间拐点;值是这一组所有成员回线都要叠加的世界坐标增量,写法为 `[dx, dy]` 或 `{ x, y }`。
|
|
53
|
+
- 偏移按组保存。`getChannelGroups()` / `getChannelGroupWaypoints()` / `getChannels()` 都返回 `waypointOffsets`;`getParallelGroups()` 与 `exportData({ format: 'test' | 'view', parts: ['waypoint'] })` 的 `group_route` 已经叠上偏移,导出的是实际路由。
|
|
54
|
+
- 组内每条回线仍先按自己的车道做平行偏移,再叠这一份刚性偏移;所以同组所有回线在 `'vertex'` 拖动时一起平移同一距离,回线之间保持原来的间距。
|
|
55
|
+
- 模式切换只影响下一次拖动,**不会清空已有的 `waypointOffsets`**。要恢复纯平行骨架,可把偏移拖回零,或重写分组声明并带上当前 `path`、省略 `waypointOffsets`(例如整份 `updateChannel(channelId, { groups })`);只写 `id` + `lineIds` 的分组补丁会沿用当前偏移。
|
|
56
|
+
- 后端同步时,通道分组拐点不能只交 `path`:同时提交 `getChannelGroupWaypoints()` 返回的 `path` 与 `waypointOffsets`,否则刷新后会回到旧骨架 / 丢掉非平行拖动结果。
|
|
57
|
+
|
|
33
58
|
### 通道右键交互框
|
|
34
59
|
|
|
35
60
|
右键点中通道的回线,引擎把这条通道的上下文交给 `onChannelContextMenu`(**通道右键框缺省关**:右键本身要接得住 —— `contextMenu` / `interactive: true` / `features: 'view'` 任一 —— 再加 `features: { channelMenu: true }`):
|
|
@@ -58,7 +83,7 @@ view.getChannelContext()
|
|
|
58
83
|
view.getChannelContext('corridor-1', 'corridor-1::2')
|
|
59
84
|
```
|
|
60
85
|
|
|
61
|
-
- 载荷是 `{ channelId, channelIndex, channel, groupId, groupCount, groups, group, actions, lineIndex, lineId, coordinate, x, y, clientX, clientY, originalEvent }`:`groups` 是这条通道的全部分组读数(`{ id, channelId, index, lineIds, lineIndexes, lineCount, waypointCount, edited, anchored, current, skeleton, path }`),`group` 是其中的当前分组,`groupCount` 是组数;`x` / `y` 是命中的画布 CSS 像素(`clientX` / `clientY` 是页面坐标),交互框照着摆就行。
|
|
86
|
+
- 载荷是 `{ channelId, channelIndex, channel, groupId, groupCount, groups, group, actions, lineIndex, lineId, coordinate, x, y, clientX, clientY, originalEvent }`:`groups` 是这条通道的全部分组读数(`{ id, channelId, index, lineIds, lineIndexes, lineCount, waypointCount, edited, anchored, current, skeleton, path }`;引擎的完整分组读数还带 `waypointOffsets`),`group` 是其中的当前分组,`groupCount` 是组数;`x` / `y` 是命中的画布 CSS 像素(`clientX` / `clientY` 是页面坐标),交互框照着摆就行。
|
|
62
87
|
- `actions` 每项 `{ id, label, enabled, reason? }`:`enabled: false` 时 `reason` 说明原因(拐点编辑关了、这一组还没有拐点、已经是多组先合并再拆…),界面直接照着渲染按钮即可。
|
|
63
88
|
- 六个动作 id:`select-group`(选中该分组)/ `deselect-group`(取消该分组的选中,等价于 `deselect({ channelId, groupId })`)、`add-waypoint` / `remove-waypoint`(给这一组加 / 删拐点,见上一节)、`split`(按 `options.size` 每组几条把通道拆成多组,只有一组且多于一条回线时可用)、`merge`(其余分组并回当前这一组)。
|
|
64
89
|
- 更自由的分配走 `moveChannelLine(channelId, lineId, target)`:按**回线**粒度拆成新分组或并进别的分组 —— 两回线选「拆成新分组」就是两个分组各一条(见「通道分组」)。
|
|
@@ -36,7 +36,7 @@ view.updateOptions({
|
|
|
36
36
|
| `position` | `GisViewLabelPosition` | `'right'` | 八向与居中 |
|
|
37
37
|
| `offset` / `gap` | `[number, number]` / `number` | `[0, 0]` / `8` | 额外像素偏移与节点间距(`anchor: 'edge'` 时从符号外沿量起) |
|
|
38
38
|
| `anchor` | `'edge' \| 'center'` | `'edge'` | 贴边方式:`edge` 让标签贴着符号外沿,节点放大后标签一起往外让;`center` 是从节点中心量 |
|
|
39
|
-
| `html` | `boolean` | `false` |
|
|
39
|
+
| `html` | `boolean` | `false` | 字符串 `content` 是否按 HTML 解析。逐条 `labels[].html` / `updateLabel(..., { html })` 可以覆盖全局;写 `false` 时标记按字面文本显示。模板取值、`field` 与默认名称始终转义,函数返回 `HTMLElement` 时不受它影响。**它只控制解析方式,不决定容器**:写 `false` 仍然逐条建 `.gis-view__label`,要减少高密度纯文本标签的 DOM 见「标签容器渲染(labelRender)」 |
|
|
40
40
|
| `when` | `GisViewConditionInput` | 无 | 条件语言,只给命中的节点渲染标签 |
|
|
41
41
|
| `maxCount` | `number` | `200` | 最多渲染多少个标签(按当前视口内可见点的顺序取,被避让丢掉的候选不计入),`0` 表示不限制;只在 `labelOptimize: true` 时生效(缺省强制显示档不截断) |
|
|
42
42
|
| `className` / `style` | `string` / `Record<string, string>` | 无 | 附加类名与内联样式:**不用写 HTML 也能改外观** —— `style` 是内联样式的增量合并,`background` / `color` / `fontSize: '14px'` / `fontWeight` / `borderRadius` / `padding` 这些写什么加什么(值按 CSS 写法给字符串),逐条再叠 `updateLabel` 的 `style`;不配就是样式表里的默认(白底 88% / `#1f3550` / 12px),想清掉某一项就写 `{ 那一项: null }` |
|
|
@@ -52,6 +52,10 @@ view.updateOptions({
|
|
|
52
52
|
|
|
53
53
|
函数形态的 `content` 会收到 `{ index, item, x, y, screen, radius }`(`radius` 是节点画在屏幕上的半径),需要自己拼 DOM 时直接返回元素即可。`anchor: 'edge'` 时标签到节点的距离始终是「符号当前的屏幕半径 + `gap`」:节点被悬停 / 选中样式放大,标签跟着往外让,缩小跟着靠近;缩放地图时标签按新相机重新投影,间距不会跟着地图一起被放大(鹰眼图放大缩小几次之后标签也不会飘出画布)。`html: true` 时字符串按 HTML 渲染:模板串里的取值仍然会转义,自己拼标记时也要记得转义业务字段。标签只渲染当前视口内的节点,相机变化后按 `requestAnimationFrame` 合并重排,移出视口的标签 DOM 回收进池子复用;单个节点可以用 `labelVisible: false`、`labelPosition`、`labelOffset` 覆盖全局配置,也可以用 `labelVisibility: 'always' | 'hidden'` 单独指定显隐策略(`labelVisible: true` 等价于 `always`)。
|
|
54
54
|
|
|
55
|
+
`html` 是**逐条**开关:全局 `label.html: true` 时,某条数据仍可写 `labels: [{ id: 'default', content: '<b>{name}</b>', html: false }]`,这一条就显示字面的 `<b>…</b>`;反过来全局 `false`、单条写 `true` 也会按标记渲染。运行时用 `updateLabel(kind, ref, { html: true | false })` 双向切换,写 `null` 回到全局 / 声明值。只有字符串 `content` 受它控制,函数返回 `HTMLElement` 时始终按元素插入。`getLabels()` 的清单以及 `labelClick` / `labelDrag` 载荷里的 `label` 都会带 `html`;HTML 模式下 `content` 返回 `innerHTML`,编辑面板回写时不会把 `<b>` / `<span>` 洗成纯文本。导出数据会把显式 `true` / `false` 一起写进 `labels`,导出再回灌保持原语义。
|
|
56
|
+
|
|
57
|
+
HTML 标签不等于每帧重新 `innerHTML`:覆盖层把内容、内联样式、class 与尺寸签名分开比较,只有内容真的变化才重解析标记;只改字号 / 颜色 / 位置时内容一个字节都不重写。尺寸缓存也按内容与样式共享,不同节点上的同文案标签只量一次布局。HTML 解析本身仍有成本,能靠 `style` / `className` 完成的外观优先用它们;大量富文本标签建议配合 `labelOptimize: true`、`maxCount` 与 `scanLimit`。
|
|
58
|
+
|
|
55
59
|
排布顺序上有两层稳定化:上一轮已经显示出来的标签优先占位(平移时不会因为扫描窗口换人而成片消失),同一轮里 `always` 的排在 `auto` 前面,`auto` 抢不到位置就不画。避让用的是按 64 px 分桶的网格索引,候选再多也不会退化成两两比较;竖长拖拽时每隔 220 ms 会补一次真实排版,不然整层变换只能带着上一轮那批标签走。整层变换只剩平移:缩放时一条标签都不缩放文字 —— 512 条以内逐个按新相机重投影,超过 512 条按排版时记下的节点中心与固定像素偏移线性推,字号、清晰度与观感都保持原样。标签落点吸附到**设备像素网格**(`devicePixelRatio × 宿主显示比例`,1.25 / 1.5 倍屏上整数 CSS 像素仍可能落在半个设备像素上);拖动期整层位移也按同一网格吸附,且逐条标签不再各自写 `will-change`,只保留标签层整体的合成提示,避免多个合成层产生相位差与重复栅格化。节点标签、线路标签、提示框、底图区域文字的投影都走这一份口径。默认开启的标签避让(`avoidOverlap`)在候选和已经放下的标签重叠时按 `overlapMode` 走:`'shift'`(默认)主动换落点,节点标签绕这个节点换锚点再试(`anchorFallback`,默认最多试 4 个:先换对侧,再按左 / 上 / 下…绕一圈),线路标签沿线滑动(`slideFallback`),找不到空位才不画;`'hide'` 不做任何位置回退,碰撞就直接不显示。换向只改 class 与 transform,内容与尺寸都不重量,也没多读一次布局。底图的区域文字(`background.geoJsonStyle.label`)另占一张「软」网格:标签先绕开它,实在绕不开照画 —— 数据标签优先于底图上写死的文字;只压到区域文字不会触发 `hide`。
|
|
56
60
|
|
|
57
61
|
**一个节点上多条标签**:`label` 也可以写成数组,数组里每一条各自有内容 / 位置 / 样式 / 显隐,`id` 是它与数据里的 `labels`(见「标签(labels,导入即回显)」)以及 `updateLabel` / `removeLabel` 对上号的键:
|
|
@@ -138,6 +142,21 @@ view.updateOptions({ labelOptimize: true }) // 切到优化档;false / 不写
|
|
|
138
142
|
|
|
139
143
|
两档的差别在密集场景里是数量级:同一个视口里挤着 2 万个节点(1280×800、quality high,实测见「性能实测与 WebGPU 评估」),优化档只画得下 57 条标签、连续平移 / 缩放都在一帧 17 ms 上下(顶到刷新率);强制显示档 2 万个标签全画,同样的平移 / 缩放要 187.5 / 390.7 ms 一帧。十万级数据建议显式打开;另一头,强制显示档因为避让判定整段不走,连底图区域文字那张「软」网格都不用铺(省掉逐条量区域文字盒子的那一趟)。
|
|
140
144
|
|
|
145
|
+
## 标签容器渲染(labelRender)
|
|
146
|
+
|
|
147
|
+
`html` 与顶层 `labelRender` 解决的是两件事:`html: false` 只关闭字符串的 HTML 解析,标签外壳仍是逐条 `.gis-view__label`(默认 `div`),几万条纯文本标签就会产生几万个节点;`labelRender` 决定这些纯文本标签要不要换成一张画布。
|
|
148
|
+
|
|
149
|
+
- **`auto`(缺省)**:只在**强制显示档**(`labelOptimize` 未开、`denseMode` 未激活)启用。节点层与线路层各自统计这一轮能画布的纯文本标签,某一层达到 **1024** 条,该层就改画到一张 `.gis-view__label-canvas`,不再为这些标签建 `div`;两层互不牵连。
|
|
150
|
+
- **`dom`**:始终逐条建 `.gis-view__label`,保持旧行为。数据量不大、要自己用 CSS / 事件精细控制标签时用它。
|
|
151
|
+
- **仍走 DOM 的标签**:`html: true`、函数返回 `HTMLElement`、可拖拽 / 可编辑,以及 Canvas 画不全的样式(背景图、阴影、`transform`、多行、字间距、非实线或四边不一致的边框等)会留在同层 DOM 里,和画布标签可以混排。画布只接管纯文本且样式能完整复刻的那部分。
|
|
152
|
+
- **画布档不参与优化档**:`labelOptimize: true` 或进入密集档后,标签仍走逐条 DOM —— 避让、换落点、截断与命中判定都要真实量尺寸,Canvas 这条路不会绕过它们。所以高密度场景应优先打开 `labelOptimize`;如果业务要求“一条都不能少”,再让缺省的 `auto` 用画布承接强制显示档。
|
|
153
|
+
- **画布与交互**:画布按视口 CSS 尺寸与设备像素比(含宿主显示比例,夹在 0.5 ~ 4)分配像素;平移、缩放、拖节点、拖线路、悬停半径变化时同步重算,`exportImage({ includeLabels: true })` 也会把画布标签写进 SVG。数量回落到阈值以下或切到 `dom` 时,DOM 标签会重新建回来;切换只重排标签层,不动引擎。
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
view.updateOptions({ labelRender: 'auto' }) // 缺省:高密度纯文本自动走单画布
|
|
157
|
+
view.updateOptions({ labelRender: 'dom' }) // 强制逐条 DOM,保持旧行为
|
|
158
|
+
```
|
|
159
|
+
|
|
141
160
|
## 标签的拖动与编辑
|
|
142
161
|
|
|
143
162
|
节点标签与线路标签各有一对开关(`label` / `lineLabel` 上的 `draggable` 与 `editable`,都缺省 `false`),打开之后标签自己接住指针,关着时标签照旧 `pointer-events: none`,一点都不挡画布:
|
|
@@ -155,6 +174,31 @@ view.on('labelDblClick', payload => openEditor(payload)) // 双击标签:载
|
|
|
155
174
|
view.on('labelChange', payload => {}) // { action: 'update' | 'reset' | 'resetAll' | 'add' | 'remove' | 'restore' | 'addAll' | 'updateAll' | 'removeAll', kind, index, id, labelId?, patch?, state?, removed? }
|
|
156
175
|
```
|
|
157
176
|
|
|
177
|
+
### 逐条标签的交互开关
|
|
178
|
+
|
|
179
|
+
`draggable` / `editable` 不只能配在全局 `label` / `lineLabel` 上:数据里的 `labels[]`、`addLabel` / `addLabels` 的定义也可以逐条写。规则是「这一类有任意一条全局配置打开,就成为该类标签的缺省;这一条自己写了就以自己为准」:
|
|
180
|
+
|
|
181
|
+
- 没写 `draggable` / `editable` 的标签跟随该类缺省(`options.label` 或 `options.lineLabel` 里任意一条写了 `true` 就算打开)。
|
|
182
|
+
- 写了 `true` 只让这一条接指针;写了 `false` 可以**单独关掉这一条**,即使全局是 `true`。
|
|
183
|
+
- 两个都关着(或缺省就是关)时,这一条标签保持 `pointer-events: none`,不挡画布,也不发 `labelClick` / `labelDrag` / 右键 / 双击。
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
const points = [{
|
|
187
|
+
id: 'p-1', x: 0, y: 0, name: '甲站',
|
|
188
|
+
labels: [
|
|
189
|
+
{ id: 'name', content: '{name}', position: 'right', draggable: true, editable: true }, // 这一条可拖可点
|
|
190
|
+
{ id: 'note', content: '只读备注', position: 'bottom', draggable: false, editable: false } // 全局开着也单独关掉
|
|
191
|
+
]
|
|
192
|
+
}]
|
|
193
|
+
|
|
194
|
+
const view = createGisView(container, {
|
|
195
|
+
points,
|
|
196
|
+
label: { content: '{name}', draggable: true, editable: true } // 没写交互字段的标签跟随这一档
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
拖动仍按「要素 + `labelId`」记偏移,点击仍按同一条发 `labelClick`;A 拖出来的位移不会写到 B 身上。逐条标签如果放在优化档(`labelOptimize: true`)里,还要注意避让可能把某一条挤掉;要保证同一节点上的两条都出现,可以给它们不同的 `offset`,或在定义里写 `avoidOverlap: false`(`LabelDemo` 的第一个节点就是这么让「可拖 / 可编辑」与「只读」两条同时显示的)。
|
|
201
|
+
|
|
158
202
|
**点的是哪一条标签、挂在哪条要素上**:`labelClick` 与 `labelDrag` 的载荷里都带两份现成的读数,不用自己去 `getLabels()` 反查:
|
|
159
203
|
|
|
160
204
|
- `label` —— 这一条标签自己:`{ id, content, position, className, style, source, own, added, definition }`。`content` 是当下画在屏幕上的文本(模板 / 函数算完之后的那一份),`position` / `style` 是合并之后的生效值,`source` 说明它是哪来的(`'config'` 配置里定义的 / `'data'` 数据里写的 / `'runtime'` 运行时 `addLabel` 加的),`definition` 只在运行时加的标签上有(持久化回显按它重建)。
|
package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md
CHANGED
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
| `copyPointToClipboard(sources?, options?)` / `copyPointsToClipboard(sources?, options?)` / `pastePoint(options?)` / `pastePoints(options?)` / `getPointClipboard()` | 剪贴板:`copyPointToClipboard` 默认收当前选中的全部节点(只有没选中才回落到光标下的那个),`copyPointsToClipboard` 只认给定 / 选中的一批;Ctrl+V 时单节点且没有内部线路 / 通道仍走原预览,多节点或带线路 / 通道走整组落地,`pastePoints` 强制整组(单节点也走批量载荷)。`getPointClipboard()` 读 `count` / `sources` / `items` / `lines` / `channels` / `anchor` 快照,见「节点复制」 |
|
|
84
84
|
| `mergePoints(source, target, options?)` | 把来源节点并进目标节点(接口调用,不做确认):来源 / 目标收 id、下标或 `{ id }` / `{ index }`,节点数据按 `merge.createItem` 合并(缺省目标优先、尺寸取双方最大、图标优先目标),改线路绑定、删自环 / 零长线 / 自环通道、把撞成同一条的通道合成一条,返回 `{ ok, item, linesChanged, removedLines, removedChannels, mergedChannels }`;并不了返回 `ok: false` 并另发一条失败事件,见「节点合并」 |
|
|
85
85
|
| `mergeSelectedPoints(options?)` | 框选 / 多选之后一步把选中的节点并到一个目标上(接口调用):`target` 不指定就自动挑**绑定线路最多**的那一个(一样多取画得大的、再取下标小的),来源默认是除目标之外的选中节点、也可以用 `sources`(id / 下标 / `{ id }` / `{ index }`,单个或数组)直接给 —— 这时不用先选中;`mergeable: false` 默认跳过(`force: true` 越过)、`label` 改这一步历史的标签。**多个来源合成一步历史**(数据变更收成一条 `batch`),返回 `{ phase: 'mergeSelected', ok, reason, target, merged, skipped, sourcesMerged, linesChanged, removedLines, removedChannels, mergedChannels, channelsChanged }`,见「节点合并」 |
|
|
86
|
+
| `splitPoint(source, parts, options?)` | `mergePoints` 的逆操作:把一个节点拆成两份或更多(第一份接在原下标上、缺省沿用原 id,其余追加到末尾),按每份的 `item` / 顶层字段生成新节点,按 `lines` 分配线路、按 `channels` / `channelFrom` / `channelTo` 分配受影响通道的两端。没显式分配的线路跟第一份;没显式分配的通道端点跟它第一条被分配过的成员回线。线路的 `from` / `to` 与和来源坐标重合的 `start` / `end` / `path` 顶点改到对应新节点;拆完后的自环 / 零长度线路与两端落到同一份的通道删掉,成员全没了的通道也删掉。整次调用只记一条历史、只发一条 `splitPoint` 数据变更,返回的 `changes` 可直接作为后端事务输入;至少两份、坐标必须有效、线路不能重复分配,写错时 `ok: false` 并在 `reason` 说明 |
|
|
86
87
|
| `getPointMerge()` / `resolvePointMerge(confirmed?, options?)` / `commitPointMerge()` / `revertPointMerge(options?)` | 读 / 裁决 / 收尾当前待确认的合并(把一个节点拖到另一个节点上之后):`resolvePointMerge(true, { keep, item })` 推进到 `confirm` 阶段 —— `keep` 选保留碰撞目标(缺省)还是拖动的来源,`item` 改写最终节点的信息与样式,位置 / id 始终取保留端;`resolvePointMerge(false)` 让拖动的节点回原位。`pending` / `confirm` / `getPointMerge()` 的载荷带 `impact`(受影响线路条数、具体 lineIds 与通道读数;`lineCount` 按线路去重,`linesChanged` / `removedLines` 与落地结果同口径)。自己接管数据落地时用 `commitPointMerge()` 收状态、`revertPointMerge()` 还原(`silent: true` 不补发 cancel 载荷) |
|
|
87
88
|
| `removePoints(refs, options?)` | 移除节点(接口调用):`'a'` / `{ index: 2 }` / 数组都收,绑在它上面的线路一并删掉、声明了它当端点的整条通道(含成员回线)跟着删,返回 `{ ok, indexes, items, removedLines, removedChannels }` |
|
|
88
89
|
| `requestRemove(options?)` | 发起一次删除(Delete 键同一条路):把选中的拐点 / 线路 / 节点与它们的连带影响摊成一份载荷交给 `onRemove` / `'remove'`,停在 `pending` 等裁决(确认方式见 `remove.confirm`),没有可删的东西时返回 `{ phase: 'empty', ok: false, reason }`,见「删除(remove)」 |
|
|
@@ -139,6 +140,7 @@
|
|
|
139
140
|
| `removeChannel(channelId)` | 移除整条通道:它名下的回线一条条摘掉,返回 `{ ok, channelId, lineIds, lines, removedChannel }` |
|
|
140
141
|
| `updatePoint(id, patch)` / `updateLine(id, patch)` / `updateChannel(id, patch)` | 定向更新一个节点 / 线路 / 通道并重渲染:单项字段(尺寸、图标、颜色、线宽、通道宽度 / 名称等)直接写补丁;只重排受影响的那一小片(节点 → 绑定线与锚定通道(尺寸 / 图标换过时,锚定通道的宽度一起按新尺寸重排,见「通道与多回线」),线路 → 这一条,通道 → 它的分组与成员),其余要素与缓存一个字节都不动;节点的 `visible: false` / 改回来会走一次整份重排(这一条要连绑定线、锚定通道与拾取索引一起退场 / 排回去,定向更新那条路落不下;同一个 `batch()` 里改一批只重排一次);`updateLine` 的补丁里还能写 `groupId` / `groupIndex`(可配 `channelId`)顺带把这条回线挪到目标分组,`updateChannel` 写 `groups` / `lineIds` 会顺带改分组并补一条 `channelGroups` 变更(见「通道分组」);`updateChannel` 还能给第三段 `linePatches`(一条或一批 `{ id \| index, ...字段 }`)把「通道声明 + 成员回线各自的字段 / 分组」一次下发 —— 声明先落、再逐条走 `updateLine`,整次调用只发一条 `batch` 数据变更、历史里只算一步(见「通道分组」与「逐方法示例」);第三段里认不出的成员 id 带得起定义(`from` / `to` 两端可解析、可用的 `path`、或通道自己声明了两端)会当场建一条回线并挂进通道,给不出定义才跳过点名;`updateLine` 的**拐点**写在 `path` 上(整条折线,含首尾端点 —— 只写中间拐点用 `setLineWaypoints(id, coordinates)`),通道里的回线几何由通道骨架推导(`path` 写了也会被覆盖),改它走 `setLineWaypoints` / `setChannelPath` |
|
|
141
142
|
|
|
143
|
+
| `setFeatureVisible(kind, refs, visible)` / `hideFeatures(kind, refs)` / `showFeatures(kind, refs)` | 统一隐藏 / 显示节点、线路或通道:只改数据里的 `visible`,不删要素,分组 / 拐点 / 编辑状态都保留。`kind` 收 `point` / `line` / `channel`,`refs` 收单个或一批 id / 下标 / `{ id }` / `{ index }`,也认 `'selection'`(通道按选中回线的归属取整条);`hideFeatures` / `showFeatures` 是 `false` / `true` 的快捷写法。整次调用收成一条历史与一条 `batch` 数据变更,返回 `{ ok, reason, kind, visible, indexes, ids, items, skipped }`;认不出的引用逐条进 `skipped`,不影响其余条目 |
|
|
142
144
|
| `movePoint(id, coordinate)` / `movePoint(id, x, y)` | 挪一个节点:`{ x, y }` / `[x, y]` / 两个数字都认;绑在它身上的线路端点与以它为锚点的通道骨架跟着重排(与拖动落地同一条路) |
|
|
143
145
|
| `setLineWaypoints(id, coordinates)` | 整份换一条线路的拐点:**只写中间拐点**(不含首尾两端;传单个坐标 = 只留一个拐点,空数组 = 拉直)。两端按节点 / `start` / `end` 补齐(绑了 `from` / `to` 时两端始终按节点坐标走),存回 `lines[].path` 的是「首尾端点 + 中间顶点」那一份;只插一个用 `addWaypoint(id, coordinate)`,删一个用 `removeWaypoint(id, pathIndex)`;通道里的回线改的是它所在分组的骨架(与 `setChannelPath` 同一份声明) |
|
|
144
146
|
| `getLineDecorations(id)` / `setLineDecorations(id, list)` / `addLineDecoration(id, decoration)` / `updateLineDecoration(id, index, patch)` / `removeLineDecoration(id, index)` | 线路上标记(图片 / SVG 装饰)的读写:`get` 给副本(要素没找到给空数组),`set` 整份换(空数组 = 全摘掉),`add` 追加在末尾并返回加进去的那一条,`update` / `remove` 按 `decorations` 的顺序数(返回改动的那一条,要素或下标找不到时给 `null` 并记一条 warning);四个写方法各只发一条数据变更、只落一步历史 |
|
|
@@ -151,6 +153,10 @@
|
|
|
151
153
|
| `getVisibleData()` | 过滤之后的数据副本(`points` / `lines`) |
|
|
152
154
|
| `getMovedPoints()` | 位置对账:相对上次对账(建视图 / 整份换数据 / 上一次 `clearMovedPoints`)位置变过的节点,按 id 去重、一趟扫出 `[{ index, id, x, y, item }]` —— 拖动 / `updatePoint` / `patchPoints` / 撤销都算,`removePoints` 删掉的记录顺手清掉,见「节点位置对账」 |
|
|
153
155
|
| `clearMovedPoints(refs?)` | 位置对账:把当前坐标记成新基准(发完接口告诉组件「这些已经同步过了」):不传 / `null` 全部对账,传一个或一批(写法与 `removePoints` 一致)只清这些,认不出来的跳过并记一条提醒;返回实例本身 |
|
|
156
|
+
| `getChangedChannels(options?)` | 通道变化对账:相对上次对账(建视图 / 整份换通道声明 / 上一次 `clearChangedChannels`)成员、分组、拐点、端点、可见性或其余声明字段变过的通道。每项带当前 `groups` 快照、六个 `*Changed` 标记、`fields` 与 `reasons`;新加通道只补账不报变化,删掉的通道(含 `removeLines` 连带删除)从账本清掉。`options.reasons` 只取指定原因(空数组直接返回空表),`options.ignoreGroups` 忽略分组新增 / 删除 / 重排、仍报已有分组的拐点变化。节点拖动只改坐标、不改 `from` / `to`,因此不算端点变化;拖 / 加 / 删分组拐点会进 `waypoints`,见「通道变化对账」 |
|
|
157
|
+
| `clearChangedChannels(refs?)` | 通道变化对账:把当前声明记成新基准(发完接口告诉组件「这些已经同步过了」):不传 / `null` 全部对账,传一个或一批(写法与 `removePoints` 一致)只清这些,认不出来的跳过并记一条提醒;返回实例本身 |
|
|
158
|
+
| `getChangedLines(options?)` | 线路变化对账:相对上次对账(建视图 / 整份换线路 / 上一次 `clearChangedLines`)`path` / `start` / `end`、`from` / `to`、可见性或其余声明字段变过的线路。每项带 `{ index, id, item, path, start, end, from, to, visible, pathChanged, endpointsChanged, visibilityChanged, fieldsChanged, fields, reasons }`;`options.reasons` 可写 `path` / `endpoints` / `visibility` / `fields`。新加线路只补账不报,删除线路(含 `removeChannel` 连带删除)同步清账;通道骨架变化不算线路变化,见「线路变化对账」 |
|
|
159
|
+
| `clearChangedLines(refs?)` | 线路变化对账:不传 / `null` 全部对账,传一个或一批(写法与 `removePoints` 一致)只清这些,空数组无操作;认不出来的跳过并记一条提醒,返回实例本身 |
|
|
154
160
|
| `isPointVisible(index)` | 某个下标的节点当前在不在图上:被筛选 / `visible: false` 隐藏掉的不算,初始化发散渲染还没排到的那部分也不算 |
|
|
155
161
|
| `resize()` / `refresh(options?)` | 手动同步尺寸 / 重新应用配置并重绘(`refresh({ reflow: false })` 只重画一帧,不重解析数据与配置);容器尺寸或祖先的 `transform: scale` 变过之后要调一次 `resize()` 同步(CSS `zoom` 会自己感知) |
|
|
156
162
|
| `destroy()` | 销毁引擎并移除根节点 |
|
|
@@ -182,7 +188,7 @@
|
|
|
182
188
|
|
|
183
189
|
返回 `this` 的方法都可以链式调用;返回其它值的方法(`get*` / `list*` / `export*` / `search` / `pick*` / `add*` / `remove*` / `copy*` / `merge*` / `locate*` 这些)在下面的「参数速查」里都标了返回类型。
|
|
184
190
|
|
|
185
|
-
**怎么确认「组件到底有哪些属性和方法」**:上面那张表按功能分组介绍,下面的「参数速查」把实例的**每个成员逐个列全**(当前
|
|
191
|
+
**怎么确认「组件到底有哪些属性和方法」**:上面那张表按功能分组介绍,下面的「参数速查」把实例的**每个成员逐个列全**(当前 249 个:246 个方法 + `el` / `canvas` / `gl` 三个属性),`options` 表列全 91 个键,数据字段表列全 242 个字段。这三份清单不靠手抄 —— 它们直接对着源码与类型对账:
|
|
186
192
|
|
|
187
193
|
- `node docs/steps/gen-api-index.mjs`:从 `create.js` 的实例定义、本文档的两张表与 `types.d.ts` 生成 `docs/demos/api-index.js`(`API_SUMMARY` / `API_SECTIONS` / `API_OPTIONS`),demo 的「实例 API」面板读的就是它;带 `--check` 时只校验有没有过期。
|
|
188
194
|
- `node docs/steps/check-doc-coverage.mjs`:10 项双向对账 —— 实例成员 × 文档 / × 参数速查 / × 类型、参数速查与实例方法主表各自反查过期条目、类型反查过期声明、数据字段 × 文档、options 键 × 文档 / options 表反查过期字段、demo 清单是否最新。加一个实例成员、改一个 options 键而没同步文档时,这个脚本会直接报出来。
|
|
@@ -111,6 +111,13 @@
|
|
|
111
111
|
| `getVisibleData()` | 无 | `GisViewData` |
|
|
112
112
|
| `getMovedPoints()` | 无 | `GisViewMovedPoint[]`:相对上次对账位置变过的节点(`index` / `id` / `x` / `y` / `item`),按 id 去重;被删掉的记录不在里面(顺手清掉) |
|
|
113
113
|
| `clearMovedPoints(refs?)` | `refs?: GisViewFeatureRef \| GisViewFeatureRef[]` 记成新基准的节点(id / 下标 / `{ id }` / `{ index }`,写法与 `removePoints` 一致);不传 / `null` 全部对账,空数组无操作 | `this` |
|
|
114
|
+
| `getChangedChannels(options?)` | `options?: GisViewChangedChannelOptions`:`reasons?: GisViewChangedChannelReason[]` 只返回这些原因(`[]` = 空表);`ignoreGroups?: boolean` 忽略分组新增 / 删除 / 重排,只比较共有分组的拐点 | `GisViewChangedChannel[]`:相对上次通道对账变了哪些通道,每项给 `{ index, id, item, groups, groupsChanged, waypointsChanged, membersChanged, endpointsChanged, visibilityChanged, fieldsChanged, fields, reasons }`;筛选后 `*Changed` 标记与 `fields` 同步收窄,读本身不改基准 |
|
|
115
|
+
| `clearChangedChannels(refs?)` | `refs?: GisViewFeatureRef \| GisViewFeatureRef[]` 记成新基准的通道(id / 下标 / `{ id }` / `{ index }`,写法与 `removePoints` 一致);不传 / `null` 全部对账,空数组无操作 | `this` |
|
|
116
|
+
| `getChangedLines(options?)` | `options?: GisViewChangedLineOptions`:`reasons?: GisViewChangedLineReason[]` 只返回 `path` / `endpoints` / `visibility` / `fields` 中的指定原因(`[]` = 空表) | `GisViewChangedLine[]`:相对上次线路对账变了哪些线路,每项给 `{ index, id, item, path, start, end, from, to, visible, pathChanged, endpointsChanged, visibilityChanged, fieldsChanged, fields, reasons }`;新加只补账、删除清账,读本身不改基准 |
|
|
117
|
+
| `clearChangedLines(refs?)` | `refs?: GisViewFeatureRef \| GisViewFeatureRef[]` 记成新基准的线路(写法与 `removePoints` 一致);不传 / `null` 全部对账,空数组无操作 | `this` |
|
|
118
|
+
| `setFeatureVisible(kind, refs, visible)` | `kind: GisViewVisibilityKind`(`'point'` / `'line'` / `'channel'`);`refs: GisViewVisibilityRefs` 单个 / 一批 id 或下标,也认 `'selection'`(通道按选中回线的归属取整条);`visible: boolean` | `GisViewSetVisibilityOutcome`:`{ ok, reason, kind, visible, indexes, ids, items, skipped }`;只改数据的 `visible`,整次调用一条历史、一条 `batch` 数据变更 |
|
|
119
|
+
| `hideFeatures(kind, refs)` | 同上,等价于 `setFeatureVisible(kind, refs, false)` | 同上 |
|
|
120
|
+
| `showFeatures(kind, refs)` | 同上,等价于 `setFeatureVisible(kind, refs, true)`(隐藏后原样恢复) | 同上 |
|
|
114
121
|
| `isPointVisible(index)` | `index: number` 节点下标 | `boolean`。在不在图上(被筛选 / `visible: false` 或发散渲染还没铺到的都算 `false`) |
|
|
115
122
|
| `getElementBounds(keys)` | `keys: GisViewFeatureKey[]` 要素定位数组(`{ type, id }` / `{ type, index }` / 裸 id) | `GisViewBounds \| null` |
|
|
116
123
|
| `getViewState()` | 无 | `GisViewViewState` |
|
|
@@ -140,6 +147,7 @@
|
|
|
140
147
|
| `getPointClipboard()` | 无 | `GisViewClipboardItem \| null` |
|
|
141
148
|
| `mergePoints(source, target, options = {})` | `source: GisViewPointRef` 被并入的节点(并完就没了)、`target: GisViewPointRef` 接收的节点、`options?: { originalEvent?: Event \| null; item?: GisViewPoint }`:`item` 可以给一份已经改好的合并结果(确认阶段改过 `payload.item` 之后就是它) | `GisViewMergeOutcome` |
|
|
142
149
|
| `mergeSelectedPoints(options = {})` | `options: GisViewMergeSelectedOptions`:`target` 指定并到哪一个(不给就自动挑绑定线路最多的);`sources` 直接给来源(不依赖选中集);`item` 是最终节点补丁或 `(context) => 补丁`(上下文含 `item` / `merged` / `source` / `target` / `sourceIndex` / `targetIndex` / `merge` / `view`;坐标与 id 仍按 `target` 保留);`force: true` 越过 `mergeable: false`;`label` / `originalEvent` | `GisViewMergeSelectedOutcome` |
|
|
150
|
+
| `splitPoint(source, parts, options = {})` | `source: GisViewPointRef` 要拆的节点;`parts: GisViewSplitPointPart[]` 至少两份,每份可直接写节点字段或放进 `item`,并用 `lines` / `channels` / `channelFrom` / `channelTo` 分配线路与通道端点;`options?: GisViewSplitPointOptions`:`keepSourceId`(缺省 `true`,第一份沿用来源 id)/ `label` | `GisViewSplitPointOutcome`:`{ ok, reason, source, items, indexes, pointIds, linesChanged, removedLines, removedLineIds, removedLineIndexes, channelsChanged, removedChannels, removedChannelIds, skipped, changes }`;`changes` 可直接作为后端事务输入 |
|
|
143
151
|
| `getPointMerge()` | 无(载荷带 `impact`:`lineCount` / `linesChanged` / `removedLines` / `lineIds` / `removedLineIds` 与通道读数,确认前就能估算影响) | `GisViewPointMergePayload \| null` |
|
|
144
152
|
| `resolvePointMerge(confirmed, options = {})` | `confirmed?: boolean`(缺省 `true` = 确认,`false` = 取消);`options?: GisViewPointMergeConfirmOptions`:`keep` 选 `'target'`(缺省,保留碰撞目标)/ `'source'`(保留拖动的来源),`item` 是最终节点字段补丁(名称 / 分区 / 颜色 / 尺寸 / 图标 / 描边等;位置与 id 始终取保留端) | `GisViewPointMergePayload \| null` |
|
|
145
153
|
| `commitPointMerge()` | 无 | `boolean` |
|