gis-view-webgl2 0.1.1 → 0.1.3
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 +23 -7
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +563 -61
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +182 -0
- package/docs/GisView.md +2 -2
- 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 +165 -0
- package/docs/api/index.html +1 -1
- package/docs/demos/ApiCoverageDemo.vue +644 -0
- package/docs/demos/ChannelDemo.vue +580 -18
- package/docs/demos/ClusterDemo.vue +251 -0
- package/docs/demos/CopyMergeDemo.vue +60 -10
- package/docs/demos/EventDemo.vue +3 -3
- package/docs/demos/GisViewDemo.vue +64 -8809
- package/docs/demos/LabelDemo.vue +67 -11
- package/docs/demos/api-index.js +58 -41
- package/docs/demos/demos.js +33 -16
- package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +28 -0
- package/docs/demos/gis-view-demo/GisViewDemoDialogs.vue +82 -0
- package/docs/demos/gis-view-demo/GisViewDemoFooter.vue +16 -0
- package/docs/demos/gis-view-demo/GisViewDemoHeading.vue +16 -0
- package/docs/demos/gis-view-demo/GisViewDemoPanelStack.vue +71 -0
- package/docs/demos/gis-view-demo/GisViewDemoStage.vue +219 -0
- package/docs/demos/gis-view-demo/demo.css +1230 -0
- package/docs/demos/gis-view-demo/demoContext.js +12 -0
- package/docs/demos/gis-view-demo/panels/DataPanels.vue +303 -0
- package/docs/demos/gis-view-demo/panels/OpsPanels.vue +174 -0
- package/docs/demos/gis-view-demo/panels/StylePanels.vue +561 -0
- package/docs/demos/gis-view-demo/panels/ViewPanels.vue +396 -0
- package/docs/demos/gis-view-demo/useGisViewDemo.js +6188 -0
- 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/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +3 -2
- package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +1 -1
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +10 -4
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +105 -16
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +8 -8
- 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 +78 -3
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +31 -5
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +64 -10
- 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 +65 -8
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +54 -1
- package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +3 -0
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +35 -19
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +5 -0
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +18 -6
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +47 -22
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +80 -13
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +27 -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 +5 -5
- package/docs/steps/README.md +18 -10
- package/docs/steps/selftest-actions.html +3 -5
- package/docs/steps/selftest-demo.html +27 -18
- package/docs/steps/selftest-demos.html +578 -53
- package/docs/steps/selftest-docs.html +7 -6
- package/docs/steps/selftest-effects.html +200 -0
- package/docs/steps/selftest-features.html +725 -12
- package/docs/steps/selftest-labels.html +323 -4
- package/docs/steps/selftest-perf.html +293 -20
- package/docs/steps/selftest-scale.html +52 -29
- package/docs/steps/step-10-full.js +1 -1
- package/docs/steps/step-12-events.js +1 -1
- 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` 只落地不通知),开着历史时记一步(可撤销)。
|
|
@@ -30,6 +30,39 @@ createGisView(container, {
|
|
|
30
30
|
|
|
31
31
|
`clickable: false` 的要素既不会悬停高亮也不会被点中;`options.clickable: false` 则整体关闭拾取。
|
|
32
32
|
|
|
33
|
+
## 点击级联高亮(cascadeHighlight)
|
|
34
|
+
|
|
35
|
+
级联高亮是**另一份状态**,不走选中集:点击节点时算出它自己和直接相连的线路 / 另一端节点;点击线路或通道回线时只算这条线的两端节点。它默认关,`cascadeHighlight: true` 等价于 `{ scope: 'both', triggerOnClick: true, triggerOnMultiSelect: true, triggerOnBoxSelect: false }`;`scope: 'points'` 只亮节点,`both` 连直连线路一起亮。
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
const view = createGisView(container, {
|
|
39
|
+
points, lines, channels,
|
|
40
|
+
clickable: true,
|
|
41
|
+
cascadeHighlight: {
|
|
42
|
+
scope: 'both',
|
|
43
|
+
triggerOnClick: true,
|
|
44
|
+
triggerOnMultiSelect: true,
|
|
45
|
+
triggerOnBoxSelect: false,
|
|
46
|
+
pointStyle: { color: '#fff7ed', strokeColor: '#f59e0b', strokeWidth: 2, sizeScale: 1.2 },
|
|
47
|
+
lineStyle: { color: '#f59e0b', width: 2.6, opacity: 0.95 },
|
|
48
|
+
clearOnEmptyClick: true
|
|
49
|
+
},
|
|
50
|
+
onCascadeHighlight: payload => console.log(payload?.origin, payload?.pointIndexes, payload?.lineIndexes)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
view.getCascadeHighlight() // { origin, scope, points, lines, pointIndexes, lineIndexes } 或 null
|
|
54
|
+
view.updateOptions({ cascadeHighlight: { scope: 'points' } })
|
|
55
|
+
view.updateOptions({ cascadeHighlight: false }) // 关掉并收掉当前高亮
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- `pointStyle` / `lineStyle` 是级联专用的两套状态样式,缺省为琥珀色描边 / 线条,不需要先配 `pointSelectStyle` / `lineSelectStyle`。它们只作用于**被级联的邻接项**:直接选中的节点 / 线路仍走 `pointSelectStyle` / `lineSelectStyle`(多选 / 框选各走自己的那一档);没配选中样式时保持普通外观,也不会回落到级联样式;只有 `defaultHighlight: true` 时才补上内置选中强调。
|
|
59
|
+
- 触发方式三个开关彼此独立:`triggerOnClick` 管普通单击(缺省 `true`),`triggerOnMultiSelect` 管 Ctrl / ⌘ 累加点击与 `multiSelect`(缺省 `true`),`triggerOnBoxSelect` 管框选 / `selectInBox()`(缺省 `false`)。某一路关掉时,该操作只改变选中集,并把旧级联收掉;另一路仍可正常触发。
|
|
60
|
+
- 框选触发时,框内每个命中项都参与邻接计算,取并集后一次派发;`origin` 取框内按数据顺序的第一个命中项作代表。框内直接命中的项走 `boxSelectStyle` / `lineSelectStyle`,框外的邻接节点 / 线路才套级联样式。
|
|
61
|
+
- 点空白处是否收掉由 `clearOnEmptyClick` 控制,缺省 `true`。收掉、首次点亮、换节点 / 线路 / 通道或改范围都会发 `onCascadeHighlight` 与总线事件 `cascadeHighlight`;读数与回调收到的是同一份对象,收掉时是 `null`。
|
|
62
|
+
- `origin.target` 为 `point` / `line` / `channel`:通道回线在底层仍是线路,但 `origin` 会另带 `channelId` / `channelIndex` / `groupId` / `groupIndex` / `channel`,方便直接回显“点的是哪条通道的哪一组”。
|
|
63
|
+
- 数据整份更新后会按新的邻接关系刷新;来源被删除、被隐藏到不可拾取,或 `cascadeHighlight` 关掉时,高亮自动收掉。它不写 `selectionKeys`,`getSelectionData()` / `getSelections()` 不会因为级联高亮多出条目。
|
|
64
|
+
- 综合 Demo(`?demo=full`)的「视图 · 交互 → 交互开关与快捷键」面板已接入这项能力:可现场开关、在「节点 + 直连线路 / 只亮节点」之间切换,并直接读 `getCascadeHighlight()` 的来源、范围与节点 / 线路条数。
|
|
65
|
+
|
|
33
66
|
## 指针事件
|
|
34
67
|
|
|
35
68
|
鼠标的每一种动作都有回调,载荷里都能拿到「当前是哪个元素」:`onHover`(悬停)、`onClick`(单击左键)、`onDblClick`(双击)、`onPointerDown` / `onPointerUp`(按下与抬起,含左 / 中 / 右键)、`onContextMenu`(右键)。悬停是 `GisViewHoverPayload`、点击与双击是 `GisViewClickPayload` —— 都是在 `GisViewPickResult`(点 / 线 / 通道回线的 `type`、`index`、`item`、`screen`、`coordinate` 与通道字段)上再补光标位置与一份 `action`;按下 / 抬起 / 右键统一发 `GisViewPointerPayload`(同样是拾取结果 + `action`)。
|
|
@@ -95,13 +128,13 @@ createGisView(container, {
|
|
|
95
128
|
})
|
|
96
129
|
```
|
|
97
130
|
|
|
98
|
-
- 载荷形状一致,取信息只需认三处:`type`(`'point'` / `'line'` / `null`)、`item`(要素原始数据,可读业务字段)、`world` / `coordinate`(前者是光标处的世界坐标,后者是要素自身的坐标)。命中通道回线时,`channelIndex` / `channelId` / `groupId` / `groupIndex` / `channel`
|
|
131
|
+
- 载荷形状一致,取信息只需认三处:`type`(`'point'` / `'line'` / `null`)、`item`(要素原始数据,可读业务字段)、`world` / `coordinate`(前者是光标处的世界坐标,后者是要素自身的坐标)。命中通道回线时,`channelIndex` / `channelId` / `groupId` / `groupIndex` / `channel` 一并给出;右键载荷还会在 `channelContext` 上带整份分组上下文。
|
|
99
132
|
- 配了 GeoJSON 背景时,以上载荷都多一个 `lngLat`:按光标的世界坐标(要素命中用 `coordinate`,悬停用最近一次光标位置)反算出来的真实经纬度,点 / 线 / 通道都一样;想自己换算就用 `worldToLngLat` / `screenToLngLat`,光标级别的屏幕坐标 + 世界坐标 + 经纬度看 `onPointer` 与 `getPointer()`。
|
|
100
133
|
- `onPointerDown` / `onPointerUp` 不改变组件行为:选中、拖拽、框选、拐点编辑照旧。左键的一次「点」(没有拖动)仍然走 `onClick` / `onDblClick`,两者是并列的,不会互相吞掉 —— 载荷里的 `action.type` 各说各话:按下是**意图**、松开是**刚做完的那件事**(`action.moved` 说明是拖还是点)、点击是**真正发生的那一件**。
|
|
101
134
|
- 点空白处照发:`onPointerDown` / `onPointerUp` 的 `action.target` 是 `empty`(开了平移时按下是 `pan` 的意图),`onClick` / `onDblClick` 的载荷是 `null`(选中在那一刻已经清空)——「点了空白」这件事也可以看 `onSelectChange` 的 `action.type: 'clearSelection'`。
|
|
102
135
|
- **中键按住就是临时平移**(`pan` 开关开着时):`onPointerDown` 的 `action.type` 是 `pan`、`action.button` 是 `'middle'`,按住拖动平移画布、松开结束(`onPointerUp` 的 `action.type` 也是 `pan`);这一路**不经过** `onClick`,中键也不会改选中 —— 想自己接中键(中键菜单 / 快捷操作)就按 `action.button === 'middle'` 认它。长按(触摸 / 触控笔)与中键一样有明确的读数:看松开 / 点击载荷的 `action.longPress` 与 `action.press` / `action.duration`。
|
|
103
|
-
- `onContextMenu`
|
|
104
|
-
- 命中的层级是**点 → 线 →
|
|
136
|
+
- `onContextMenu` 每次都只发这一条;命中通道回线时,分组上下文挂在同一份载荷的 `channelContext` 上(见「通道右键交互框」),不再额外发第二个回调。**右键缺省整个不接**(`contextMenu` 开关没开时系统菜单照弹、回调也不发);接住之后一律 `preventDefault()` 收掉系统菜单(点 / 线 / 空白 / 通道都一样),回调照常发。想自己弹菜单就写 `interactive: true` / `contextMenu: true` / `features: 'view'`,想让系统菜单自己弹出来就写 `preventContextMenu: false`。
|
|
137
|
+
- 命中的层级是**点 → 线 → 通道**:右键落在节点上时载荷是 `type: 'point'`,没有 `channelContext` —— 节点压在通道回线上(端点最常见)不会被当成通道右键;点空白处同样没有 `channelContext`,可以直接用它收框。
|
|
105
138
|
- 悬停是高频率回调:大数据量下建议只做节流的读数或轻量样式切换(demo 里的读数就是这么做的)。
|
|
106
139
|
|
|
107
140
|
- 命中的要素**在不在选中集里**、以及整份选中集有多少,载荷里直接给:`selected`(这一刻在不在选中集里)、`wasSelected`(`onClick` 给的是「点之前」的那一份,双击同理;右键与按下 / 松开都是「当下」,此时与 `selected` 相同)、`selection`(整份选中集的读数 `{ ids, count, points, lines, channels }`,通道回线按通道算一个)。左键 / 右键要自己接一套操作(按命中分支、弹自己的面板、决定要不要保留多选)时,看这三项就不用再去比对 `getSelectionData().ids`:
|
|
@@ -558,6 +591,8 @@ onPointMerge: async payload => {
|
|
|
558
591
|
|
|
559
592
|
接口调用不做确认,直接按规则合并:`view.mergePoints('a', 'b')`(来源、目标收 id、下标或 `{ id }` / `{ index }`)。合并的数据规则是:
|
|
560
593
|
|
|
594
|
+
这里的「合并样式」不是另一项样式配置,也不是新的 API;它指的是**合并后保留节点最终写进数据的 `size` 与 `symbol`**。Demo 的读数用它回答“合完会长什么样、用哪个图标”:尺寸取双方较大值、图标优先目标节点。它决定合并结果的外观,需要自定义时改 `merge.createItem(...)` 的返回值或 `payload.item` 即可。
|
|
595
|
+
|
|
561
596
|
- 节点数据缺省按「被并入的一端在前、保留端在后」合并(保留端优先,另一端独有的字段带过来),坐标与 id 取保留端;拖动合并确认里缺省保留碰撞目标,`keep: 'source'` 时保留拖动的来源节点;
|
|
562
597
|
- **图标大小取双方的最大值**:高电压往低电压合,原来的图标跟着变大(结果写成节点自己的 `size`,`sizeScale` 归 1);**图标类型优先目标节点**,目标没配符号才用来源的;
|
|
563
598
|
- 想自己决定就用 `merge.createItem(source, target, context)`,`context` 里带双方的 `sourceStyle` / `targetStyle`(`{ index, size, symbol, zIndex }`,样式链定下来的那一份,不含状态样式与热力倍率);
|
|
@@ -593,6 +628,44 @@ view.resolvePointMerge(false) // 取消:拖动的节点回原位,数据不
|
|
|
593
628
|
|
|
594
629
|
删节点用 `view.removePoints('a')` / `view.removePoints({ index: 2 })` / 数组:绑在它上面的线路一并删掉,声明了它当端点的整条通道(含成员回线)跟着删。
|
|
595
630
|
|
|
631
|
+
## 节点拆分(splitPoint)
|
|
632
|
+
|
|
633
|
+
`splitPoint` 是 `mergePoints` 的逆操作:把一个节点按业务需要拆成两份或更多,原来连在它身上的线路与通道两端按分配重新挂到新节点上。它不弹对话框,直接按规则落地;整次调用只记一条历史、只发一条 `splitPoint` 数据变更。
|
|
634
|
+
|
|
635
|
+
```js
|
|
636
|
+
const split = view.splitPoint('hub', [
|
|
637
|
+
{ name: '枢纽(主)' }, // 第一份:沿用原下标与 id
|
|
638
|
+
{ x: 116.42, y: 39.91, name: '枢纽(东)', lines: ['line-east'] },
|
|
639
|
+
{ x: 116.38, y: 39.89, name: '枢纽(西)', lines: ['line-west'], channelTo: ['corridor-1'] }
|
|
640
|
+
], { label: '拆分枢纽' })
|
|
641
|
+
|
|
642
|
+
if (split.ok) {
|
|
643
|
+
console.log(split.pointIds, split.linesChanged, split.channelsChanged)
|
|
644
|
+
api.post('/gis/points/split', split.changes) // 拆分后的节点、受影响的线路与通道端点,以及删除项
|
|
645
|
+
}
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
每份节点定义可以直接写节点字段(`id` / `x` / `y` / `name` / 样式 / 业务字段),也可以放进 `item`;另外三项是从节点字段里摘出来的分配指令,不会写进节点数据:
|
|
649
|
+
|
|
650
|
+
| 字段 | 作用 |
|
|
651
|
+
| --- | --- |
|
|
652
|
+
| `lines` | 分到这一份的线路(id / 下标 / `{ id }` / `{ index }`,单个或一批)。只能分配真的连在来源节点上的线路(`from` / `to` 等于来源 id,或 `start` / `end` / `path` 顶点与来源坐标重合) |
|
|
653
|
+
| `channels` | 两端都分到这一份的受影响通道;把同一条通道写进两份时,按出现顺序当作 `from` 端、`to` 端各去一份 |
|
|
654
|
+
| `channelFrom` / `channelTo` | 只把通道的起点 / 终点分到这一份;同一条通道的同一端不能分配给两份 |
|
|
655
|
+
|
|
656
|
+
落地规则:
|
|
657
|
+
|
|
658
|
+
- **第一份接在原下标上**,缺省沿用来源节点的 id;其余几份追加到 `points` 末尾。`options.keepSourceId: false` 时第一份也生成 `${来源 id}-split-1`。没写 id 的其余份依次生成 `${来源 id}-split-2` / `-3`…,撞上现有 id 时自动加序号;显式写了重复 id 会整次失败。
|
|
659
|
+
- **节点字段从来源继承,再按这一份的字段覆盖**;坐标缺省继承来源。至少给两份,坐标必须是有效数字,否则 `ok: false` 且 `reason` 说明原因。
|
|
660
|
+
- **线路默认跟第一份**:没写进任何一份 `lines`、但连在来源节点上的线路,全部改挂到第一份;同一条线路显式分给两份会整次失败。认不出 / 没连在来源上的引用进 `skipped.lines`,不影响其余条目。
|
|
661
|
+
- **通道端点默认跟第一条被分配过的成员回线**:例如通道里唯一一条回线分给了第二份,通道的 `from` 会自动改到第二份节点,而不是留在第一份;成员都没显式分配时才回落到第一份。显式写了 `channels` / `channelFrom` / `channelTo` 的按显式分配来。
|
|
662
|
+
- **几何一起改**:线路的 `from` / `to` 改到对应新节点;没有 id 绑定、但 `start` / `end` / `path` 顶点与来源坐标重合的端点 / 顶点也改到新坐标。通道端点按新声明重新锚定,成员回线由通道骨架推导,不用逐条改。
|
|
663
|
+
- **拆完清理退化数据**:两端落到同一份、变自环或零长度的线路删掉;两端因此相同的通道删掉;成员回线全被删空的通道也删掉。删除项分别进 `removedLineIds` / `removedLineIndexes` 与 `removedChannelIds`。
|
|
664
|
+
- **返回的 `channelsChanged` 只数端点被改写的通道**;被删掉的通道单列在 `removedChannels` / `removedChannelIds`。`changes` 给 `{ removedPoint, points, lines, removedLines, channels, removedChannels }`:`points` 是拆分后的全部节点,`lines` / `channels` 是仍存在且端点被改写的线路 / 通道,删除项单列;可直接作为后端事务输入。`skipped` 说明哪些线路 / 通道引用没认出来。
|
|
665
|
+
- **撤销**:不用逐条回滚,`view.undo()` 一步回到拆分前;重做一步恢复拆分结果。
|
|
666
|
+
|
|
667
|
+
`CopyMergeDemo.vue` 的「拆分节点」一节可以直接点按钮看效果:选中一个节点,按两份拆开,读数会列出新节点 id、改绑的线路与通道端点,以及这一步的变更集。
|
|
668
|
+
|
|
596
669
|
## 删除(remove)
|
|
597
670
|
|
|
598
671
|
删除走的是「**先看清楚要删什么,确认之后才动数据**」这条路:Delete 键(`shortcuts.remove`,默认 `['delete', 'backspace']`)与 `requestRemove()` 都先发一份载荷(`onRemove` 回调 / `'remove'` 事件,`phase: 'pending'`),载荷里把这次要删的拐点 / 线路 / 节点、各自的连带影响与可选的删除范围列全;使用方拿着它弹自己的确认框,确认才删、取消数据一个字节都不动。
|
|
@@ -651,4 +724,6 @@ const view = createGisView('#map', {
|
|
|
651
724
|
|
|
652
725
|
---
|
|
653
726
|
|
|
727
|
+
**相关章节**:[03 配置项(options)](03-配置项.md) · [08 选中与拐点](08-选中与拐点.md) · [14 视图与筛选](14-视图与筛选.md) · [17 参数速查](17-参数速查.md)
|
|
728
|
+
|
|
654
729
|
[← 06 数据与场景更新](06-数据与场景更新.md) · [手册目录](../GisView.md) · [08 选中与拐点 →](08-选中与拐点.md)
|
|
@@ -74,6 +74,8 @@ view.selectFeatures(['station-1', { type: 'point', index: 5 }])
|
|
|
74
74
|
// ② 通道:整条通道的回线 / 通道里的某一组 / 通道里的某几条回线
|
|
75
75
|
view.selectFeatures({ type: 'channel', channelId: 'channel-a' })
|
|
76
76
|
view.selectFeatures({ channelId: 'channel-a', groupId: 'g-2' })
|
|
77
|
+
view.selectFeatures({ type: 'group', channelId: 'channel-a', groupId: 'g-2' })
|
|
78
|
+
view.selectFeatures({ type: 'group', groupId: 'g-2' }) // groupId 全局唯一时可省略通道
|
|
77
79
|
view.selectFeatures({ channelId: 'channel-a', lineIds: ['channel-channel-a-0', 'channel-channel-a-1'] })
|
|
78
80
|
view.selectFeatures({ channelIndex: 0, lineIndexes: [3, 4] })
|
|
79
81
|
|
|
@@ -90,6 +92,15 @@ view.selectFeatures({ type: 'channel', channelId: 'channel-a' }, { mode: 'remove
|
|
|
90
92
|
|
|
91
93
|
拾取载荷(`pickAt` 或指针事件里的那一份)与搜索命中里可能带着 `channelId` / `channelIndex` / `groupId` —— 那是「这条回线属于哪条通道」的上下文,不是「要选整条通道」:它们的 `type` / `kind` 写着 `line` / `point`,落地选中的就是那一条要素自己(与在画布上点它一个结果)。要整条通道,显式写 `{ type: 'channel', channelId: 'channel-a' }`。
|
|
92
94
|
|
|
95
|
+
聚焦同一套目标写法交给 `focusOn`,显式 `type` 还能解决节点与线路同 id 的歧义;整条通道可以写 `{ channelId }`,分组写 `{ type: 'group', channelId?, groupId }`(`groupId` 全局唯一时可省略 `channelId`)。节点、线路、通道与分组的取范围与缩放都走 `zoomToBounds`,不会因为类型不同换一套放大倍率:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
view.focusOn({ type: 'point', id: 'shared-id' })
|
|
99
|
+
view.focusOn({ type: 'line', id: 'shared-id' })
|
|
100
|
+
view.focusOn({ type: 'channel', channelId: 'channel-a' })
|
|
101
|
+
view.focusOn({ type: 'group', channelId: 'channel-a', groupId: 'g-2' })
|
|
102
|
+
```
|
|
103
|
+
|
|
93
104
|
读回选中用 `getSelectionData()`,一次拿全(单击、多选、框选、接口选中都是这一份;只要下标与要素引用时用 `getSelections()`):
|
|
94
105
|
|
|
95
106
|
```js
|
|
@@ -126,12 +137,25 @@ view.selectFeatures('station-7', { mode: 'remove' }) // 与 deselect('sta
|
|
|
126
137
|
|
|
127
138
|
- `deselect(refs)` 的写法与 `selectFeatures` **完全一样**(节点 / 通道 / 线路 / 混合一批,拾取载荷与搜索命中也能直接喂进来),只做「去掉这几条」:其余选中原样留着,认不出来的条目跳过(与 `patchPoints` 的 `skipped` 一个态度)。不传(或传 `null`)等于取消全部(`selectFeatures(null)` 也是这个意思);传空数组是「没有目标」,什么都不做。返回实例本身,可以接着链式调。
|
|
128
139
|
- 「加 / 减 / 取反」三种并入选中集的方式还是 `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()
|
|
140
|
+
- **画布上的手动取消**:单击空白处与 Esc 都是清空全部(Esc 先逐层退出复制预览 → 待确认合并 → 通道草稿,最后一层才是清空);**Ctrl / ⌘ + 单击**已经选中的要素是取反取消(节点一直是单个取反;回线在 `channelSelectMode` 为 `'group'` / `'channel'` 时整组 / 整条通道一起取反,`'line'` 模式下 Ctrl + 单击留给加点与换选中);通道右键交互框里的「取消该分组选中」就是 `deselect({ channelId, groupId })`。要「取消全部」时这三种写法等价:`selectFeatures(null)` / `selectFeatures([])` / `deselect()`。按住加点快捷键点击通道回线时,新增拐点后的选中与高亮也按 `channelSelectMode` 摊成一条 / 一组 / 整条通道。
|
|
130
141
|
- 想先确认再取消:`isSelected(ref)` 问单个、`getSelections()` 取原始条目、`getSelectionData()` 一次取全(`count` / `ids` / `bounds`)。
|
|
131
142
|
|
|
132
143
|
## 线路拐点
|
|
133
144
|
|
|
134
|
-
写 `waypointEditable: true`(缺省关)后,线路的中间顶点会画出可交互的拐点:按住 Ctrl 点击线路在点击位置插入拐点,单击选中,拖动即移动顶点、线路随之变成折线,双击或选中后按 Delete 删除。节点很密时 Ctrl 点击会先比较点到节点与点到线路的屏幕距离:**线路更近就按加点处理**,比节点远才走 Ctrl
|
|
145
|
+
写 `waypointEditable: true`(缺省关)后,线路的中间顶点会画出可交互的拐点:按住 Ctrl 点击线路在点击位置插入拐点,单击选中,拖动即移动顶点、线路随之变成折线,双击或选中后按 Delete 删除。节点很密时 Ctrl 点击会先比较点到节点与点到线路的屏幕距离:**线路更近就按加点处理**,比节点远才走 Ctrl 多选节点,所以贴着节点云也能给线路补拐点。通道回线加点后的高亮范围与普通点选共用 `channelSelectMode`:`'line'` 只高亮点中的一条、`'group'` 连这一组、`'channel'` 整条通道。
|
|
146
|
+
|
|
147
|
+
### 代码选中拐点(selectWaypoint)
|
|
148
|
+
|
|
149
|
+
`selectWaypoint()` 走与点选同一份拐点状态:字符串 / 数字写法是「线路 id 或下标 + `pathIndex`」,对象写法是 `{ lineIndex, pathIndex }` 或 `{ lineId, pathIndex }`。`pathIndex` 用 `getWaypoints()` 返回的下标(首尾端点不算拐点)。选中成功时会把拐点所在线路一起选上,因此缺省 `waypointVisibility: 'selected'` 下拐点立即显示;`options.mode` 与 `selectFeatures` 一样决定线路怎么并进当前选中集(缺省换一份)。
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
view.selectWaypoint('line-a-b', 2)
|
|
153
|
+
view.selectWaypoint({ lineIndex: 0, pathIndex: 1 }, { mode: 'add' })
|
|
154
|
+
view.selectWaypoint({ lineId: 'line-a-b', pathIndex: 2 })
|
|
155
|
+
view.selectWaypoint(null) // 只取消拐点选中,不动要素选中集
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
找不到线路或下标不是可编辑拐点时不会凭空选中一个拐点;传 `null` 只清拐点高亮,原来的要素选中集保持不动。
|
|
135
159
|
|
|
136
160
|
需要完全停掉拐点编辑时写 `features: { waypoint: false }` 或 `waypointEditable: false`:Ctrl + 点击、`addWaypoint` / `addWaypoints` / `addChannelGroupWaypoint`、通道右键框「加拐点」、拖动已有拐点以及双击 / Delete 删除都会一起变成无操作,数据与历史都不动。拐点只有这一道总闸,没有 `waypointAdd` 之类的细分开关;`shortcuts.addWaypoint` 只决定加点用哪个组合键,不改变总闸。运行时也可以 `view.setFeatures({ waypoint: false })`,`getFeatures().waypoint` 读当前有效值。
|
|
137
161
|
|
|
@@ -155,11 +179,11 @@ const view = createGisView(container, {
|
|
|
155
179
|
拐点就是 `path` 的中间顶点:首尾两端始终是线的端点(`start` / `end` 或绑定的 `from` / `to`),所以顶点数不足 3 的线没有拐点;首次加点按离点击位置最近的线段插入进 `path`。`addWaypoint(lineId, [x, y])` 与 `removeWaypoint(lineId, pathIndex)` 可以在代码里增删,删到只剩两个顶点时写法还原为 `start` / `end`。拖动中的 `phase` 依次是 `start`、`move`、`end`,每次载荷里的 `line` 都是该线路的最新数据。
|
|
156
180
|
|
|
157
181
|
整份换一条线的拐点用 `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`
|
|
182
|
+
`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
183
|
|
|
160
184
|
拖动中间态(`start` / `move`)只更新线路数据与引擎读数,**不再逐帧写** `getOptions().channels` 里那份声明副本:`add` / `remove` / `end` 才是落地,声明与导出文档在那一刻跟上(一次拖动因此在历史里只留一条记录);拖动中要看实时骨架用 `getChannelGroups()` / `getChannelGroupWaypoints()`,它们读的本来就是实时几何。
|
|
161
185
|
|
|
162
|
-
通道内的回线按**分组**各记一份拐点:给某条回线加点 / 拖点 /
|
|
186
|
+
通道内的回线按**分组**各记一份拐点:给某条回线加点 / 拖点 / 删点只会改它所在分组的骨架,同一通道里其它分组的线路一根拐点都不动(见「通道分组」)。拖动分组的拐点还有两种模式(`channelWaypointDragMode`):缺省 `'parallel'` 改共用骨架、组内回线保持平行;`'vertex'` 只平移同组同一 `pathIndex` 的拐点,相邻顶点与其它分组不动,结果记在 `groups[].waypointOffsets`。
|
|
163
187
|
|
|
164
188
|
### 拐点样式(waypointStyle / waypointSelectStyle)
|
|
165
189
|
|
|
@@ -206,8 +230,10 @@ view.getParallelGroups()
|
|
|
206
230
|
// ]
|
|
207
231
|
```
|
|
208
232
|
|
|
209
|
-
一个通道的每个**分组**各成一组:`group_id` 取分组 id(没拆分过就是通道的 `id`,拆分出来的分组是 `通道 id::2` 这样的写法;没写 `id` 时用内容签名),`member_line_ids` 是这一分组里的回线,`group_route` 是它们共用的骨架(两端节点 +
|
|
233
|
+
一个通道的每个**分组**各成一组:`group_id` 取分组 id(没拆分过就是通道的 `id`,拆分出来的分组是 `通道 id::2` 这样的写法;没写 `id` 时用内容签名),`member_line_ids` 是这一分组里的回线,`group_route` 是它们共用的骨架(两端节点 + 每个拐点),同一分组的回线只记一份。分组写了 `waypointOffsets` 时,`group_route` 是已经叠上刚性偏移的实际路由。通道外的线路自己成组,组内只有它一条。集合只列**当前还有拐点**的组:删掉最后一个拐点后线路回到两端点,这条组也跟着退出去;换一批线路数据时通道外的组一起作废,通道的组跟着骨架一起保留。`onWaypointChange` 载荷里的 `group_id` 就是这个组号,拐点属于哪个集合一眼可见(分组见「通道分组」)。
|
|
210
234
|
|
|
211
235
|
---
|
|
212
236
|
|
|
237
|
+
**相关章节**:[07 指针与要素编辑](07-指针与要素编辑.md) · [09 通道与多回线](09-通道与多回线.md) · [16 实例方法总表](16-实例方法总表.md) · [17 参数速查](17-参数速查.md) · [18 逐方法示例](18-逐方法示例.md)
|
|
238
|
+
|
|
213
239
|
[← 07 指针与要素编辑](07-指针与要素编辑.md) · [手册目录](../GisView.md) · [09 通道与多回线 →](09-通道与多回线.md)
|
package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md
CHANGED
|
@@ -4,7 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
## 通道与多回线
|
|
6
6
|
|
|
7
|
-
一个通道表示一条走廊里并行敷设的多条回线(二 / 三 / 四 / 五回线都行)。回线本身照常写进 `lines`,通道只描述它们怎么排布。通道两端可以绑在节点上:声明 `from` / `to` 之后,**两端必须有节点,通道才显示**——骨架的首尾顶点就是这两个节点的坐标,拖动节点整条通道跟着走,节点被删掉或设成 `visible: false`
|
|
7
|
+
一个通道表示一条走廊里并行敷设的多条回线(二 / 三 / 四 / 五回线都行)。回线本身照常写进 `lines`,通道只描述它们怎么排布。通道两端可以绑在节点上:声明 `from` / `to` 之后,**两端必须有节点,通道才显示**——骨架的首尾顶点就是这两个节点的坐标,拖动节点整条通道跟着走,节点被删掉或设成 `visible: false` 时通道与它的回线一起消失。写 `lineFollowsNode: false` 时例外:节点退场只影响节点,通道与回线继续停在最后一次解析出的骨架与宽度,见「筛选」与「配置项」。
|
|
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', ...)`。
|
|
8
28
|
|
|
9
29
|
### 通道选中模式(channelSelectMode)
|
|
10
30
|
|
|
@@ -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
|
|
|
@@ -112,11 +161,13 @@ console.log(view.getChannels()) // 骨架、每条回线的偏移与推导出来
|
|
|
112
161
|
|
|
113
162
|
两端锚定的通道还会把切点往节点里收一段:缺省按成员回线里最粗那条的线宽收(切点从节点边缘再往节点里挪一个线宽),所以线不会从节点边缘探出去;`options.channelTangentInset` 或单个通道的 `tangentInset` 可以改成固定的 CSS 像素(`0` 表示切点正好落在节点边上),和线宽一样会随相机缩放折算回世界单位。内收两侧合计最多吃掉节点宽度的 40%,小节点配粗线时不会把回线挤成一条;写成负数就是**外收**:切点从节点边缘往外让一段,整组回线比节点还宽,单侧最多让出节点宽度的 50%。
|
|
114
163
|
|
|
164
|
+
筛选只改变节点自身是否绘制,不改变仍显示的通道宽度:被筛掉的端点仍按 `points` 里的数据和同一条样式链取外框(点自身 > 规则 > `pointStyle`,含热力倍率),所以只筛掉一端时不会改按另一端尺寸算,两端都被筛掉时也不会退回 `width` / `channelWidth`。只有节点真被删除或写了 `visible: false` 时才按「没有节点」处理。
|
|
165
|
+
|
|
115
166
|
通道的配置项没有重复的,成对出现的都是「**全局默认 + 逐通道覆盖**」:`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
167
|
|
|
117
|
-
|
|
168
|
+
拐点挂在骨架上:`'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
169
|
|
|
119
|
-
|
|
170
|
+
两端锚定的通道与节点默认是联动的:拖动端点节点,骨架首尾跟着走(中间拐点保留),所有回线一起重算;节点被删掉或设成不可见时,通道与它的回线都从这一帧开始不参与渲染,节点回来后通道恢复,之前编辑过的拐点还在。`lineFollowsNode: false` 时只让节点退场,通道、回线与最后一份解析尺寸保持原样;节点仍在时拖动仍会实时更新通道几何。
|
|
120
171
|
|
|
121
172
|
### 通道分组
|
|
122
173
|
|
|
@@ -130,7 +181,8 @@ view.getChannelGroups('corridor-1')
|
|
|
130
181
|
// lineIndexes: [0, 1, 2, 3], lineIds: ['c-1', 'c-2', 'c-3', 'c-4'],
|
|
131
182
|
// anchored: true, edited: true,
|
|
132
183
|
// skeleton: [[116.2, 39.3], [116.7, 39.6], [117.2, 39.15]],
|
|
133
|
-
// path: [[116.7, 39.6]] // 可以直接写回 groups[].path
|
|
184
|
+
// path: [[116.7, 39.6]], // 可以直接写回 groups[].path
|
|
185
|
+
// waypointOffsets: {} // 'vertex' 拖出来的刚性偏移
|
|
134
186
|
// }]
|
|
135
187
|
|
|
136
188
|
// 拆成两组:每组最少一条;裸数组也行(分组 id 自动取 通道 id / 通道 id::2)
|
|
@@ -172,25 +224,27 @@ view.updateChannel('corridor-1', { name: '东干线', lineIds: ['c-1', 'c-2', 'c
|
|
|
172
224
|
- **一次拆几条走 `addChannelGroup(channelId, lineIds, options?)`**:`moveChannelLine` 一次只挪一条,要把两三回一起单独成组用这一条 —— **数据里没写 `groups` 的通道照样能拆**(没写就是「整条通道一组」,这几条从第一组里挪出去,被挪空的分组整组消失),新分组缺省照抄来源组当前的骨架(拆出来那一组画面不会跳),`options.path` 自己给这一组的中间拐点、`path: []` 要一条直连;组数 / 回线数到上限返回 `{ ok: false, reason }`,认不出的回线进 `skipped`、不影响其余条目。
|
|
173
225
|
|
|
174
226
|
- **合并时拐点跟随目标分组**:被并入的线路读的是目标分组的骨架,目标分组没有拐点就一条都不带(它们原来的拐点随之作废),目标分组自己的拐点原样保留;没参与合并的分组不受影响(拐点与编辑标记都留着)。
|
|
175
|
-
- 分组项可以写成 `{ id, lineIds, path }` 显式给这一组一份声明:给了 `path` 就按它重建(想主动清掉某组的拐点就写 `path: []`),只写 `id` + `lineIds` 则沿用同 id
|
|
227
|
+
- 分组项可以写成 `{ id, lineIds, path, waypointOffsets? }` 显式给这一组一份声明:给了 `path` 就按它重建(想主动清掉某组的拐点就写 `path: []`),只写 `id` + `lineIds` 则沿用同 id 分组当前的骨架与偏移;要清掉 `'vertex'` 偏移而不动骨架,重写声明时省略 `waypointOffsets`(空对象同义),只写 `id` + `lineIds` 会把当前偏移一起沿用。
|
|
176
228
|
- **分组变化另有专用事件 `channelGroupChange` / `onChannelGroupChange`**:拆分 / 合并 / 挪线 / 新建组与整份换声明除了照旧发 `dataChange('channelGroups')`(说「数据变了」),还会发这一条(说「变了什么」):`action.type` 为 `splitGroups` / `mergeGroups` / `moveLine` / `addGroup` / `setGroups`,`action.groupId` / `groupIds` / `lineIds` 指到具体分组与回线,`item` / `groups` 与 `dataChange` 那份是**同一个引用**(不想区分动作就照旧只接 `onDataChange`)。分组拐点写回声明那条路不发这一条 —— 它属于 `onWaypointChange`。
|
|
177
229
|
#### 拐点 / 分组同步后端(推荐)
|
|
178
230
|
|
|
179
231
|
这两类事件都不要逐帧提交,也不要把事件里的 action 当成完整数据:
|
|
180
232
|
|
|
181
233
|
- **拐点**:`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 自行猜「删了哪一组」:被挪空的分组会整组消失,按整份快照覆盖最稳。
|
|
234
|
+
- **通道拐点**:事件载荷里的 `line` 是**被操作那条回线**经过平行与刚性偏移后的几何,不能拿它覆盖整组骨架。要整份覆盖时,用 `channelId` + `groupId`(载荷里也可能是 `group_id`)调 `getChannelGroupWaypoints(channelId, groupId)`,提交返回的 `path` 与 `waypointOffsets`。只提交 `path` 会把 `'vertex'` 拖动结果丢掉。独立线路没有通道字段,才用 `PATCH /gis/lines/{id}/path`。
|
|
235
|
+
- **分组**:`onChannelGroupChange` 的 `action.type` 可能是 `splitGroups` / `mergeGroups` / `moveLine` / `addGroup` / `setGroups`;动作只是审计信息,声明以 `getChannelGroups(channelId)` 返回的整份 `groups` 为准(每项含 `id` / `lineIds` / `path` / `waypointOffsets`)。不要根据这个 action 自行猜「删了哪一组」:被挪空的分组会整组消失,按整份快照覆盖最稳。
|
|
184
236
|
- **空 path 合法**:删掉分组最后一个拐点时 `path` 就是 `[]`。这正是「回到直连」的完整声明,必须照常发请求;不要用 `if (!path.length) return` 把删除动作吞掉。
|
|
185
237
|
|
|
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)。
|
|
238
|
+
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
239
|
|
|
188
240
|
生产环境还要加两件事:**按通道串行**(同一条通道的请求不能并发覆盖,拖动期间只保留最后一个 `end`),以及**服务端版本校验**。整份覆盖接口应返回新版本号;客户端收到 409 时先拉取该通道最新声明,再让用户决定 rebase 还是放弃本地这次编辑。稳定 id 优先于数组下标,`groupId` 与 `lineIds` 都按声明里的 id 提交。
|
|
189
241
|
|
|
190
242
|
- **加点 / 删点 / 拖点只作用于同一分组**:同一分组共用一条骨架,其余分组的线路不动。
|
|
191
|
-
- 分组结果会写回 `channels[].groups`(`{ id?, lineIds, path? }`),下次 `updateOptions` / 换数据按它重建;也可以一开始就手写 `groups` 而不是 `lineIds` + `path`。
|
|
243
|
+
- 分组结果会写回 `channels[].groups`(`{ id?, lineIds, path?, waypointOffsets? }`),下次 `updateOptions` / 换数据按它重建;也可以一开始就手写 `groups` 而不是 `lineIds` + `path`。
|
|
192
244
|
- `getChannelMembers()` 与鹰眼图都按分组各成一份:`channelCircuits: false` 时每个分组各画偏移最靠中间的那一根回线。
|
|
193
245
|
|
|
194
246
|
---
|
|
195
247
|
|
|
248
|
+
**相关章节**:[03 配置项(options)](03-配置项.md) · [08 选中与拐点](08-选中与拐点.md) · [10 通道编辑与新增](10-通道编辑与新增.md) · [17 参数速查](17-参数速查.md)
|
|
249
|
+
|
|
196
250
|
[← 08 选中与拐点](08-选中与拐点.md) · [手册目录](../GisView.md) · [10 通道编辑与新增 →](10-通道编辑与新增.md)
|