gis-view-webgl2 0.1.7 → 0.1.9

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.
Files changed (70) hide show
  1. package/README.md +14 -11
  2. package/dist/gis-view.css +1 -1
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +445 -241
  5. package/dist/index.js +1 -1
  6. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +65 -498
  7. package/docs/GisView.md +1 -1
  8. package/docs/GisView/345/256/236/347/216/260/345/216/237/347/220/206.md +1 -1
  9. 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 +114 -478
  10. package/docs/demos/BasicDemo.vue +144 -27
  11. package/docs/demos/BatchAddDemo.vue +133 -7
  12. package/docs/demos/ChannelDemo.vue +61 -0
  13. package/docs/demos/ConnectDemo.vue +164 -33
  14. package/docs/demos/CopyMergeDemo.vue +144 -42
  15. package/docs/demos/EventDemo.vue +29 -24
  16. package/docs/demos/LabelDemo.vue +5 -5
  17. package/docs/demos/api-index.js +53 -41
  18. package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +1 -1
  19. package/docs/demos/gis-view-demo/GisViewDemoStage.vue +1 -1
  20. package/docs/demos/gis-view-demo/panels/DataPanels.vue +1 -1
  21. package/docs/demos/gis-view-demo/useGisViewDemo.js +31 -33
  22. package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +1 -0
  23. package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +3 -0
  24. 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 +8 -7
  25. package/docs/from0/05-/347/233/270/346/234/272/344/270/216/344/272/244/344/272/222.md +1 -1
  26. package/docs/from0/07-/351/200/232/351/201/223/345/210/206/347/273/204/344/270/216/345/244/232/345/233/236/347/272/277.md +26 -10
  27. package/docs/index.html +136 -13
  28. package/docs/internals/15-04-/351/200/220/350/275/256/350/256/260/345/275/225-/347/203/255/345/212/233/345/233/276/344/270/216/346/200/247/350/203/275.md +10 -0
  29. package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +1 -1
  30. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +82 -14
  31. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +23 -17
  32. package/docs/manual/05-/347/224/273/350/264/250/344/270/216/346/211/271/351/207/217/346/233/264/346/226/260.md +65 -0
  33. 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 +9 -10
  34. 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 +100 -40
  35. package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +11 -7
  36. package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +66 -6
  37. 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 +226 -493
  38. package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +28 -5
  39. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +46 -14
  40. package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +5 -2
  41. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +34 -29
  42. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +30 -22
  43. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +168 -81
  44. package/docs/manual/19-/350/257/273/346/225/260/344/270/216/346/213/226/346/213/275/351/242/204/350/247/210.md +1 -1
  45. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +5 -3
  46. 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 +9 -6
  47. package/docs/steps/README.md +201 -120
  48. package/docs/steps/bench-scale.html +1 -1
  49. package/docs/steps/index.html +222 -6
  50. package/docs/steps/selftest-actions.html +26 -25
  51. package/docs/steps/selftest-data.html +1 -1
  52. package/docs/steps/selftest-demos.html +290 -16
  53. package/docs/steps/selftest-docs.html +17 -6
  54. package/docs/steps/selftest-features.html +594 -54
  55. package/docs/steps/selftest-full.html +3 -3
  56. package/docs/steps/selftest-labels.html +82 -1
  57. package/docs/steps/selftest-perf.html +62 -7
  58. package/docs/steps/selftest-select.html +71 -6
  59. package/docs/steps/step-05-pick.js +4 -8
  60. package/docs/steps/step-07-channels.js +3 -5
  61. package/docs/steps/step-08-labels.js +3 -3
  62. package/docs/steps/step-10-full.js +3 -3
  63. package/docs/steps/step-12-events.js +41 -24
  64. package/docs//344/273/2160/345/210/260/345/256/214/346/225/264/347/232/204/345/256/236/347/216/260.md +1 -0
  65. package/package.json +1 -1
  66. package/docs/ai/01-/350/265/267/346/272/220/344/270/216/350/277/231/344/273/275/346/261/207/346/200/273/347/232/204/347/224/261/346/235/245.md +0 -29
  67. package/docs/ai/02-/346/214/211/345/206/205/345/256/271/345/210/206/347/261/273-/350/277/231/344/270/200/350/267/257/351/203/275/345/234/250/346/224/271/344/273/200/344/271/210.md +0 -66
  68. package/docs/ai/03-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/200/357/274/211/357/274/232/351/200/211/345/236/213/351/200/232/351/201/223/344/270/216/346/240/267/345/274/217.md +0 -178
  69. package/docs/ai/04-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/272/214/357/274/211/357/274/232/344/272/244/344/272/222/346/200/247/350/203/275/344/270/216/346/225/260/346/215/256.md +0 -140
  70. package/docs/ai/05-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/211/357/274/211/357/274/232/345/256/277/344/270/273/346/227/201/346/224/257/344/270/216/346/224/266/345/217/243.md +0 -43
@@ -135,7 +135,7 @@ const frame = await view.whenRendered()
135
135
  view.updateData(nextPoints, nextLines)
136
136
  await view.whenDataRendered()
137
137
 
138
- // snapshotScene(options?):一次取全画布读数 / 数据快照(矢量导出、单测都用它)
138
+ // snapshotScene(options?):一次取全画布读数 / 数据快照(矢量导出、单测都用它;raisedLines 是抬层线路)
139
139
  const snapshot = view.snapshotScene()
140
140
  const white = view.snapshotScene({ background: '#ffffff' })
141
141
 
@@ -178,7 +178,7 @@ view.focusOn({ type: 'line', id: 'a' })
178
178
  view.focusOn({ type: 'channel', channelId: 'c1' }) // 通道也可以直接写 { channelId: 'c1' }
179
179
  view.focusOn({ type: 'group', channelId: 'c1', groupId: 'c1::2' })
180
180
  view.focusOn({ type: 'group', groupId: 'global-group' }) // groupId 全局唯一时可省略通道
181
- view.focusOn([120, 80], { center: true }) // 显式只居中(与缺省一致)
181
+ view.focusOn([120, 80]) // 缺省只居中、比例不动
182
182
  view.focusOn({ lngLat: [116.4, 39.9] })
183
183
 
184
184
  // flyTo(target, options?):与 focusOn 同一套目标解析,平滑飞过去
@@ -201,9 +201,25 @@ const asyncHits = await view.searchAsync('甲站') // 大表走 Worker;s
201
201
 
202
202
  // setCamera(camera) / getCamera():读写相机(只写一个字段也行)
203
203
  view.setCamera({ scale: 10 })
204
- view.setCamera({ x: 200, y: 120 })
204
+ view.setCamera({ center: [200, 120] })
205
205
  const camera = view.getCamera()
206
206
 
207
+ // getCenter() / setCenter(center):只读写视图中心(setCenter 保留当前比例,返回实例)
208
+ const center = view.getCenter() // [x, y] 的副本
209
+ center[0] += 100 // 改副本不会移动视图
210
+ view.setCenter([116.4, 39.9])
211
+
212
+ // getScale():读当前比例(与 getCamera().scale 同一份数)
213
+ const scale = view.getScale()
214
+
215
+ // setScale(scale, origin?):立即改比例;origin 是画布 CSS 像素锚点,缺省视口中心
216
+ view.setScale(3)
217
+ view.setScale(3, [320, 180])
218
+
219
+ // getIdleTime():距创建或上一次指针 / 滚轮 / 键盘操作的毫秒数
220
+ const idleTime = view.getIdleTime()
221
+ view.updateOptions({ idleTimeout: 60000, onIdle: ({ idleTime }) => console.log(idleTime) })
222
+
207
223
  // stopCameraAnimation() / getCameraAnimation():停止飞行 / 读当前飞行读数(没有动画时为 null)
208
224
  const flight = view.getCameraAnimation() // { active, progress, duration, from, to }
209
225
  view.stopCameraAnimation() // 停在当前帧,不补跳到终点
@@ -384,8 +400,9 @@ view.deselect('a')
384
400
  view.deselect(['a', 'b'])
385
401
  view.deselect()
386
402
 
387
- // getSelections() / getSelectionData() / isSelected(ref):读回选中
403
+ // getSelections() / getSelectionInfo() / getSelectionData() / isSelected(ref):读回选中
388
404
  const picks = view.getSelections()
405
+ const info = view.getSelectionInfo() // { ids, count, points, lines, channels }
389
406
  const selection = view.getSelectionData() // { ids, count, points, lines, channels, bounds, … }
390
407
  const selected = view.isSelected('a')
391
408
 
@@ -405,11 +422,13 @@ view.on('cascadeHighlight', payload => console.log(payload?.pointIndexes, payloa
405
422
  // selectInBox(box, options?):用矩形框选(画布 CSS 像素,与手动框选同一套判定)
406
423
  view.selectInBox({ x: 40, y: 40, width: 320, height: 200 })
407
424
  view.selectInBox({ x: 40, y: 40, width: 320, height: 200 }, { selectionKinds: ['line', 'channel'] })
425
+ view.selectInBox({ x: 40, y: 40, width: 320, height: 200 }, { selectionKinds: ['lineEndpoints', 'channelEndpoints'] }) // 两端节点都在框内才命中
408
426
 
409
427
  // selectionKinds:框选与 Ctrl / multiSelect 点击的类型过滤(数组任意组合;'all' / true 全开,false / null 全关)
410
428
  view.updateOptions({ selectionKinds: ['point', 'line'] }) // 只收节点与通道外线路
411
429
  view.updateOptions({ selectionKinds: 'channel' }) // 只收通道回线(再按 channelSelectMode 摊开)
412
430
  view.updateOptions({ selectionKinds: ['point', 'line', 'channel'] })
431
+ view.updateOptions({ selectionKinds: ['lineEndpoints'] }) // 框选:两端节点都在框内的独立线路
413
432
 
414
433
  // beginCopyPoint(options) / getPointCopy() / updateCopyPoint(patch) / commitPointCopy() / cancelPointCopy()
415
434
  view.updateOptions({ copy: { enabled: true, autoCommit: false } }) // API 复制入口要先开 copy 配置
@@ -438,17 +457,26 @@ const clipboard = view.getPointClipboard()
438
457
  // mergePoints(source, target, options?):把 source 并进 target(接口调用,不做确认)
439
458
  const merged = view.mergePoints('a', 'b')
440
459
  const mergedCustom = view.mergePoints('a', 'b', { item: { name: '合并结果', size: 12 } })
460
+ console.log(merged.item?.mergeInfo?.sourceIds) // 保留节点上的来源 id;只有 merge.maxKeys 列出的字段取最高,缺省全部取保留端
441
461
 
442
462
  // mergeSelectedPoints(options?):框选 / 多选之后一次并完(目标缺省挑绑定线路最多的那个)
443
463
  const mergedAll = view.mergeSelectedPoints()
444
464
  view.mergeSelectedPoints({ target: 'b', sources: ['a', 'c'], force: true })
445
465
 
446
- // splitPoint(source, parts, options?):把一个节点拆成多份(mergePoints 的逆操作)
466
+ // splitPoint(source):有 mergeInfo 时自动解除合并(省略 parts / null / [] 等价)
467
+ const autoRestored = view.splitPoint('b', null, { label: '解除合并(自动恢复)' })
468
+ console.log(autoRestored.autoRestored, autoRestored.mergeSourceIds, autoRestored.changes)
469
+ // 默认不选中结果;需要选中全部恢复节点时写 splitPoint('b', null, { select: true })
470
+
471
+ // splitPoint(source, parts, options?):手工拆成多份(mergePoints 的逆操作)
447
472
  const splitResult = view.splitPoint('a', [
448
473
  { x: 0, y: 0, name: '甲站(主)' }, // 第一份沿用原下标 / id
449
- { x: 80, y: 40, name: '甲站(东)', lines: ['l1'] } // 显式分给第二份;未分配的线路跟第一份
450
- ], { label: '拆分甲站' })
451
- console.log(splitResult.pointIds, splitResult.linesChanged, splitResult.changes)
474
+ { x: 80, y: 40, name: '甲站(东)', lines: ['l1'] } // 显式分给第二份
475
+ ], { autoLines: 'alternate', label: '解除合并' })
476
+ // options.autoLines: 'first'(缺省,未分配的线路跟第一份)| 'alternate'
477
+ // alternate:同一条通道的成员成组、独立线路单条,轮流给负载最少的一份,解除合并推荐
478
+ console.log(splitResult.pointIds, splitResult.autoAssignedLines, splitResult.lineAssignments)
479
+ console.log(splitResult.autoLines, splitResult.changes) // lineAssignments 每项带 index / pointId / lineIds / lineCount
452
480
 
453
481
  // getPointMerge() / resolvePointMerge(confirmed?, options?) / commitPointMerge() / revertPointMerge()
454
482
  const pendingMerge = view.getPointMerge() // 待确认的那一份载荷(没有就是 null)
@@ -564,10 +592,9 @@ view.runChannelAction('split', { lineId: 'l2' })
564
592
  view.addChannelGroupWaypoint('c1', 'c1::2', [200, 120])
565
593
  view.removeChannelGroupWaypoint('c1', 'c1::2', 1)
566
594
 
567
- // beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?)
595
+ // beginAddChannel(options?) / beginAddLine(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?)
568
596
  // commitAddChannel(input?, options?) / cancelAddChannel() / endAddChannel()
569
597
  view.beginAddChannel({
570
- kind: 'channel',
571
598
  circuits: 3,
572
599
  channelDraftStyle: {
573
600
  color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],
@@ -582,70 +609,93 @@ const draft = view.getAddChannelDraft() // 草稿读数(跟随
582
609
  view.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头
583
610
  view.updateAddChannelStyle(null) // 回到全局 channelDraftStyle
584
611
 
612
+ // 草稿的 select 只决定点节点时是否顺手选中节点;缺省 false
613
+ view.beginAddChannel({ from: 'station-a', select: true })
614
+ // 这一段最终落地几条回线由 circuits / lineData 决定;落地后是否选中新回线由 commitAddChannel 的 options.select 决定
615
+ view.commitAddChannel({}, { select: true })
616
+
617
+ // 单一通道 / 已有通道策略:singleChannel 只约束 beginAddChannel
618
+ view.beginAddChannel({ from: 'station-a', singleChannel: true }) // 两端已有通道 -> plan.blocked,commit 发 phase: 'blocked'
619
+ view.beginAddChannel({ from: 'station-a', existingChannel: 'newGroup' })
620
+ view.beginAddChannel({ from: 'station-a', existingChannel: 'joinGroup', groupId: 'c1::2' })
621
+ view.beginAddChannel({ from: 'station-a', existingChannel: 'joinGroup', groupIndex: 1 })
622
+ view.beginAddChannel({ from: 'station-a', existingChannel: 'auto' }) // 有新分组余量就新分组,否则并组
623
+
624
+ // beginAddLine:入口固定为线路语义;缺省独立线路,显式 existingChannel 且已有通道时才挂进通道
625
+ view.beginAddLine({ from: 'station-a' })
626
+ view.beginAddLine({ from: 'station-a', existingChannel: 'newGroup' })
627
+ view.beginAddLine({ from: 'station-a', existingChannel: 'joinGroup', groupId: 'c1::2' })
628
+ // 旧 beginAddChannel({ kind: 'line' }) 已移除;线路入口只认 beginAddLine
629
+
585
630
  // commitAddChannel 只负责把已 ready 的草稿落地;没有草稿的程序化新增用 addChannel / addLine。
586
631
  // 下面每个 commit 例子都假设当前草稿已经重新 ready;实际连续调用请逐段 begin / 点选。
587
- // 五类入参:无参、旧 resolution、直接线路、直接通道(必须有 lineIds)、容器(channel / line / lines / ...)。
588
632
  // 1. 无参:沿用草稿的 preferred(newChannel / newGroup / joinGroup / newLine)
589
633
  view.commitAddChannel()
590
634
 
591
- // 2. 旧 resolution:字符串或只含 type / groupId / groupIndex / channelId 的对象
592
- view.commitAddChannel('newChannel')
593
- view.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2', groupIndex: 1, channelId: 'c1' })
635
+ // 2. 只改落点:所有 commitAddChannel 入参都写在一个容器对象里
636
+ view.commitAddChannel({ resolution: 'newChannel' })
637
+ view.commitAddChannel({ resolution: { type: 'joinGroup', groupId: 'c1::2', groupIndex: 1, channelId: 'c1' } })
594
638
 
595
639
  // 3. 从 ready 的方案读数里选一条:只取四个定位字段,不能把整条 option 回传。
596
- // 整条 option 还带 enabled / count / lineCount / lineIds,可能被误识别成直接通道或容器。
640
+ // 整条 option 还带 enabled / count / lineCount / lineIds,只取 resolution 需要的子集。
597
641
  view.updateOptions({
598
642
  onChannelDraft: payload => {
599
643
  if (payload.phase !== 'ready' || payload.blocked) return
600
644
  const chosen = payload.options.find(option => option.enabled && option.type === 'joinGroup')
601
645
  if (!chosen) return
602
646
  const result = view.commitAddChannel({
603
- type: chosen.type,
604
- groupId: chosen.groupId,
605
- groupIndex: chosen.groupIndex,
606
- channelId: chosen.channelId
647
+ resolution: {
648
+ type: chosen.type,
649
+ groupId: chosen.groupId,
650
+ groupIndex: chosen.groupIndex,
651
+ channelId: chosen.channelId
652
+ }
607
653
  })
608
654
  console.log(result.ok, result.reason, result.lineIds)
609
655
  }
610
656
  })
611
657
 
612
- // 4. 直接线路对象:没有 lineIds,但至少写 from / to / path / kind: 'line' 之一。
613
- // 业务字段和 lineProps 都透传到新线路;path 是中间拐点(不含首尾)。
658
+ // 4. 独立线路对象放在 line 里:from / to、业务字段与 lineProps 都透传到新线路;
659
+ // path 是中间拐点(不含首尾)。线路对象不要直接放在顶层。
614
660
  view.commitAddChannel({
615
- id: 'new-line',
616
- name: '对象线路',
617
- from: 'station-a',
618
- to: 'station-b',
619
- path: [[116.72, 39.91], [116.86, 39.95]],
620
- color: '#ff7a45', // 直接业务字段 → lineProps
621
- width: 3,
622
- lineType: 'dashed',
623
- voltageLevel: '10kV',
624
- lineProps: { opacity: 0.9 } // 合并顺序:业务字段 → 自己的 lineProps
661
+ line: {
662
+ id: 'new-line',
663
+ name: '对象线路',
664
+ from: 'station-a',
665
+ to: 'station-b',
666
+ color: '#ff7a45',
667
+ width: 3,
668
+ lineType: 'dashed',
669
+ voltageLevel: '10kV',
670
+ lineProps: { opacity: 0.9 }
671
+ },
672
+ path: [[116.72, 39.91], [116.86, 39.95]]
625
673
  })
626
674
 
627
- // 5. 直接通道对象:必须写 lineIds;每个 id 生成一条回线,groups 是分组声明。
628
- // id 只决定通道 id,不是回线 id 种子;并进已有通道要改用容器 + resolution。
675
+ // 5. 通道对象放在 channel 里:lineIds 的每个 id 生成一条回线,groups 是分组声明。
676
+ // id 只决定通道 id,不是回线 id 种子;并进已有通道要显式写 resolution。
629
677
  view.commitAddChannel({
630
- id: 'new-channel', name: '对象通道', from: 'station-a', to: 'station-b',
631
- lineIds: ['new-channel-1', 'new-channel-2'],
632
- groups: [
633
- { id: 'new-group-1', lineIds: ['new-channel-1'], path: [[116.75, 39.86]] },
634
- { id: 'new-group-2', lineIds: ['new-channel-2'], path: [[116.75, 39.98]] }
635
- ],
636
- width: 28,
637
- lineProps: { color: '#2f6fd0', lineType: 'dashed' },
638
- owner: '运维一班' // 其余字段 → channelProps
678
+ channel: {
679
+ id: 'new-channel', name: '对象通道', from: 'station-a', to: 'station-b',
680
+ lineIds: ['new-channel-1', 'new-channel-2'],
681
+ groups: [
682
+ { id: 'new-group-1', lineIds: ['new-channel-1'], path: [[116.75, 39.86]] },
683
+ { id: 'new-group-2', lineIds: ['new-channel-2'], path: [[116.75, 39.98]] }
684
+ ],
685
+ width: 28,
686
+ owner: '运维一班'
687
+ },
688
+ lineProps: { color: '#2f6fd0', lineType: 'dashed' }
639
689
  })
640
690
 
641
691
  // 6. 容器写法:一段一段覆盖草稿,没写的继续用草稿。
642
- // lines / lineData / lineItems 同义,优先级 lines > lineData > lineItems,给了几条就建几条。
692
+ // lines / lineData 同义,优先级 lines > lineData,给了几条就建几条。
643
693
  view.commitAddChannel({
644
694
  resolution: 'newChannel',
645
695
  from: 'station-a',
646
696
  to: 'station-b',
647
697
  channel: { id: 'container-channel', name: '容器通道', nodeWidth: 1 },
648
- lines: [
698
+ lineData: [
649
699
  { id: 'n-1', color: '#dd4b2e', width: 2.2, name: '一回' },
650
700
  'n-2',
651
701
  { id: 'n-3', color: '#2ea36b', voltageLevel: '10kV' }
@@ -654,52 +704,56 @@ view.commitAddChannel({
654
704
  { id: 'container-channel::1', lineIds: ['n-1', 'n-2'] },
655
705
  { id: 'container-channel::2', lineIds: ['n-3'], path: [[116.78, 39.96]] }
656
706
  ],
657
- routing: 'orthogonal', // 只有容器这一层的 routing 会改走线
658
- path: [[116.78, 39.9]], // 中间拐点,不含两端
659
- lineProps: { opacity: 0.9 }, // 容器合并:草稿 → 顶层 → line.lineProps → 逐条
660
- channelProps: { width: 36 } // 最后覆盖新通道字段
707
+ routing: 'orthogonal',
708
+ path: [[116.78, 39.9]],
709
+ lineProps: { opacity: 0.9 },
710
+ channelProps: { width: 36 }
661
711
  })
662
712
 
663
- // 7. 只给 channel.lineIds 当种子:没有 lines / line / lineData / lineItems 时,每个 id 建一条回线
713
+ // 7. 只给 channel.lineIds 当种子:没有 lines / line / lineData 时,每个 id 建一条回线
664
714
  view.commitAddChannel({
665
715
  channel: { id: 'seed-channel', from: 'station-a', to: 'station-b', lineIds: ['seed-1', 'seed-2'] },
666
716
  lineProps: { color: '#2f6fd0', width: 2 }
667
717
  })
668
718
 
669
- // 8. 空数组不覆盖草稿:lines / lineData / lineItems 给 [] 时,沿用草稿的 lineItems / circuits
719
+ // 8. 空数组不覆盖草稿:lineData / lines 给 [] 时,沿用草稿的 lineData / circuits
670
720
  view.commitAddChannel({ channel: { id: 'draft-lines' }, lines: [] })
671
721
 
672
- // 9. 覆盖端点 / id / 名称 / 路由:顶层字段优先,lineId 是多回时自动展开的 id 种子
722
+ // 9. 覆盖端点 / id / 名称 / 路由:顶层字段优先,id 是线路 id 种子
673
723
  view.commitAddChannel({
674
- from: 'station-c', to: 'station-d', name: '改道联络线',
675
- lineId: 'rerouted', lines: [{ id: 'rerouted-1' }, { id: 'rerouted-2' }],
724
+ from: 'station-c', to: 'station-d', name: '改道联络线', id: 'rerouted',
725
+ lineData: [{ id: 'rerouted-1' }, { id: 'rerouted-2' }],
676
726
  routing: 'orthogonal', path: [[116.9, 39.9]]
677
727
  })
678
728
 
679
- // 10. 并进已有通道 / 分组:直接通道默认新建,所以用容器并显式写 resolution
729
+ // 10. 并进已有通道 / 分组:显式写 resolution
680
730
  view.commitAddChannel({
681
731
  resolution: { type: 'joinGroup', groupId: 'c1::2' },
682
- lines: [{ id: 'join-1', color: '#dd4b2e' }, { id: 'join-2', color: '#2ea36b' }],
732
+ lineData: [{ id: 'join-1', color: '#dd4b2e' }, { id: 'join-2', color: '#2ea36b' }],
683
733
  lineProps: { width: 2.4 }
684
734
  })
685
735
 
686
- // 11. 第二参数只管本次收尾:continuous: false 强制结束;originalEvent 原样带回重开的草稿
687
- view.commitAddChannel(undefined, { continuous: false, originalEvent: null })
736
+ // 11. 第二参数只管本次收尾与选中:continuous: false 强制结束,select: true 才选中新回线
737
+ view.commitAddChannel(undefined, { continuous: false, originalEvent: null, select: true })
688
738
  const commitResult = view.commitAddChannel()
689
739
  console.log(commitResult.ok, commitResult.kind, commitResult.lineIds, commitResult.channelLineIds)
690
740
 
691
- view.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)
741
+ // 连续模式只续草稿配置,不能把这一段切换成线路;要线路请重新 beginAddLine
742
+ view.continueAddChannel({ from: 'station-b' })
692
743
  view.cancelAddChannel() // 取消(Esc / 右键等价)
693
744
 
694
- // 交接模式:两个节点选完发完 ready,假通道当场收掉,使用方自己走 addChannel
745
+ // 交接模式:两个节点选完发完 ready,假通道当场收掉,使用方自己走 addChannel。
746
+ // 新增回线缺省不选中;这里显式传 options.select: true。
695
747
  view.beginAddChannel({ handoff: true })
696
748
  view.updateOptions({
697
749
  onChannelDraft: payload => {
698
750
  if (payload.phase === 'ready') {
699
- view.addChannel({ from: payload.from.id, to: payload.to.id, name: '手填通道' }, [
700
- { id: 'm-1', color: '#dd4b2e' },
701
- { id: 'm-2', color: '#2ea36b' }
702
- ])
751
+ view.addChannel(
752
+ { from: payload.from.id, to: payload.to.id, name: '手填通道' },
753
+ [{ id: 'm-1', color: '#dd4b2e' }, { id: 'm-2', color: '#2ea36b' }],
754
+ undefined,
755
+ { select: true }
756
+ )
703
757
  }
704
758
  }
705
759
  })
@@ -714,27 +768,55 @@ if (between?.hasChannel) {
714
768
  console.log(between.channelId, between.groupCount, between.groups.map(group => group.id), between.currentGroupId)
715
769
  }
716
770
 
717
- // addChannel(spec, lineData?, group?) / addChannels(input, options?) / addChannelGroups(channelId, groups, options?) / addLine(input):程序化新增
718
- const channelAdd = view.addChannel({ from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] }, [{ id: 'm-1' }, { id: 'm-2' }])
719
- const manyChannels = view.addChannels([{ from: 'a', to: 'b', circuits: 1 }, { from: 'b', to: 'a', circuits: 2, kind: 'line' }])
771
+ // getDirectConnections(pointIds):只取这组节点之间的直接通道 / 线路(不走中间节点;没有就是 [])
772
+ const directConnections = view.getDirectConnections(['a', 'b'])
773
+ for (const connection of directConnections) {
774
+ if (connection.type === 'channel') {
775
+ console.log('通道', connection.channelId, connection.lineIds, connection.from.id, connection.to.id)
776
+ }
777
+ else {
778
+ console.log('线路', connection.lineId, connection.from.id, connection.to.id)
779
+ }
780
+ }
781
+
782
+ // addChannel(spec, lineData?, group?, options?) / addChannels(input, options?)
783
+ // addChannelGroupLines(channelId, lineData?, group?, options?) / joinChannelGroupLines(channelId, groupId?, lineData?, options?)
784
+ // addChannelGroups(channelId, groups, options?) / addLine(spec, lineData?, options?):程序化新增
785
+ // 所有入口缺省都不改变选中集;只有 options.select: true 才选中这一次新建的回线。
786
+ const channelAdd = view.addChannel(
787
+ { from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] },
788
+ [{ id: 'm-1' }, { id: 'm-2' }],
789
+ undefined,
790
+ { select: true }
791
+ )
792
+ const manyChannels = view.addChannels([
793
+ { from: 'a', to: 'b', name: '三号通道', lineData: [{ id: 'm-3' }, { id: 'm-4' }] },
794
+ { from: 'b', to: 'a', name: '四号通道', lineData: ['m-5'] }
795
+ ])
796
+ console.log(channelAdd.lineIds, manyChannels.lineIds)
797
+
798
+ // 独立线路:第二段 lineData 给几条就建几条;只碰 lines,不碰 channels
799
+ const lineAdd = view.addLine(
800
+ { from: 'c', to: 'd', name: '三回' },
801
+ [
802
+ { id: 'wl-1', name: '第一回', color: '#dd4b2e', width: 5 },
803
+ { id: 'wl-2', color: '#2ea36b' }
804
+ ],
805
+ { select: true }
806
+ )
807
+ console.log(lineAdd.lineIds, lineAdd.channelId) // channelId 为 null:独立线路不挂通道
808
+
809
+ // 已有通道:新加一组 / 并进某一组(语义直说,不用自己拼 resolution)
810
+ const addedGroup = view.addChannelGroupLines('c1', [{ id: 'm-6' }], { id: 'c1::3' }, { select: true })
811
+ const joined = view.joinChannelGroupLines('c1', 'c1::2', [{ id: 'm-7' }])
812
+ console.log(addedGroup.channelLineIds, joined.channelLineIds) // 更新后这条通道的完整成员表
813
+
814
+ // 一次加多组:每组写 lineData / lines,lineItems 已移除;缺省不选中
720
815
  const manyGroups = view.addChannelGroups('c1', [
721
- { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-7' }, { id: 'm-8' }] },
722
- { path: [[150, 90]], lines: ['m-9'] }
816
+ { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-8' }, { id: 'm-9' }] },
817
+ { path: [[150, 90]], lines: ['m-10'] }
723
818
  ])
724
819
  console.log(manyGroups.count, manyGroups.lineIds, manyGroups.groupIds)
725
- const lineAdd = view.addLine({ from: 'a', to: 'b', name: '三回' })
726
- // 第二段 lineItems:连同线路一起新建(一条或一批逐条属性),给了几条就建几条(circuits 忽略)
727
- const withLines = view.addChannel({ from: 'c', to: 'd', name: '三号通道' }, [
728
- { id: 'wl-1', name: '第一回', color: '#dd4b2e', width: 5 },
729
- { id: 'wl-2', color: '#2ea36b' }
730
- ])
731
- // 并进已有通道:resolution 或顶层简写(channelId / groupId / groupIndex)
732
- view.addChannel({ from: 'a', to: 'b', resolution: { type: 'joinGroup', groupId: 'c1::1' } })
733
- const joined = view.addChannel({ from: 'a', to: 'b', channelId: 'c1', groupIndex: 0 }) // 旧写法:取这条通道的第 0 组
734
- // 已经有通道时用拆出来的两条(语义直说,不用自己拼 resolution)
735
- view.addChannelGroupLines('c1', [{ id: 'm-5' }], { id: 'c1::3' }) // 新加一组
736
- view.joinChannelGroupLines('c1', 'c1::2', [{ id: 'm-6' }]) // 并进某一组(序号 1 也认)
737
- console.log(joined.channelLineIds) // 更新后这条通道的完整成员表(lineIds)
738
820
 
739
821
  // removeLine(lineId) / removeLines(lineIds) / removeChannel(channelId):删线路 / 删多条 / 删整条通道
740
822
  view.removeLine('l1')
@@ -851,6 +933,10 @@ view.updateLabel('point', 'a', { style: { color: '#f60' } })
851
933
  view.updateLabel('point', 'a', { labelId: 'volt', content: '{vn_kv} kV' })
852
934
  view.updateLabel('line', 'l1', { labelId: 'note1', hidden: true })
853
935
  view.updateLabel('point', 'a', { position: 'bottom', offset: [0, 6], visibility: 'always' })
936
+ view.updateLabel('point', 'a', { followNodeZIndex: true }) // 跟随节点样式链的 zIndex
937
+ view.updateLabel('point', 'a', { zIndex: 30 }) // 显式层级压过跟随
938
+ view.updateLabel('line', 'l1', { zIndex: 10, zoomScale: 1 }) // 线路标签层级 + 字号随地图缩放
939
+ view.updateLabel('point', 'a', { zIndex: null, zoomScale: null }) // 回到配置默认
854
940
 
855
941
  // addLabel(kind, ref, definition, labelId?) / removeLabel(kind, ref, labelId?)
856
942
  const addedLabel = view.addLabel('point', 'a', { content: '{name} 备注', position: 'bottom' })
@@ -934,6 +1020,7 @@ view.clearWebGLLayers() // 清空全部自定义层
934
1020
  // exportData(options?):统一入口 —— scope 选范围(all / viewport / selection),parts 选分项,format 选口径
935
1021
  const viewFormat = view.exportData() // 全量 + view 口径(缺省)
936
1022
  const inViewport = view.exportData({ scope: 'viewport', margin: 24 }) // 视口内,可外扩 24 屏幕像素
1023
+ const endpointsOnly = view.exportData({ scope: 'viewport', selectionKinds: ['lineEndpoints', 'channelEndpoints'] }) // 两端节点都在视口内
937
1024
  const picked = view.exportData({ scope: 'selection', parts: ['station', 'channel'], withLines: true })
938
1025
  const fullDoc = view.exportData({ format: 'full' }) // 标准数据文档:整份喂回 createGisView / importData 就回到这一屏
939
1026
  const testFormat = view.exportData({ format: 'test', parts: ['line', 'waypoint'] })
@@ -36,7 +36,7 @@ canvas.addEventListener('contextmenu', (event) => {
36
36
 
37
37
  `GisViewListOptions` 的几个筛选条件是「与」的关系:`indexes`(只看这几个下标)、`inView`(`true` 按当前视口,或者直接给一份世界矩形 `{ minX, minY, maxX, maxY }`)、`limit`(最多几条)、`visibleOnly`(缺省 `true`:被筛选 / `visible: false` 隐藏掉的不算)、`withScreen`(补一份画布 CSS 像素坐标)。都不给就是「全部可见的」;十万级数据下这套方法适合面板读数、导出与校验,别逐帧调。
38
38
 
39
- `listPoints()` 用的就是引擎画点那一份可见判定(旧版单独暴露的 `getVisiblePoints()` 并进了这一条):`radius` / 图层号 / 屏幕坐标这些几何读数都在它给的条目里。`focusOn()` 只移动相机、不改数据:缺省(以及显式 `{ center: true }`)只居中、比例不动,`{ zoom: true }` 才按目标范围适配,`{ select: true }` 顺带把这批选上,传一批目标(数组)就是整体居中。
39
+ `listPoints()` 用的就是引擎画点那一份可见判定(旧版单独暴露的 `getVisiblePoints()` 并进了这一条):`radius` / 图层号 / 屏幕坐标这些几何读数都在它给的条目里。`focusOn()` 只移动相机、不改数据:缺省只居中、比例不动,`{ zoom: true }` 才按目标范围适配,`{ select: true }` 顺带把这批选上,传一批目标(数组)就是整体居中。
40
40
 
41
41
  `getPointStyle()` / `getLineStyle()` 给的是**当前解析结果**:节点看尺寸 / 图标 / 层级,线路看原始线宽 / RGBA / 透明度 / 层级;悬停 / 选中样式、整体倍率、热力倍率都不算在里面。逐条自己写死的字段优先于规则与全局样式,所以用它们对账「预览里多大、落地后多大」或者鹰眼图的小图尺寸时,不需要自己重跑样式链。
42
42
 
@@ -56,13 +56,15 @@ const stats = view.getStats() // clusterCount / clusterPointCount /
56
56
  10 万点 + 10 万线(含通道)在 `docs/demos/GisViewDemo.vue` 里可以直接切到,交互路径上没有按总量线性增长的循环:
57
57
 
58
58
  - **高密度纯文本标签画布化**:强制显示档(`labelOptimize` 未开、`denseMode` 未激活)下,节点层或线路层里能画布的纯文本标签达到 **1024** 条时,默认 `labelRender: 'auto'` 会让该层改用一张 `.gis-view__label-canvas`,不再为每条标签建 `div`。要求优化档也走画布时显式写 `labelRender: 'canvas'`:不设数量阈值,`maxCount` / `when` / `scanLimit` 仍照常截断,避让、换锚点与沿线滑动直接使用画布尺寸,不创建也不测量 DOM。HTML、函数返回元素、可拖拽 / 可编辑及超出 Canvas 样式能力的标签始终留在同层 DOM,因此画布与少量 DOM 可以混排;`labelRender: 'dom'` 可强制回退逐条 DOM。画布路径另有文字测量缓存(同字体 / 样式 / 文案只量一次)、交互期 `requestAnimationFrame` 合并重绘与屏外标签裁剪,连续平移 / 缩放时不会因多次指针事件重复清画布。`selftest-labels.html` 用 1100 条纯文本验证字宽只测一次、连续 5 次缩放只重绘 1 次、显式画布在优化档遵守 `maxCount: 200`,以及线路标签 6 条全部进画布;另覆盖混入 `html: true` 时只保留那一条 DOM、切回 `dom`、再切回 `auto` 的节点回收与 SVG 导出。
59
+ - **标签层级、节点遮挡与缩放字号**:跟随节点层级(`followNodeZIndex`)的点标签需要知道哪些更高层节点压住了自己的盒子。覆盖层为此按需建立一张 64px 屏幕网格,只收有效 `zIndex > 0` 的视口内节点;引擎没有任何非 0 节点层时整趟扫描直接跳过。判定只在标签真正参与排版时发生,被盖住的标签不占避让网格、不计 `maxCount`;`updatePoint` / `patchPoints` 会比对被改节点的层号、屏幕半径与坐标,必要重排时被改节点自己没标签也会补一次。`zoomScale` 的字号计算每轮只问一次相机,缩放指数共用 `Math.pow` 结果,交互期按 0.1px 量化后只改变换,停手再按真实字号校正盒子与避让,避免缩放过程中反复重排整屏标签。Canvas 标签仅在同层出现倒序时才排序,已有序时直接复用上一轮顺序。
60
+ - **选中线路抬层**:`selectionOnTop` 没有把选中线路插进普通的逐线循环,而是把线路实例、阴影、圆头、高亮与热力光晕各自拆成「普通段 + 抬层段」;只在抬层集合真的变化时重铺线路批次,集合内容相同(只换了一份引用)不会把十万条线重算一遍。两端节点单独进抬层点集合,绘制次序因此固定为普通节点 → 抬层线路 → 抬层端点,不需要为每条线动态调整节点层号。
59
61
  - 拾取与框选共用网格索引:点走按数据范围重建的均匀网格,线走随线段重建的 `createSegmentGrid`,每次交互只遍历光标附近的候选,耗时与总量基本脱钩,`getStats().pickMs` 是随手可读的实测值(10 万规模下热态约 0.2 ~ 0.3 ms / 次)。线段按格线步进登记进它真正穿过的格子(早期按包围盒填格,一条斜跨全图的线会占掉上万格、只好整条丢进「超长线」单,而十万条线里绝大多数都是这种,于是每次拾取都要重算十万条的距离);剩下的长线只记一条包围盒,查询时按盒子先筛一遍。10 万点 / 10 万线的合成数据里(随机配对、长线极密),500 次 `pick` 从 3530 ms 降到 164 ms(0.3 ms / 次)。
60
62
  - 拖动时「哪些线跟着这个点走」按需建索引:`collectDragLinks` 从前每次都把全部线路的端点与组内成员逐个比一遍,10 万点 / 12 万线下这一步占总 CPU 的 27%,是拖动路径上唯一还按总量线性付费的循环。现在线路超过 `LINE_BIND_TOLERANCE_SCAN_LIMIT`(2048)条时改用一张线路归属表(按 `from` / `to` 的 id 与端点坐标建,整份换数据 / 增删线 / 改绑定才重建,只改坐标就地把那一条换掉),只对命中的候选逐字段判定 —— 十万级拖动降到 CPU 的 1% 以下,单条 `updatePoint`(改名这类不动几何的改动)55 → 3.3 ms(稳态 0.02 ms)。线路不多时保留原来的浮点容差扫描(行为与从前逐字不差);索引那条路只认 id 绑定与坐标精确相等 —— 这个量级坐标都是同一份来源算出来的,容差是给手写数据留的余量。
61
63
  - 视口里的线路改走线段网格:线路标签排版这类「只看屏幕里这一片」的路径(`forEachLineInView`)原先要把每条线的包围盒过一遍,10 万级 + 标签的 CPU profile 里它占 47 ~ 58%。现在线路超过 `LINE_VIEW_GRID_MIN_LINES`(2048)条、且估计出来的候选线段只占总数的 1/32 以内(`LINE_VIEW_GRID_COST_SHARE`)时改查拾取用的那张线段网格,再逐条按包围盒确认;候选排回要素下标顺序,脏线与没有线段的退化线单独补一遍,访问顺序与判定口径与整份扫描逐行相同。网格脏着(正在拖线 / 改线)或线路不多时照旧整份扫描。
62
64
  - 视口里的节点遍历也先查点网格:`forEachPointInView` 在节点超过 `POINT_VIEW_GRID_MIN_POINTS`(2048)、点网格不脏且估计候选不足总数的 1/32(`POINT_VIEW_GRID_COST_SHARE`)时,只查视口覆盖的格子、把候选排回数据顺序再逐个精确判定;网格脏着(刚改过数据 / 拖拽中)或视口几乎铺满时仍走整份扫描,避免为了省一趟遍历先付出建网格和排序的成本。`createPointGrid.estimate(..., stopAfter)` 与查询共用同一套格子口径,传 `stopAfter` 时一旦超过阈值立即返回,只判断候选是否过密时不必扫完视口里的全部格子;框选线路则复用已有的 `forEachLineInView`,不再为每条候选线复制路径 / 包围盒 / 颜色快照。
63
65
  - 数据包围盒读数改为缓存:整份解析本来已经顺路算出点与线的范围,`getBounds()` 直接接住这一份;定向改点 / 改线、拖拽、追加要素与复制预览只把缓存标脏,下一次真被问到时才用点坐标和每条线的 `line.bounds` 重建,不再逐顶点重扫。`getBounds()` 每次返回独立副本,调用方就地修改不会污染下一次读数;追加到原范围之外、拖出边界这类增量也会立即失效。10 万点 + 10 万线(每条 10 顶点)实测连续 10 次 `getBounds()`:优化前 **5.2 ~ 6.6 ms / 次**,优化后 **0.00 ms / 次**;改一个点后缓存重建一次 **3.6 ms**,读数正确。
64
66
  - 悬停、选中、拖拽只做增量更新:选中集合用点 / 线两张下标 `Set` 维护,样式变化按要素下标重新解析,顶点与实例数据就地改写后按「实例槽位」上传,不重建整批缓冲。
65
- - 选中事件的通道分组读数一次成型:同一条通道的 `groups` 只构造一份,`onSelect` / `onSelectChange` 与载荷顶层共享对象引用,不深拷贝;单通道不再额外生成 `channelContexts`,`items[]` 里的回线只保留 `channelId` / `channelIndex` / `groupId` / `groupIndex` 标量,不逐条重复挂 `group` / `groups`。单通道也不会先构造随后丢弃的 `lineIndexes` / `lineIds`,这两份只在多通道 `channelContexts` 里按选中顺序补一次。通道重建时顺手按 `channelIndex` 收好分组状态与组序号,后续悬停 / 选中查同通道分组和组号都是 O(1),不再每次筛整张通道表;构造一条通道的全部分组读数从 O(组数 × 通道数) 降为 O(组数)。`selectFeatures` 对同内容、同顺序的重设会在引擎刷新和 `describeSelection` 之前直接返回;事件本身只在选中操作 / 选中集变化时发生,不进入逐帧渲染路径。
67
+ - 选中事件的通道上下文只在外层出现一次:同一条通道的 `groups` 只构造一份,`onSelect` / `onSelectChange` 与载荷顶层共享对象引用,不深拷贝;单通道把通道 / 当前组与本次选中的 `lineIndexes` / `lineIds` 摊平到顶层,不再额外生成 `channelContexts`,多通道才使用 `channelContexts`。`items[]` 里的回线完全不挂 `channelId` / `channelIndex` / `groupId` / `groups` 等通道字段,十万级选中时不为每条线路重复写同一份归属。组件侧按载荷外层的 `lineIndexes` 一次建立“线路下标 → 通道”缓存;选中数组换新时按引用失效,其他改动路径用引擎 O(1) 的 `getLineChannel()` 补建,`getSelectionInfo()`、复制与 `getSelectionData()` 直接查表,不再逐条 `describe()`。通道重建时顺手按 `channelIndex` 收好分组状态与组序号,后续悬停 / 选中查同通道分组和组号都是 O(1);构造一条通道的全部分组读数从 O(组数 × 通道数) 降为 O(组数)。指针载荷先展开拾取结果再补 `action` / 光标读数,不再把 `type` / `index` / `item` 和整个结果各写一遍;`selection`、按键 / 修饰键、`clientX` / `clientY` 这类可再取得的读数不再每条事件复制一份,`lngLat` 也改为就地补字段。`selectFeatures` 对同内容、同顺序的重设会在引擎刷新和 `describeSelection` 之前直接返回;事件本身只在选中操作 / 选中集变化时发生,不进入逐帧渲染路径。
66
68
  - 状态样式就地更新:悬停 / 选中样式只改颜色、大小、描边时按实例槽位覆盖,不重建整批;符号换成 / 换出贴图、阴影条数变化这类批次结构变化才回退整批重排。哪些要素当前真的按状态样式画过另有一份记账(`stateStyledSignatures`),整体重建后立刻重算,取消选中时按它精确还原本体样式,不会留下残留。
67
69
  - 动效只维护真正的动画线路:`lineEffectRuns` 让每帧的动点循环只遍历配了 `effect` 的线路,路径就地变化时只重算这一条的总长与包围盒;普通线路再多,也不会进入动画的逐帧扫描。
68
70
  - 热力图的要素采样与密度烘焙分开缓存:坐标 / 权重 / 显隐变化才重新采集,核参数 / 色带变化复用采样重烤,`density.opacity` 与 `layer` 只改绘制;拖动期间延迟到停手统一补烤。
@@ -106,8 +108,8 @@ const stats = view.getStats() // clusterCount / clusterPointCount /
106
108
  - **运动期保持全分辨率**:`interactionNoMsaa` 的非 MSAA 后备缓冲只换抗锯齿与细线画法,不降低 viewport / 后备存储尺寸。相机拖动、滚轮、`zoomBy` / `setCamera` / 飞行与尺寸倍率变化的帧都按画布实际像素绘制,整幅贴回保持 1:1,停手 160 ms 后恢复 MSAA 静态帧。上一轮曾按整帧间隔把运动帧降到 **0.65× / 0.5×** 再线性放大贴回,实测会让缩放 / 移动中的画面短暂模糊,现已整体移除;与之配套的 `drawWidth` / `drawHeight` / `framePixelRatio`、`u_offsetScale` 和 UV 裁剪也一并删除。尺寸倍率连续变化仍复用 160 ms 的 `scaleMotion` 窗口,暂停符号贴图重烤与预烤,停手后再对齐当前尺寸档;这部分只省重烤,不改变当前帧分辨率。最终全分辨率基准(1280×800,无头 Chrome + ANGLE / AMD D3D11,`quality: 'high'`,三次运行取中位):10 万点 / 5 万线缩放 p50 **23.2 ms**、p95 **28.6 ms**,平移 **16.7 / 18.2 ms**;10 万点 / 10 万线缩放 **42.6 / 46.3 ms**,平移 **16.7 / 24.5 ms**。`selftest-perf.html` 另有回归断言记录每次 `gl.viewport`,人为拉长运动帧间隔时也必须始终等于画布全尺寸。
107
109
  - **片元管线专用化(为什么此前「感觉没提升多少」)**:之前的优化主要落在相机排帧、聚合脏标记、整份重排与符号预烤上;10 万线交互的 CPU profile 约 **98.6% idle**、`render` 自身约 **0.1%**,说明主线程不是瓶颈。真正剩余的是每条可见线在 GPU 上重复执行虚线取模 / `smoothstep` / 光晕分支的填充成本。`shaders.js` 现在给交互期 `gl.LINES` 一条只输出颜色的 `THIN_LINE_FRAGMENT_SHADER`,并给「全实线、无光晕」的四边形批次一条 `SOLID_LINE_FRAGMENT_SHADER`:跳过虚线取模、`smoothstep` 与光晕分支,只保留颜色与(非 MSAA 帧的)边缘覆盖率;有虚线或热力光晕的批次仍走通用片元。`engine.js` 的 `drawLineBatch` 按批选择,`refreshLineThinLedger` 同时给出是否全实线;线宽、端点圆头、全分辨率与停稳后的 MSAA 静态帧都不变。实测(1280×800,无头 Chrome + ANGLE / AMD D3D11,`quality: 'high'`):10 万点 / 5 万线缩放 p50 **23.2 → 16.7 ms**、p95 **28.6 → 16.8 ~ 16.9 ms**;10 万点 / 10 万线缩放三次中位 p50 **42.6 → 18.0 ms**、p95 **46.3 → 21.4 ms**。16 ~ 18 ms 已接近 60Hz 帧节拍,剩余成本仍是 GPU 填充率;细线快路径的实际门槛是**最大半宽 ≤ 0.6 设备像素且无虚线**(dpr = 1 时约 1.2px 线宽),2px 实线不会走 `gl.LINES`,但轻量四边形片元仍把同一 10 万线场景从约 **82.4 → 63.9 ms**。要继续明显提速,方向是减少可见线段数或屏幕覆盖面积,而不是继续抠 JS。
108
110
 
109
- - 框选提交(`pointerup`)只做必要的工作:选中集每次同步整份换新,抬层集合因此只比引用(没有拖动 / 合并这类额外抬层时直接引用选中集本身,空集合三处共用一份),撤销选中与「同内容再框一次」不再逐个搬 / 逐个比 3.4 万个下标;组件层的选中集读数(id 表、键集合、点 / 线 / 通道条数)在一趟循环里收好并按数组引用缓存,`selectionInfo()` / `selectChanged()` / `selectChange` 载荷直接取现成的;选中集的存活校验走 `isKeyAlive` 的键查表,不再为每个键造对象、投影一次;抬层欠下的整份重排记在 `raiseLayoutPending` 上、由 `flushRaiseLayout` 在下一帧渲染之前补,`pointerup` 回调里不再同步排十万级场景。实测(10 万点 / 10 万线):框住 3.4 万个节点的 `pointerup` 48.7 → 15.7 ms(框小一些的几轮 9.5 / 6.5 / 6.8 ms),提交后第一帧 19.5 ~ 24.2 → 15.6 / 6.7 ~ 9.5 ms。
110
- - 整份重排(`layoutScene`)也少扫几趟十万条:点 glyph 从对象池里复用(逐字段就地改写,不再每轮造十万个对象)、圆点实例用 `writeCircleGlyph` 直写进 `prepareInstances` 的批次数组(不再逐条造 `circleFromGlyph` 的中间对象);没有线路装饰 / 没有跑动动画 / 没有节点阴影 / 没有逐要素状态样式时,各自的十万级空扫直接跳过(`hasLineDecorations` / `hasLineEffects` / `hasPointShadows`,节点侧另有 `hasPointStateStyles()`);细线快路径的账本只在「线宽或虚线开关真的变了」时才标脏;`zIndex` 分层只有一层(绝大多数场景)时不再重铺点顺序。同一份 10 万级的 CPU profile 里 `syncRaiseSets` 6.2 → 0 ms、`buildLineEffectGlyphs` 3.69 → 0 ms、`buildLineDecorationGlyphs` 1.66 → 0.62 ms、`thinLineBatchEligible` 2.2 → 0 ms。
111
+ - 框选提交(`pointerup`)只做必要的工作:选中集每次同步整份换新,抬层集合因此只比引用(没有拖动 / 合并这类额外抬层时直接引用选中集本身,空集合三处共用一份),撤销选中与「同内容再框一次」不再逐个搬 / 逐个比 3.4 万个下标;组件层的选中集读数(id 表、键集合、点 / 线 / 通道条数)在一趟循环里收好并按数组引用缓存,`getSelectionInfo()` / `selectChanged()` / `selectChange` 载荷直接取现成的;选中集的存活校验走 `isKeyAlive` 的键查表,不再为每个键造对象、投影一次;抬层欠下的整份重排记在 `raiseLayoutPending` 上、由 `flushRaiseLayout` 在下一帧渲染之前补,`pointerup` 回调里不再同步排十万级场景。实测(10 万点 / 10 万线):框住 3.4 万个节点的 `pointerup` 48.7 → 15.7 ms(框小一些的几轮 9.5 / 6.5 / 6.8 ms),提交后第一帧 19.5 ~ 24.2 → 15.6 / 6.7 ~ 9.5 ms。
112
+ - 整份重排(`layoutScene`)也少扫几趟十万条:点 glyph 从对象池里复用(逐字段就地改写,不再每轮造十万个对象)、圆点实例用 `writeCircleGlyph` 直写进 `prepareInstancesForLayout` 的批次数组(不再逐条造 `circleFromGlyph` 的中间对象);没有线路装饰 / 没有跑动动画 / 没有节点阴影 / 没有逐要素状态样式时,各自的十万级空扫直接跳过(`hasLineDecorations` / `hasLineEffects` / `hasPointShadows`,节点侧另有 `hasPointStateStyles()`);细线快路径的账本只在「线宽或虚线开关真的变了」时才标脏;`zIndex` 分层只有一层(绝大多数场景)时不再重铺点顺序。同一份 10 万级的 CPU profile 里 `syncRaiseSets` 6.2 → 0 ms、`buildLineEffectGlyphs` 3.69 → 0 ms、`buildLineDecorationGlyphs` 1.66 → 0.62 ms、`thinLineBatchEligible` 2.2 → 0 ms。
111
113
  - 定向更新的线路标签候选复用:`updatePoint` / `patchPoints` 只改节点侧(补丁没动坐标 / id、筛选也没开)时,线路标签层接着用上一轮收好的候选 —— 视口矩形、画布尺寸与「上一轮显示过的下标集合」逐项核对(定义对象是缓存键),十万条线的视口扫描整个省掉;改线路内容 / 挪节点 / 换 id / 筛选联动时判据返回 `undefined`,照旧整层重扫。10 万点 / 10 万条长线 + 两层标签(DOM 75 条)实测单次 `updatePoint` p50 **4.7 → 1.7 ms**,CPU profile 采样 531 → 226、`forEachLineInView` 从热点清单消失。
112
114
  - 超长线段换更粗的一层网格:线段网格原来给「一层里铺不下的长线」只记一条包围盒、查询按盒子先筛,长线密布时几乎等于全量重算;现在每层格子边长 ×4,粗层同样只登记线段真正穿过的格子(跨层 stamp 去重),候选从「包围盒相交」变成「几何上真的经过光标附近」。10 万条随机长线实测一次查询的候选 11101 → **2210** 条、`pick` p50 / p95 / 平均 **0.9 / 6.2 / 1.90 → 0.3 / 1.7 / 0.53 ms**;代价是极限数据集建网格 16.3 → 41.6 ms、堆内存多约 26 MB(短线为主的数据集不变)。
113
115
  - **补点 / 删点不再因为「场景没铺完」回落整份重建**:发散渲染(见《实现原理》15.46)欠着账时,`addPoint` / `addPoints` / `addLines` 原先一律回落整份重解析(几十毫秒),「删一条 → 立刻补一条」这种最常见的串操作因此每一轮都付整份的钱。现在补点这一趟自己把没铺完的那一轮停掉、只补末尾这几条,再按新的解析结果重开一轮;删除那一趟同理(15.89)。5 万点 / 3 万线实测:稳态 `addPoint` 0.4 ~ 1.3 ms,「删一条 → 立刻补一条」29.7 → **1.3 ~ 6.4 ms**。纯删点这一趟还整段跳过通道解析(通道声明逐条没变、没删到回线与锚点时,骨架与全部回线折线照用上一轮的),选中通道时抬层集合也改成按内容比对(内容没变就不再重铺十万条线的批次)。导出这条链顺手切了三刀(空标签表先判空、节点坐标不造中间对象、折线端点共用一个取值回调):50k 点 + 30k 线全量 `exportData()` 中位 22.1 → 21.6 ms、`exportData({ scope: 'viewport' })` 36.7 → 32.5 ms(首跑那一次 36 ~ 38 ms 是 id 表与 JIT 冷着,跑第二遍就落回 20 ms 上下)。
@@ -78,13 +78,13 @@
78
78
  **密度场那一层怎么看都糊成一片 / 看不出色带?** 先看三个数:`threshold`(门槛,抬到 0.15 ~ 0.3 就只留热点,参考图那种「大片浅色 + 白心」就是把门槛抬起来)、`gamma`(对比度,大于 1 把色带一圈圈收在热核外面)、`max`(`'auto'` 是分位膝盖,长尾数据别写死成场的峰值)。再看 `radiusRatio`:太小色斑会碎成一堆点、太大就糊成一整片,缺省 5.5% 是「一眼能看出色斑」的档。数据本身也要有疏密差别 —— 均匀铺满一屏的网格数据,密度场本来就该是平的。
79
79
 
80
80
  **没有通道,能只用点和线吗?**
81
- 可以。`channels` 是可选声明,省略或写 `channels: []` 都能正常运行;`lines` 自己的 `path` / `from` / `to`、`direction`、`lineType` 与新增 / 选中 / 拐点编辑都不依赖通道。交互新增用 `beginAddChannel({ kind: 'line' })`,程序化用 `addLine` / `addLines`,不会往 `channels` 写任何东西。`?demo=connect` 就是从 `channels: []` 开始的纯点 + 线示例。
81
+ 可以。`channels` 是可选声明,省略或写 `channels: []` 都能正常运行;`lines` 自己的 `path` / `from` / `to`、`direction`、`lineType` 与新增 / 选中 / 拐点编辑都不依赖通道。交互新增用 `beginAddLine()`,程序化用 `addLine` / `addLines`,不会往 `channels` 写任何东西。`?demo=connect` 就是从 `channels: []` 开始的纯点 + 线示例。
82
82
 
83
83
  **怎么加一条不挂通道的线路?**
84
- `view.addLine({ from, to })`(等价于 `addChannel({ from, to, kind: 'line' })`),走交互就是 `beginAddChannel({ kind: 'line' })`。它只往 `lines` 里加一条(或 `circuits` 条)普通线路,`channels` 声明一个字节都不动 —— `getChannels()` 里不会出现它,之后也不用担心它被通道分组、拆分、整条通道删除这些操作牵连。反过来 `beginAddChannel()` / `addChannel()` 是新建通道:新签名只负责新建,已经存在通道时要加一组 / 并进某一组用 `addChannelGroupLines` / `joinChannelGroupLines`(旧写法的 `addChannel` 仍按方案表分流)。
84
+ `view.addLine({ from, to })` 新增一条独立线路,走交互就是 `beginAddLine()`;要一次加多回就传第二段 `lineData`(例如 `view.addLine({ from, to }, [{ id: 'l-1' }, { id: 'l-2' }])`)。它只往 `lines` 里加普通线路,`channels` 声明一个字节都不动 —— `getChannels()` 里不会出现它,之后也不用担心它被通道分组、拆分、整条通道删除这些操作牵连。`addLine` 缺省不改变选中集,需要选中新线时传 `{ select: true }`。反过来 `beginAddChannel()` / `addChannel()` 是新建通道:`addChannel` 只负责新建,已经存在通道时要加一组 / 并进某一组用 `addChannelGroupLines` / `joinChannelGroupLines`。
85
85
 
86
86
  **怎么一次给通道加多回?**
87
- `circuits` 就是「这一次加几条」:`beginAddChannel({ kind: 'channel', circuits: 3 })` / `addChannel({ from, to, circuits: 3 })`(旧写法),新建线路也一样(`addLine({ from, to, circuits: 2 })` 一次两条)。方案表的余量按它算 —— 加 3 回时只剩 2 回余量的分组会置灰,`reason` 写清「只剩 2 回余量,本次要加 3 回」;落地回执里 `lines` / `lineIds` 是这一批线,`onChannelDraft` 的 `commit` 载荷里也一样,`removeLine` 一条条摘掉就行(demo 的「移除上一条新增」就是整批摘)。
87
+ 通道草稿和 `commitAddChannel` 用 `circuits` 表示「这一次加几条」;程序化 `addChannel` / `addLine` 的新版入口用第二段 `lineData` 的长度表示回数。例如 `addChannel({ from, to }, [{ id: 'l-1' }, { id: 'l-2' }, { id: 'l-3' }])` 一次建三回,独立线路写成 `addLine({ from, to }, ['l-1', 'l-2'])`。方案表的余量按本次条数算 —— 加 3 回时只剩 2 回余量的分组会置灰,`reason` 写清「只剩 2 回余量,本次要加 3 回」;落地回执里 `lines` / `lineIds` 是这一批线,`onChannelDraft` 的 `commit` 载荷里也一样,`removeLine` 一条条摘掉就行(demo 的「移除上一条新增」就是整批摘)。这些入口缺省都不选中新回线,`options.select: true` 才会选中。
88
88
 
89
89
  **鹰眼图的节点大小 / 范围 / 框选怎么调?**
90
90
  节点大小:`inheritRules` 打开(默认)时,小图取主图最终解析出的尺寸,再乘「小图视口 ÷ 主图视口」的较小比例 —— 只看两边 CSS 视口宽高,不随主图相机倍数变化;仍嫌大就调小图自己的 `pointSizeScale`,或配自己的 `pointRules` / `pointStyle` 接管尺寸。范围:`extent: 'auto'` 默认取数据 ∪ 底图且**不随相机收窄**;想要外围内容消失后跟着收窄才打开 `extentFollowCamera`。框选:`dragAction: 'box'` 不需要打开 `zoomable`,关闭缩放也能在空白处直接框;`zoomable: 'follow'` 是整体范围跟随主图,跟手动缩放 `true` 互斥。
@@ -142,7 +142,10 @@
142
142
  先分清卡的是哪一半:`getStats().frameMs` 量的是引擎自己那一趟(算变换、攒实例、写缓冲、发绘制),十万级下它常年 0.1 ~ 0.8 ms;真正花时间的是**填充率** —— 十万个节点加十几万条线铺满一屏,光栅化与浏览器合成都由 GPU(没有 GPU 时是软件光栅)扛,这一半不在 `frameMs` 里。于是按顺序看四件事:① **总覆盖量**(线的屏幕总长 × 宽度 × 条数,再算上互相重叠)—— 同样 10 万点,网格邻居线(19.9 万条、线短不叠)拖动 p50 只要 7 ms,而「每 5 个点连一条到 0 号点」的长线在圆心附近叠十几层,p50 就是 194 ~ 208 ms;② **MSAA** —— 同一份几何实测 MSAA 4× 是关掉时的近 5 倍(`antialias: false`,软件光栅下引擎已经自动关),读数看 `getStats().antialias`;交互期与动画期还有更省事的一档 `interactionNoMsaa`(缺省开:拖动 / 缩放与动画在跑的那些帧画进不带 MSAA 的后备贴图、收尾整幅贴回,停手 / 停帧后照旧走 MSAA)—— 十万级实测交互期单帧 133 → 36.6 ms、拖动 p50 130 → 16 ~ 22 ms、动画帧 212 → 53 ms,读数看 `getStats().noMsaa`;③ 交互期默认走**细线快路径**(`interactionThinLines`):最大半宽 ≤ 0.6 设备像素(dpr = 1 时约 1.2px 线宽)且没有虚线时,拖动 / 缩放期间改用 `gl.LINES` 画,实测 p50 194 ~ 208 ms → 132 ~ 139 ms、同一窗口帧数 13 → 19 ~ 24,读数看 `getStats().thinLines`;2px 实线不会走这一档,但全实线四边形另有轻量片元;④ 画质档的杠杆是**分辨率**,`fast` 档按 0.75× 渲染(画布像素少一半),同一画面实测 156 → 92 ms/帧,自动档还看整帧间隔(连续画帧时掉到 40fps 以下就逐档往下掉);高分屏 / 全屏下按 `maxCanvasPixels`(缺省 400 万像素预算)压渲染倍率、或把窗口开小,都是直接减填充量。见「画质」与「大数据量」。
143
143
 
144
144
  **一次改很多要素,回调被刷屏、历史被撑爆?**
145
- 把这一段包进 `view.batch(fn, { label })`:收尾只发一条 `type: 'batch'` 的 `onDataChange`(`changes` 按顺序列出每一步),历史里也只占一步,见「批量更新」。
145
+ 把这一段包进 `view.batch(fn, { label })`:收尾只发一条 `type: 'batch'` 的 `onDataChange`(`changes` 按顺序列出每一步),历史里也只占一步。场站 / 线路直接用 `patchPoints` / `patchLines` / `removePoints` / `removeLines`,通道与分组把逐条 `updateChannel` / `removeChannel` / `splitChannelGroups` / `removeChannelGroup` 放进同一个 `batch` 即可,见「批量更新」里的增删改配方。
146
+
147
+ **解除合并之后为什么没有默认选中恢复出来的节点?**
148
+ 这是现在的缺省口径:`splitPoint(source, ...)` 的 `options.select` 默认为 `false`,只把来源节点本身从选中集里摘掉,恢复 / 拆分出来的节点不选中,其它无关选中也不动。旧 Demo 在调用后又手动 `selectFeatures()`,看起来像组件默认选中。需要保持「解除后继续编辑这批节点」时显式传 `select: true`,例如 `view.splitPoint('hub', null, { select: true })`;手工拆分同样支持。
146
149
 
147
150
  **配置写了没反应,控制台也不报错?**
148
151
  听着 `onWarning`(或 `view.on('warning', …)`):配置项的键写错、功能预设 / 画质档位拼错、订阅了不存在的事件名都会点名说一句,同一个错只提醒一次,见「配置提醒」。
@@ -182,12 +185,12 @@
182
185
  | 覆盖层 | `GisViewLabelOptions`、`GisViewLineLabelOptions`、`GisViewItemLabel`、`GisViewLabelState`、`GisViewLabelPatch`、`GisViewSetLabelContentOptions`、`GisViewLabelListItem`、`GisViewItemLabelsInfo`、`GisViewRemoveLabelResult`、`GisViewSetLabelStatesResult`、`GisViewLabelChangePayload`、`GisViewLabelDragPayload`、`GisViewLabelClickPayload`、`GisViewLabelPointerPayload`、`GisViewLabelRef`、`GisViewLabelTarget`、`GisViewLabelBatchEntry`、`GisViewLabelRemoveEntry`、`GisViewLabelBatchSkip`、`GisViewAddLabelsResult`、`GisViewUpdateLabelsResult`、`GisViewRemoveLabelsResult`、`GisViewLineDecoration`、`GisViewTooltipOptions`、`GisViewLabelPayload`、`GisViewLineLabelPayload` |
183
186
  | 筛选 | `GisViewFilter`、`GisViewFilterInput`、`GisViewFilterShorthand`、`GisViewFilterResult`、`GisViewFilterCascade`、`GisViewFilterMode`、`GisViewFilterWorkerOptions` |
184
187
  | 大数据展示模式 | `GisViewDenseMode`、`GisViewDenseModeOptions`、`GisViewDenseModeInput`、`GisViewDenseState` |
185
- | 通道 | `GisViewChannelInput`、`GisViewChannelBuildOptions`、`GisViewChannelBuild`、`GisViewChannelInfo`、`GisViewChannelGroupInfo`、`GisViewChannelGroupWaypoints`、`GisViewChannelGroupLineWaypoints`、`GisViewChannelMember`、`GisViewChannelContext`、`GisViewChannelAddPlan`、`GisViewChannelBetween`、`GisViewChannelAddOption`、`GisViewChannelAddInput`、`GisViewChannelAddCommitInput`、`GisViewChannelAddResult`、`GisViewChannelDraftOptions`、`GisViewChannelDraftPayload`、`GisViewChannelLineMoveTarget`、`GisViewChannelLineMoveResult`、`GisViewRemoveLineResult`、`GisViewRemoveChannelResult`、`GisViewRemoveLinesResult`、`GisViewChannelInView`、`GisViewChannelAction`、`GisViewChannelActionId`、`GisViewChannelDraftStyle`、`GisViewChannelDraftDots`、`GisViewChannelDraftDotStyle`、`GisViewChannelWaypointDragMode`、`createChannels`、`CHANNEL_DEFAULT_COLORS` |
188
+ | 通道 | `GisViewChannelInput`、`GisViewChannelBuildOptions`、`GisViewChannelBuild`、`GisViewChannelInfo`、`GisViewChannelGroupInfo`、`GisViewChannelGroupWaypoints`、`GisViewChannelGroupLineWaypoints`、`GisViewChannelMember`、`GisViewChannelContext`、`GisViewChannelAddPlan`、`GisViewChannelBetween`、`GisViewChannelAddOption`、`GisViewChannelAddSpec`、`GisViewLineAddSpec`、`GisViewChannelLineData`、`GisViewChannelAddGroup`、`GisViewChannelAddResolution`、`GisViewChannelAddCommitInput`、`GisViewChannelAddResult`、`GisViewChannelDraftOptions`、`GisViewChannelDraftPayload`、`GisViewChannelLineMoveTarget`、`GisViewChannelLineMoveResult`、`GisViewRemoveLineResult`、`GisViewRemoveChannelResult`、`GisViewRemoveLinesResult`、`GisViewChannelInView`、`GisViewChannelAction`、`GisViewChannelActionId`、`GisViewChannelDraftStyle`、`GisViewChannelDraftDots`、`GisViewChannelDraftDotStyle`、`GisViewChannelWaypointDragMode`、`createChannels`、`CHANNEL_DEFAULT_COLORS` |
186
189
  | 事件载荷 | `GisViewPickResult`、`GisViewPickAtResult`、`GisViewRectQueryResult`、`GisViewSelectionInfo`、`GisViewSelectedItems`、`GisViewSelectPayload`、`GisViewClickPayload`、`GisViewPointerPayload、GisViewCascadeHighlightScope、GisViewCascadeHighlightOptions、GisViewCascadeHighlightInput、GisViewCascadeHighlightEntry、GisViewCascadeHighlightOrigin、GisViewCascadeHighlightInfo`、`GisViewPointerAction`、`GisViewPointerActionType`、`GisViewPointerTarget`、`GisViewPointerModifiers`、`GisViewWaypointActionData`、`GisViewLabelActionData`、`GisViewHoverPayload`、`GisViewCursorPayload`、`GisViewPointDragPayload`、`GisViewDragPointChange`、`GisViewDragLineChange`、`GisViewPointCopyPayload`、`GisViewPointCopyState`、`GisViewPointMergePayload`、`GisViewMergeImpact`、`GisViewMergeResult`、`GisViewMergeFailure`、`GisViewWaypointPayload`、`GisViewWaypointInfo`、`GisViewWaypointKey`、`GisViewParallelGroup`、`GisViewReadyPayload`、`GisViewEngineStats`、`GisViewEngineHeatDensity`、`GisViewDataChangeType`、`GisViewDataChangePayload`、`GisViewSelectChangePayload`、`GisViewShortcutPayload`、`GisViewRenderPayload`、`GisViewAddItemsResult`、`GisViewAddPointResult` |
187
190
  | 快捷键与复制 / 合并 / 删除 | `GisViewShortcuts`、`GisViewShortcut`、`GisViewShortcutAction`、`GisViewCopyOptions`、`GisViewCopyContext`、`GisViewCopyStartOptions`、`GisViewPasteOptions`、`GisViewCopyItem`、`GisViewClipboardItem`、`GisViewMergeOptions`、`GisViewMergeContext`、`GisViewPointStyleSnapshot`、`GisViewPointRef`、`GisViewMergeResult`、`GisViewMergeFailure`、`GisViewMergeOutcome`、`GisViewRemovePointsOutcome`、`GisViewRemoveSelectedOutcome`、`GisViewRemoveOptions`、`GisViewRemoveScope`、`GisViewRemovePayload`、`GisViewRemoveResult`、`GisViewRemoveOutcome`、`GisViewRemoveFailure`、`GisViewRemoveAction`、`GisViewRemovePoint`、`GisViewRemoveLine`、`GisViewRemoveWaypoint`、`GisViewRemoveGroup`、`GisViewRemoveChannel`、`GisViewRemoveCounts`、`GisViewChannelSelectMode`、`GisViewUpdateItemsOutcome`、`GisViewUpdateItemEntry` |
188
191
  | 相机与视图 | `GisViewCamera`、`GisViewViewport`、`GisViewFocusOptions`、`GisViewFocusTarget`、`GisViewExportImageOptions`、`GisViewExportSelectionOptions`、`GisViewExportSelectionResult` |
189
192
  | 导出数据 | `GisViewExportScope`、`GisViewExportPart`、`GisViewExportDataOptions`、`GisViewExportDataResult`、`GisViewFeatureRef`、`GisViewExportFilterOptions`、`GisViewExportMeta`、`GisViewExportedLabel`、`GisViewStationRecord`、`GisViewPointRecord`、`GisViewLineRecord`、`GisViewChannelRecord`、`GisViewExportFormat`、`GisViewBuiltinExportFormat`、`GisViewExportFormatContext`、`GisViewExportFormatAdapter` |
190
- | 开关 / 历史 / 事件 | `GisViewFeatureName`、`GisViewFeatures`、`GisViewFeatureState`、`GisViewFeaturePreset`、`GisViewFeaturesInput`、`GisViewSelectTrigger`、`FEATURE_NAMES`、`FEATURE_PRESETS`、`FEATURE_PRESET_NAMES`、`FEATURE_EDIT_NAMES`、`featureDefault`、`normalizeFeatures`、`featureOn`、`hasForcedFeatures`、`GisViewHistoryOptions`、`GisViewHistoryState`、`GisViewHistoryEntry`、`GisViewHistoryRecord`、`GisViewHistoryOp`、`GisViewHistoryOpEntry`、`GisViewHistoryRecorder`、`GisViewHistoryHost`、`GisViewHistoryPayload`、`GisViewHistoryStepResult`、`GisViewHistoryAction`、`GisViewHistoryCollection`、`createGisHistory`、`normalizeHistoryOptions`、`applyHistoryOps`、`DEFAULT_HISTORY_LIMIT`、`HISTORY_COLLECTIONS`、`GisViewEventMap`、`GisViewEventName`、`GisViewEventNameInput`、`GisViewEventWildcard`、`GisViewEventListener`、`GisViewAnyEventListener`、`GisViewEventBus`、`GisViewEventBusHost`、`createGisEventBus`、`EVENT_NAMES` |
193
+ | 开关 / 历史 / 事件 | `GisViewFeatureName`、`GisViewFeatures`、`GisViewFeatureState`、`GisViewFeaturePreset`、`GisViewFeaturesInput`、`GisViewSelectTrigger`、`FEATURE_NAMES`、`FEATURE_PRESETS`、`FEATURE_PRESET_NAMES`、`featureDefault`、`normalizeFeatures`、`featureOn`、`hasForcedFeatures`、`GisViewHistoryOptions`、`GisViewHistoryState`、`GisViewHistoryEntry`、`GisViewHistoryRecord`、`GisViewHistoryOp`、`GisViewHistoryOpEntry`、`GisViewHistoryRecorder`、`GisViewHistoryHost`、`GisViewHistoryPayload`、`GisViewHistoryStepResult`、`GisViewHistoryAction`、`GisViewHistoryCollection`、`createGisHistory`、`normalizeHistoryOptions`、`applyHistoryOps`、`DEFAULT_HISTORY_LIMIT`、`HISTORY_COLLECTIONS`、`GisViewEventMap`、`GisViewEventName`、`GisViewEventNameInput`、`GisViewEventWildcard`、`GisViewEventListener`、`GisViewAnyEventListener`、`GisViewEventBus`、`GisViewEventBusHost`、`createGisEventBus`、`EVENT_NAMES` |
191
194
  | 动画效果与预热 | `GisViewLineEffect`、`GisViewLineEffectInput`、`GisViewPointEffect`、`GisViewPointEffectInput`、`GisViewEffectsOptions`、`GisViewEffectsInput`、`GisViewEffectsReadout`、`GisViewLineEffectReadout`、`GisViewPointEffectReadout`、`GisViewPrebakeOptions`、`GisViewPrebakeCombo`、`GisViewPrebakeReadout`、`GisViewSymbolCombos`、`PREBAKE_COMBO_KEYS`、`LINE_EFFECT_DEFAULTS`、`LINE_EFFECT_KEYS`、`POINT_EFFECT_DEFAULTS`、`POINT_EFFECT_KEYS`、`EFFECT_OPTION_KEYS`、`PREBAKE_OPTION_KEYS`、`normalizeLineEffect`、`normalizePointEffect`、`normalizeEffectsOptions`、`normalizePrebakeCombo`、`normalizePrebakeOptions`、`isEffectConfig`、`isEffectsExplicitlyOff`、`wrapPhase`、`phaseSeed`、`polylineLength`、`samplePolyline`、`lineEffectDistance`、`pointEffectPhase`、`sameLineEffect`、`samePointEffect` |
192
195
  | 坐标与映射 | `GisViewGeoProjection`、`GisViewGeoMapper`、`GisViewGeoMapperOptions`、`GisViewPointerState`、`GisViewResolvedGeoJson`、`createGeoProjection`、`createGeoMapper` |
193
196
  | 搜索 | `GisViewSearchOptions`、`GisViewSearchScope`、`GisViewSearchScopeInput`、`GisViewSearchHit`、`GisViewSearchGroupItem`、`GisViewSearchResult`、`GisViewVisiblePoint` |