gis-view-webgl2 0.1.13 → 0.1.15

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 (62) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.d.ts +140 -60
  3. package/dist/index.js +1 -1
  4. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +73 -5
  5. package/docs/GisView.md +49 -49
  6. 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 +87 -16
  7. package/docs/demos/ApiCoverageDemo.vue +1 -5
  8. package/docs/demos/BackendSyncDemo.vue +1 -2
  9. package/docs/demos/BackgroundDemo.vue +68 -1
  10. package/docs/demos/BasicDemo.vue +76 -76
  11. package/docs/demos/BatchAddDemo.vue +2 -1
  12. package/docs/demos/ChannelDemo.vue +1 -3
  13. package/docs/demos/ClusterDemo.vue +48 -1
  14. package/docs/demos/CopyMergeDemo.vue +899 -904
  15. package/docs/demos/DemoShell.vue +336 -336
  16. package/docs/demos/EventDemo.vue +300 -300
  17. package/docs/demos/ExtensionDemo.vue +200 -200
  18. package/docs/demos/GisViewDemo.vue +64 -64
  19. package/docs/demos/LabelDemo.vue +489 -489
  20. package/docs/demos/StyleDemo.vue +67 -67
  21. package/docs/demos/api-index.js +24 -29
  22. package/docs/demos/demos.js +92 -92
  23. package/docs/demos/gis-view-demo/useGisViewDemo.js +19 -14
  24. package/docs/demos/shared/demoData.js +107 -107
  25. package/docs/demos/shared/demoStyle.css +249 -249
  26. package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +3 -3
  27. 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 +3 -0
  28. package/docs/from0/01-/345/205/210/346/212/212/345/234/260/345/233/276/346/203/263/346/270/205/346/245/232.md +8 -7
  29. package/docs/index.html +3 -3
  30. package/docs/internals/04-/347/233/270/346/234/272/345/235/220/346/240/207/344/270/216/345/203/217/347/264/240.md +6 -4
  31. package/docs/internals/14-/350/203/214/346/231/257/345/261/202.md +2 -2
  32. package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +36 -16
  33. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +38 -84
  34. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +36 -19
  35. 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 +1 -1
  36. 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 +7 -7
  37. package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +15 -15
  38. 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 +1 -1
  39. package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +6 -0
  40. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +1 -1
  41. package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +2 -0
  42. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +10 -8
  43. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +11 -7
  44. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +27 -8
  45. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +27 -4
  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 +3 -3
  47. package/docs/playground/index.html +1 -1
  48. package/docs/sample-document/345/255/227/346/256/265/350/257/246/350/247/243.md +2 -2
  49. package/docs/steps/check-doc-truth.mjs +1 -1
  50. package/docs/steps/selftest-actions.html +4 -4
  51. package/docs/steps/selftest-data.html +5 -5
  52. package/docs/steps/selftest-demos.html +1683 -1619
  53. package/docs/steps/selftest-docs.html +4 -4
  54. package/docs/steps/selftest-effects.html +1 -1
  55. package/docs/steps/selftest-features.html +548 -47
  56. package/docs/steps/selftest-full.html +566 -566
  57. package/docs/steps/selftest-labels.html +647 -647
  58. package/docs/steps/selftest-perf.html +2282 -2237
  59. package/docs/steps/selftest-select.html +53 -14
  60. package/docs/steps/step-10-full.js +1 -4
  61. package/docs/steps/step-12-events.js +1 -4
  62. package/package.json +43 -43
@@ -1,23 +1,23 @@
1
- <script setup>
2
- /**
1
+ <script setup>
2
+ /**
3
3
  * 样式与规则演示:同一份数据,换几套规则看画面怎么变,并现场改单个 / 多个要素。
4
- *
4
+ *
5
5
  * 规则就是「条件 + 样式」的数组(`pointRules` / `lineRules`),改完交给 updateOptions 立刻重排;
6
6
  * 普通规则高于 pointStyle / lineStyle、低于要素自身字段(逐点样式),单条规则写 override: true 后压过要素字段;
7
7
  * 悬停 / 选中 / 多选 / 框选 / 级联这些交互样式始终压在最上面,override 规则也盖不住它们写的颜色。
8
- */
9
- import { onBeforeUnmount, onMounted, ref } from 'vue'
10
- import { createGisView } from '../../src/index.js'
11
- import { buildDemoData, VOLTAGE_COLORS } from './shared/demoData.js'
12
- import './shared/demoStyle.css'
13
-
14
- const emit = defineEmits(['notice'])
15
- const container = ref(null)
16
- const dataset = buildDemoData({ count: 24 })
17
- let view = null
18
-
19
- const preset = ref('voltage')
20
- const pointSize = ref(7)
8
+ */
9
+ import { onBeforeUnmount, onMounted, ref } from 'vue'
10
+ import { createGisView } from '../../src/index.js'
11
+ import { buildDemoData, VOLTAGE_COLORS } from './shared/demoData.js'
12
+ import './shared/demoStyle.css'
13
+
14
+ const emit = defineEmits(['notice'])
15
+ const container = ref(null)
16
+ const dataset = buildDemoData({ count: 24 })
17
+ let view = null
18
+
19
+ const preset = ref('voltage')
20
+ const pointSize = ref(7)
21
21
  const lineWidth = ref(1.6)
22
22
  const perPointStyle = ref(false)
23
23
  const ruleOverride = ref(false)
@@ -73,7 +73,7 @@ const interactionStyles = () => highlightOn.value
73
73
  }
74
74
 
75
75
  const cascadeOptions = () => (cascadeOn.value ? { ...CASCADE_STYLES } : false)
76
-
76
+
77
77
  /** 三套预设:按电压上色 / 按负载上色 / 单色 */
78
78
  const withRuleOverride = rules => (ruleOverride.value ? rules.map(rule => ({ ...rule, override: true })) : rules)
79
79
 
@@ -101,16 +101,16 @@ const buildLineRules = () => withRuleOverride(preset.value === 'load'
101
101
  : [])
102
102
 
103
103
  /** 逐点样式:数据里自己写了 color / size 的节点默认压过普通规则;开启强制覆盖后由规则压回来(每 6 个一个金节点) */
104
- const pointsWithOwnStyle = () => (perPointStyle.value
105
- ? dataset.points.map((point, index) => (index % 6 === 0 ? { ...point, color: '#ffd15c', size: 15, selectStyle: { color: '#ff7a45' } } : point))
106
- : dataset.points)
107
-
108
- const buildOptions = () => ({
109
- points: pointsWithOwnStyle(),
110
- lines: dataset.lines,
111
- channels: dataset.channels,
112
- pointStyle: { size: pointSize.value, color: '#ffffff', strokeColor: '#4c8dff', strokeWidth: 1.5 },
113
- lineStyle: { width: lineWidth.value, color: 'rgba(120, 168, 226, 0.5)' },
104
+ const pointsWithOwnStyle = () => (perPointStyle.value
105
+ ? dataset.points.map((point, index) => (index % 6 === 0 ? { ...point, color: '#ffd15c', size: 15, selectStyle: { color: '#ff7a45' } } : point))
106
+ : dataset.points)
107
+
108
+ const buildOptions = () => ({
109
+ points: pointsWithOwnStyle(),
110
+ lines: dataset.lines,
111
+ channels: dataset.channels,
112
+ pointStyle: { size: pointSize.value, color: '#ffffff', strokeColor: '#4c8dff', strokeWidth: 1.5 },
113
+ lineStyle: { width: lineWidth.value, color: 'rgba(120, 168, 226, 0.5)' },
114
114
  pointRules: buildPointRules(),
115
115
  lineRules: buildLineRules(),
116
116
  ...interactionStyles(),
@@ -143,11 +143,11 @@ const buildOptions = () => ({
143
143
  : '级联高亮未触发:点一下画布上的节点或线路'
144
144
  }
145
145
  })
146
-
146
+
147
147
  const syncRules = () => {
148
148
  ruleText.value = `节点规则 ${buildPointRules().length} 条 · 线路规则 ${buildLineRules().length} 条${ruleOverride.value ? ' · 全部 override: true' : ''}`
149
149
  }
150
-
150
+
151
151
  /**
152
152
  * 改规则 / 尺寸 / 高亮:只发样式类配置(不重发 points / lines),
153
153
  * 一次 updateOptions 落到引擎的「重解析 + 槽位覆盖」快路径,顺带报一下这一趟的耗时。
@@ -285,42 +285,42 @@ const syncDetail = () => {
285
285
  `通道全量:getChannels() ${channelCount} 条`
286
286
  ].join(';')
287
287
  }
288
-
289
- onMounted(() => {
290
- view = createGisView(container.value, buildOptions())
291
- })
292
-
293
- onBeforeUnmount(() => {
294
- view?.destroy()
295
- view = null
296
- })
297
- </script><template>
298
- <section class="dv-demo">
299
- <div class="dv-stage">
300
- <div ref="container" class="dv-canvas"></div>
301
- <div class="dv-toolbar">
302
- <button type="button" class="dv-button" @click="view?.fitView(60)">适应全部</button>
303
- </div>
304
- </div>
305
- <aside class="dv-panel">
306
- <div class="dv-group">
307
- <h3>规则预设</h3>
308
- <div class="dv-row">
309
- <label>预设
310
- <select v-model="preset" @change="applyRules">
311
- <option value="voltage">按电压上色(带分层 zIndex)</option>
312
- <option value="load">按负载率上色</option>
313
- <option value="mono">单色</option>
314
- </select>
315
- </label>
316
- </div>
317
- <div class="dv-row">
318
- <label>节点尺寸 <input v-model.number="pointSize" type="range" min="3" max="16" step="1" @change="applyRules" /></label>
319
- <label>线宽 <input v-model.number="lineWidth" type="range" min="0.6" max="6" step="0.2" @change="applyRules" /></label>
320
- </div>
321
- <div class="dv-row">
288
+
289
+ onMounted(() => {
290
+ view = createGisView(container.value, buildOptions())
291
+ })
292
+
293
+ onBeforeUnmount(() => {
294
+ view?.destroy()
295
+ view = null
296
+ })
297
+ </script><template>
298
+ <section class="dv-demo">
299
+ <div class="dv-stage">
300
+ <div ref="container" class="dv-canvas"></div>
301
+ <div class="dv-toolbar">
302
+ <button type="button" class="dv-button" @click="view?.fitView(60)">适应全部</button>
303
+ </div>
304
+ </div>
305
+ <aside class="dv-panel">
306
+ <div class="dv-group">
307
+ <h3>规则预设</h3>
308
+ <div class="dv-row">
309
+ <label>预设
310
+ <select v-model="preset" @change="applyRules">
311
+ <option value="voltage">按电压上色(带分层 zIndex)</option>
312
+ <option value="load">按负载率上色</option>
313
+ <option value="mono">单色</option>
314
+ </select>
315
+ </label>
316
+ </div>
317
+ <div class="dv-row">
318
+ <label>节点尺寸 <input v-model.number="pointSize" type="range" min="3" max="16" step="1" @change="applyRules" /></label>
319
+ <label>线宽 <input v-model.number="lineWidth" type="range" min="0.6" max="6" step="0.2" @change="applyRules" /></label>
320
+ </div>
321
+ <div class="dv-row">
322
322
  <label><input v-model="perPointStyle" type="checkbox" @change="applyPerPointStyle" /> 逐点样式(数据里自己写 color / size)</label>
323
- </div>
323
+ </div>
324
324
  <div class="dv-row">
325
325
  <label><input v-model="highlightOn" type="checkbox" @change="applyRules" /> 悬停 / 选中 / 多选 / 框选</label>
326
326
  <label><input v-model="cascadeOn" data-demo="style-cascade" type="checkbox" @change="applyRules" /> 级联高亮</label>
@@ -390,6 +390,6 @@ onBeforeUnmount(() => {
390
390
  <h3>试一下</h3>
391
391
  <p class="dv-hint">先把预设切到「按电压上色」再切「按负载率」,同一份数据两种读法;同时勾「逐点样式」与「规则强制覆盖」,对比同一批金节点在两种优先级下的颜色 / 尺寸;再选一个节点或线路点 updatePoint / updateLine,最后直接点画布看交互色仍然最高。</p>
392
392
  </div>
393
- </aside>
394
- </section>
395
- </template>
393
+ </aside>
394
+ </section>
395
+ </template>
@@ -7,8 +7,8 @@
7
7
  * 源码或文档改过之后重跑一遍;`node docs/steps/check-doc-coverage.mjs` 会对账。
8
8
  */
9
9
 
10
- /** { members: 283, methods: 278, properties: 5, options: 107, sections: 16 } */
11
- export const API_SUMMARY = {"members":283,"methods":278,"properties":5,"options":107,"sections":16}
10
+ /** { members: 285, methods: 280, properties: 5, options: 100, sections: 16 } */
11
+ export const API_SUMMARY = {"members":285,"methods":280,"properties":5,"options":100,"sections":16}
12
12
 
13
13
  /** 实例成员,按 docs/GisView.md「参数速查」的分组排 */
14
14
  export const API_SECTIONS = [
@@ -18,8 +18,8 @@ export const API_SECTIONS = [
18
18
  { name: "updateOptions", kind: "method", params: "options: GisViewOptions 要合并的配置(按顶层键浅合并:写 points / lines / channels 就是整份换数据,写 label / pointStyle 就是整份换这一类配置);replace: boolean 传 true 时整份替换而不是合并,没写的键回到内置默认", returns: "this", doc: "合并配置(replace 为 true 时整体替换)", examples: [{"group":"配置与数据","code":"// updateOptions(options, replace?):合并配置;replace 传 true 整份替换(没写的键回到内置默认)\nview.updateOptions({ showGrid: false, zoomStep: 1.6 })\nview.updateOptions({ points: nextPoints, lines: nextLines }, true)\n","from":"header"},{"group":"相机与视图","code":"// getEffects() / updateOptions({ effects }):全局动画配置(effects: false = 一处关掉),写法与 options.effects 一致\nview.updateOptions({ effects: false })\nview.updateOptions({ effects: { line: { size: 10, speed: 60 }, point: { radius: 30 } } })\nconst effects = view.getEffects()\n","from":"header"}] },
19
19
  { name: "updatePointStyle", kind: "method", params: "patch: GisViewPointStyle | null 要改的点样式字段:按字段合并(只动给出来的键,其余保持 —— updatePointStyle({ color: '#f60' }) 不会把 size 碰掉);写 null 清掉整份全局点样式(等于 updateOptions({ pointStyle: null })),写别的非对象值本次忽略并报一句 onWarning;不认识的字段名照样点名。要与 updateOptions 一起下发时直接写 pointStyle(整份语义)", returns: "this", doc: "改全局点 / 线样式的某几个字段(字段级合并,不写的保持原来的那一份):updateOptions({ pointStyle }) 是整份换(没写的字段等于回到内置默认),这两个只动给出来的键 —— 面板上拖大小 / 换调色板一个键一条,不会把样式表里别的配置碰掉;传 null 清掉整份全局样式,写错的字段名走 onWarning 点名。只改尺寸链(size / sizeScale)或配色链(color / strokeColor / strokeWidth / opacity)时引擎走「逐要素只重算这条链」的窄路,逐点自己定死了这一项的要素连槽位都不重写,见「大数据量」", examples: [{"group":"配置与数据","code":"// updatePointStyle(patch):按字段合并全局点样式;写 null 清掉整份\nview.updatePointStyle({ color: '#f60', size: 9 })\nview.updatePointStyle(null)\n","from":"header"}] },
20
20
  { name: "updateLineStyle", kind: "method", params: "patch: GisViewLineStyle | null 线的一份,口径与 updatePointStyle 完全一致", returns: "this", doc: "改全局点 / 线样式的某几个字段(字段级合并,不写的保持原来的那一份):updateOptions({ pointStyle }) 是整份换(没写的字段等于回到内置默认),这两个只动给出来的键 —— 面板上拖大小 / 换调色板一个键一条,不会把样式表里别的配置碰掉;传 null 清掉整份全局样式,写错的字段名走 onWarning 点名。只改尺寸链(size / sizeScale)或配色链(color / strokeColor / strokeWidth / opacity)时引擎走「逐要素只重算这条链」的窄路,逐点自己定死了这一项的要素连槽位都不重写,见「大数据量」", examples: [{"group":"配置与数据","code":"// updateLineStyle(patch):与 updatePointStyle 同一套\nview.updateLineStyle({ width: 3, color: '#1e88e5' })\n","from":"header"}] },
21
- { name: "getOptions", kind: "method", params: "无", returns: "GisViewOptions", doc: "当前配置与数据的副本", examples: [{"group":"配置与数据","code":"// getOptions() / getData():整份配置 / 整份数据(读到的就是组件内部那一份,只读)\nconst options = view.getOptions()\nconst { points, lines, channels } = view.getData()\n","from":"header"}] },
22
- { name: "getData", kind: "method", params: "无", returns: "GisViewData", doc: "当前数据副本(点与线两列,含拖拽点之后的最新坐标);通道声明不在这一份里 —— 读整份配置与数据用 getOptions()(getOptions().channels)、存档 / 回灌用 exportData({ format: 'input' })", examples: [{"group":"配置与数据","code":"// getOptions() / getData():整份配置 / 整份数据(读到的就是组件内部那一份,只读)\nconst options = view.getOptions()\nconst { points, lines, channels } = view.getData()\n","from":"header"}] },
21
+ { name: "getOptions", kind: "method", params: "无", returns: "GisViewOptions", doc: "当前配置与数据的副本", examples: [{"group":"配置与数据","code":"// getOptions() / getData():整份配置 / 点线数据(读到的就是组件内部那一份,只读)\nconst options = view.getOptions()\nconst { points, lines } = view.getData()\nconst channels = options.channels // 通道声明从 getOptions() 读,不在 getData() 里\n","from":"header"}] },
22
+ { name: "getData", kind: "method", params: "无", returns: "GisViewData", doc: "当前数据副本(点与线两列,含拖拽点之后的最新坐标);通道声明不在这一份里 —— 读整份配置与数据用 getOptions()(getOptions().channels)、存档 / 回灌用 exportData({ format: 'input' })", examples: [{"group":"配置与数据","code":"// getOptions() / getData():整份配置 / 点线数据(读到的就是组件内部那一份,只读)\nconst options = view.getOptions()\nconst { points, lines } = view.getData()\nconst channels = options.channels // 通道声明从 getOptions() 读,不在 getData() 里\n","from":"header"}] },
23
23
  { name: "updateData", kind: "method", params: "整份换数据:points?: GisViewPoint[] 新的节点数组(不传就不动);lines?: GisViewLine[] 新的线路数组(不传就不动);channels?: GisViewChannel[] 给了就连通道声明一起换(切场景一次换完、只发一条 dataChange)。也收单对象写法 updateData({ points, lines, channels })(只换给出的那几列;只换通道声明写 updateData({ channels }));接口字段先在调用方映射成这三列,见「动态换数据与加载完成」", returns: "this", doc: "整份换数据:只给两个参数时换点与线(通道不动);给 channels 时连通道一起换(切场景一次换完、只发一条 onDataChange);也收单对象写法 updateData({ points, lines, channels }),只换给出的那几列 —— 只换通道声明写 updateData({ channels }),只换节点 / 线路写 updateData({ points }) / updateData({ lines })。接口字段先在调用方映射成这三列,见「动态换数据与加载完成」", examples: [{"group":"配置与数据","code":"// updateData(points, lines, channels?):整份换数据,不传的那一列不动\nview.updateData(nextPoints, nextLines) // 只换点与线\nview.updateData(nextPoints, nextLines, nextChannels) // 切场景一次换完,只发一条 dataChange\nview.updateData({ lines: nextLines, channels: nextChannels }) // 单对象写法:只换给出的那几列\n","from":"header"},{"group":"配置与数据","code":"// 只换一列也走 updateData 的单对象写法(没给的那几列不动)\nview.updateData({ channels: [{ id: 'c1', name: '二号通道', lineIds: ['l1', 'l2'] }] })\nview.updateData({ channels: [] }) // 清空通道声明\nview.updateData({ points: [{ id: 'a', x: 0, y: 0 }, { id: 'b', x: 400, y: 200 }] })\nview.updateData({ lines: [{ id: 'l1', from: 'a', to: 'b' }] })\n","from":"header"}] },
24
24
  { name: "importData", kind: "method", params: "input: GisViewDocumentInput:标准文档(format: 'gis-view')/ 与创建入参同构的数据对象(points / lines / channels / labelStates / view / filter,给哪列出哪列)/ 节点数组;options 可以把某一列(points / lines / channels / labelStates / view / filter)显式点上,checkKeys: false 不点名多余的顶层键", returns: "GisViewImportResult", doc: "整份喂回一份数据 / 一份标准文档(exportData 的五种格式都认,最标准的是 format: 'full' 那一份):顶层出哪一列就应用哪一列(points / lines / channels / labelStates / view / filter),没出的列一个都不动;只给通道(不带点线)时单独换通道声明,只给 labelStates 时只动标签状态(整份语义)。返回 { ok, reason, applied, counts, warnings },见「标准数据文档(full):接收 == 导出」", examples: [{"group":"配置与数据","code":"// importData(input, options?):标准文档 / 与创建入参同构的对象 / 节点数组都能灌\nview.importData({ format: 'gis-view', points: nextPoints, lines: nextLines, channels: nextChannels })\nview.importData(nextPoints) // 只灌节点数组\nview.importData({ points: nextPoints, view: { x: 10, y: 20, scale: 8 } }) // 连视角一起\nconst imported = view.importData(doc, { checkKeys: false }) // → { ok, reason, applied, counts, warnings }\n","from":"header"}] },
25
25
  { name: "updatePoint", kind: "method", params: "id: string | number 节点 id;patch: Partial<GisViewPoint> 要合并的字段(写 x / y 就是移动,写 color / size / symbol / strokeColor / strokeWidth 就是改这一条的样式,写 null 摘掉这一项;写 visible: false 把这一条从图上收起、写 visible: true 排回来 —— 这一条要连绑定线 / 锚定通道一起重排,因此走整份重推一次,batch() 里一批只推一次)。命中的 override: true 规则会压住同字段的逐条补丁", returns: "this", doc: "定向更新一个节点 / 线路 / 通道并重渲染:单项字段(尺寸、图标、颜色、线宽、通道宽度 / 名称等)直接写补丁;只重排受影响的那一小片(节点 → 绑定线与锚定通道(尺寸 / 图标换过时,锚定通道的宽度一起按新尺寸重排,见「通道与多回线」),线路 → 这一条,通道 → 它的分组与成员),其余要素与缓存一个字节都不动;节点的 visible: false / 改回来会走一次整份重排(这一条要连绑定线、锚定通道与拾取索引一起退场 / 排回去,定向更新那条路落不下;同一个 batch() 里改一批只重排一次);updateLine 的补丁里还能写 groupId / groupIndex(可配 channelId)顺带把这条回线挪到目标分组,updateChannel 写 groups / lineIds 会顺带改分组并补一条 channelGroups 变更(见「通道分组」);updateChannel 还能给第三段 linePatches(一条或一批 { id | index, ...字段 })把「通道声明 + 成员回线各自的字段 / 分组」一次下发 —— 声明先落、再逐条走 updateLine,整次调用只发一条 batch 数据变更、历史里只算一步(见「通道分组」与「逐方法示例」);第三段里认不出的成员 id 带得起定义(from / to 两端可解析、可用的 path、或通道自己声明了两端)会当场建一条回线并挂进通道,给不出定义才跳过点名;updateLine 的拐点写在 path 上(整条折线,含首尾端点 —— 只写中间拐点用 setLineWaypoints(id, coordinates)),通道里的回线几何由通道骨架推导(path 写了也会被覆盖),改它走 setLineWaypoints / setChannelPath", examples: [{"group":"配置与数据","code":"// updatePoint(id, patch):合并一个节点的字段;visible 翻面会连绑定线 / 锚定通道一起重排\nview.updatePoint('a', { x: 120, y: 40 })\nview.updatePoint('a', { name: '甲站(改)', status: 2 })\nview.updatePoint('a', { color: '#ff3b30', size: 18, symbol: '/marker.svg', strokeColor: '#fff', strokeWidth: 2 }) // 改这一条的颜色 / 尺寸 / 图标\nview.updatePoint('a', { symbol: { type: 'svg', path: 'M12 1 L23 12 L12 23 L1 12 Z', viewBox: [0, 0, 24, 24] } })\nview.updatePoint('a', { color: null, size: null, symbol: null }) // 摘掉逐条覆盖,回落到普通规则 / pointStyle\nview.updatePoint('a', { visible: false }) // 从图上收起;写回 true 排回来\n","from":"header"},{"group":"定向改一个要素(一次只动一条)","code":"// updatePoint(id, patch):只写要改的字段(size / symbol / color…)\nview.updatePoint('a', { size: 12 })\nview.updatePoint('a', { symbol: { kind: 'square' } })\nview.updatePoint('a', { symbol: 'https://example.com/pin.png' })\nview.updatePoint('a', { symbol: null }) // 回到内置圆\nview.updatePoint('a', { color: '#ff6b35' })\n","from":"header"}] },
@@ -39,7 +39,7 @@ export const API_SECTIONS = [
39
39
  { name: "refresh", kind: "method", params: "options?: { reflow?: boolean } 传 false 只重画一帧(跳过整份重解析:数据没变、只想重画时用);不传或 true 走整份重建", returns: "this", doc: "手动同步尺寸 / 重新应用配置并重绘(refresh({ reflow: false }) 只重画一帧,不重解析数据与配置);容器尺寸或祖先的 transform: scale 变过之后要调一次 resize() 同步(CSS zoom 会自己感知)", examples: [{"group":"配置与数据","code":"// refresh(options?):整份重建;refresh({ reflow: false }) 只重画一帧\nview.refresh()\nview.refresh({ reflow: false })\n","from":"header"}] },
40
40
  { name: "whenRendered", kind: "method", params: "等下一帧画完(Promise):兑现那一帧的载荷(frame 是帧序号,type / changes / counts 说明这一帧带上了哪几次数据变更);引擎下一次渲染结束时兑现,视图销毁时兑现 null", returns: "Promise<GisViewDataRenderedPayload | null>", doc: "/** * 等下一帧画完(Promise)。", examples: [{"group":"配置与数据","code":"// whenRendered():等下一帧画完(载荷是这一帧的读数:frame / type / changes / counts)\nconst frame = await view.whenRendered()\n","from":"header"}] },
41
41
  { name: "whenDataRendered", kind: "method", params: "等「刚换的这一份数据画出来」(Promise):有挂起的数据变更时与 whenRendered 同一条路;没有挂起的变更时立刻兑现 null。view.updateData(payload); await view.whenDataRendered(); loading = false 是收 loading 的标准写法", returns: "Promise<GisViewDataRenderedPayload | null>", doc: "/** * 等「刚换的这一份数据画出来」(Promise)。", examples: [{"group":"配置与数据","code":"// whenDataRendered():等「刚换的这份数据画完」—— 收 loading 遮罩的标准写法\nview.updateData(nextPoints, nextLines)\nawait view.whenDataRendered()\n","from":"header"}] },
42
- { name: "snapshotScene", kind: "method", params: "options?: GisViewSceneSnapshotOptions 快照选项(要哪些读数 / 哪些数据)", returns: "GisViewSceneSnapshot | null", doc: "当前画面的矢量快照(屏幕坐标的线 / 点 / 标签 / 背景 / 网格):{ width, height, background, backgroundImage, grid, regions, lines, raisedLines, points, backgroundPoints, decorations, waypoints, raised, labels };raisedLines 是抬层的选中线路 / 通道,导出时排在普通节点之后、raised 端点节点之前。options.background: false 丢背景、options.backgroundColor 只换底色、options.labels 原样带进去,见「导出图片」", examples: [{"group":"配置与数据","code":"// snapshotScene(options?):一次取全画布读数 / 数据快照(矢量导出、单测都用它;raisedLines 是抬层线路)\nconst snapshot = view.snapshotScene()\nconst white = view.snapshotScene({ background: '#ffffff' })\n","from":"header"}] },
42
+ { name: "snapshotScene", kind: "method", params: "options?: GisViewSceneSnapshotOptions 快照选项(要哪些读数 / 哪些数据)", returns: "GisViewSceneSnapshot | null", doc: "当前画面的矢量快照(屏幕坐标的线 / 点 / 标签 / 背景 / 网格):{ width, height, background, backgroundImage, grid, regions, lines, raisedLines, points, backgroundPoints, decorations, raisedDecorations, waypoints, raised, labels };raisedLines 是抬层的选中线路 / 通道,raisedDecorations 是跟着选中线路 / 节点 / 选中线路端点抬层的普通装饰。导出次序为普通装饰 → raisedLines → raisedDecorations → raised,见「导出图片」", examples: [{"group":"配置与数据","code":"// snapshotScene(options?):一次取全画布读数 / 数据快照(矢量导出、单测都用它;raisedLines 是抬层线路)\nconst snapshot = view.snapshotScene()\nconst white = view.snapshotScene({ background: '#ffffff' })\n","from":"header"}] },
43
43
  { name: "destroy", kind: "method", params: "无", returns: "无", doc: "销毁引擎并移除根节点", examples: [{"group":"配置与数据","code":"// destroy():卸载(事件、观察器、WebGL 上下文一起清)\nview.destroy()","from":"header"}] },
44
44
  ]
45
45
  },
@@ -101,13 +101,15 @@ export const API_SECTIONS = [
101
101
  { name: "worldToClient", kind: "method", params: "x / y 世界坐标", returns: "[number, number] | null 客户端坐标(摆 DOM 浮层 / 菜单用)", doc: "鼠标事件坐标 ↔ 世界坐标:自动处理 canvas rect 与宿主缩放(CSS zoom),自建 DOM 浮层 / 菜单定位用它;只要画布内像素就用 project / unproject", examples: [{"group":"坐标、拾取与读数","code":"// clientToWorld(clientX, clientY) / worldToClient(x, y):客户端坐标 ↔ 世界坐标(省掉中间一步)\nconst worldUnderCursor = view.clientToWorld(640, 360)\nconst [clientX, clientY] = view.worldToClient(120, 80) // 摆 DOM 浮层 / 菜单\n","from":"header"}] },
102
102
  { name: "clientToScreen", kind: "method", params: "clientX: number / clientY: number 鼠标事件的客户端坐标", returns: "[number, number] | null 画布 CSS 像素(引擎不在时 null)", doc: "鼠标事件坐标 ↔ 画布 CSS 像素:pick / getContextAt / nearestPointsAt / setDropPreview({ screen }) 这些吃画布像素的方法先用它转;自动处理 canvas rect 与宿主缩放,别自己写 clientX - rect.left", examples: [{"group":"坐标、拾取与读数","code":"// clientToScreen(clientX, clientY) / screenToClient(x, y):客户端坐标 ↔ 画布 CSS 像素","from":"header"}] },
103
103
  { name: "screenToClient", kind: "method", params: "x / y 画布 CSS 像素", returns: "[number, number] | null 客户端坐标(摆 position: fixed 的浮层 / 菜单用)", doc: "鼠标事件坐标 ↔ 画布 CSS 像素:pick / getContextAt / nearestPointsAt / setDropPreview({ screen }) 这些吃画布像素的方法先用它转;自动处理 canvas rect 与宿主缩放,别自己写 clientX - rect.left", examples: [{"group":"坐标、拾取与读数","code":"// clientToScreen(clientX, clientY) / screenToClient(x, y):客户端坐标 ↔ 画布 CSS 像素","from":"header"}] },
104
- { name: "getGeoProjection", kind: "method", params: "无", returns: "GisViewGeoProjection | null", doc: "当前 GeoJSON 底图的投影 { rawBounds, bounds, aspectScale, center, project, unproject },没有 GeoJSON 背景时是 null", examples: [{"group":"坐标、拾取与读数","code":"// getGeoProjection():底图的投影读数(没配 GeoJSON 背景时返回 null)\nconst geo = view.getGeoProjection()\n","from":"header"}] },
105
- { name: "lngLatToWorld", kind: "method", params: "lng: number 经度;lat: number 纬度", returns: "[number, number] | null", doc: "经纬度与画出来的世界坐标互转(含 aspectScale);没有 GeoJSON 背景时返回 null,见「经纬度与数据映射」", examples: [{"group":"坐标、拾取与读数","code":"// lngLatToWorld(lng, lat) / worldToLngLat(x, y):经纬度 ↔ 世界坐标(没配 GeoJSON 时返回 null)\nconst worldOfLngLat = view.lngLatToWorld(116.4, 39.9)\nconst lngLatOfWorld = view.worldToLngLat(120, 80)\n","from":"header"}] },
106
- { name: "worldToLngLat", kind: "method", params: "x / y 世界坐标", returns: "[number, number] | null", doc: "经纬度与画出来的世界坐标互转(含 aspectScale);没有 GeoJSON 背景时返回 null,见「经纬度与数据映射」", examples: [{"group":"坐标、拾取与读数","code":"// lngLatToWorld(lng, lat) / worldToLngLat(x, y):经纬度 ↔ 世界坐标(没配 GeoJSON 时返回 null)\nconst worldOfLngLat = view.lngLatToWorld(116.4, 39.9)\nconst lngLatOfWorld = view.worldToLngLat(120, 80)\n","from":"header"}] },
104
+ { name: "getGeoProjection", kind: "method", params: "无", returns: "GisViewGeoProjection | null", doc: "当前 GeoJSON 底图的投影 { rawBounds, bounds, aspectScale, center, scaleX, scaleY, project, unproject };bounds 与正反换算都按最终画面,没有 GeoJSON 背景时是 null", examples: [{"group":"坐标、拾取与读数","code":"// getGeoProjection():最终底图的投影读数,已含 aspectScale / bounds / rotation(没配 GeoJSON 背景时返回 null)\nconst geo = view.getGeoProjection()\n","from":"header"}] },
105
+ { name: "lngLatToWorld", kind: "method", params: "lng: number 经度;lat: number 纬度", returns: "[number, number] | null", doc: "经纬度与最终画出来的世界坐标互转(含 aspectScale、geoJsonStyle.bounds 与 rotation);没有 GeoJSON 背景时返回 null,见「经纬度与数据映射」", examples: [{"group":"坐标、拾取与读数","code":"// lngLatToWorld(lng, lat) / worldToLngLat(x, y):经纬度 ↔ 最终世界坐标(没配 GeoJSON 时返回 null)\nconst worldOfLngLat = view.lngLatToWorld(116.4, 39.9)\nconst lngLatOfWorld = view.worldToLngLat(120, 80)\n","from":"header"}] },
106
+ { name: "worldToLngLat", kind: "method", params: "x / y 世界坐标", returns: "[number, number] | null", doc: "经纬度与最终画出来的世界坐标互转(含 aspectScale、geoJsonStyle.bounds 与 rotation);没有 GeoJSON 背景时返回 null,见「经纬度与数据映射」", examples: [{"group":"坐标、拾取与读数","code":"// lngLatToWorld(lng, lat) / worldToLngLat(x, y):经纬度 ↔ 最终世界坐标(没配 GeoJSON 时返回 null)\nconst worldOfLngLat = view.lngLatToWorld(116.4, 39.9)\nconst lngLatOfWorld = view.worldToLngLat(120, 80)\n","from":"header"}] },
107
107
  { name: "lngLatToScreen", kind: "method", params: "lng / lat 经纬度", returns: "[number, number] | null", doc: "经纬度与画布 CSS 像素互转;没有 GeoJSON 背景时返回 null", examples: [{"group":"坐标、拾取与读数","code":"// lngLatToScreen(lng, lat) / screenToLngLat(x, y):经纬度 ↔ 画布 CSS 像素\nconst screenOfLngLat = view.lngLatToScreen(116.4, 39.9)\nconst lngLatUnder = view.screenToLngLat(320, 200)\n","from":"header"}] },
108
108
  { name: "screenToLngLat", kind: "method", params: "x / y 画布 CSS 像素", returns: "[number, number] | null", doc: "经纬度与画布 CSS 像素互转;没有 GeoJSON 背景时返回 null", examples: [{"group":"坐标、拾取与读数","code":"// lngLatToScreen(lng, lat) / screenToLngLat(x, y):经纬度 ↔ 画布 CSS 像素\nconst screenOfLngLat = view.lngLatToScreen(116.4, 39.9)\nconst lngLatUnder = view.screenToLngLat(320, 200)\n","from":"header"}] },
109
- { name: "createGeoMapper", kind: "method", params: "options?: GisViewGeoMapperOptions 映射选项(lngKey / latKey / xKey / yKey / fit…)", returns: "GisViewGeoMapper | null", doc: "建一个「数据坐标 → GeoJSON 坐标」的映射器:mode: 'lngLat' | 'fit' | 'scale'、scale(节点与 geojson 的大小比例)、scaleX / scaleY / offsetX / offsetY / center / bounds / data / padding / target,返回 map / unmap / mapPoint / mapLine / mapChannel / mapData 与最终的比例偏移;没有 GeoJSON 背景时是 null", examples: [{"group":"坐标、拾取与读数","code":"// createGeoMapper(options?) / mapDataToGeoJson(data, options?):把另一套坐标系的数据搬到画出来的底图上\nconst mapper = view.createGeoMapper({ mode: 'fit', padding: 0.08, data })\nconst mappedPoints = view.getData().points.map(mapper.mapPoint)\nconst mapped = view.mapDataToGeoJson({ points, lines, channels }, { mode: 'fit' })\nview.updateData(mapped.points, mapped.lines)\n","from":"header"}] },
110
- { name: "mapDataToGeoJson", kind: "method", params: "data: { points?, lines?, channels? } 要搬的数据;options?: GisViewGeoMapperOptions 同 createGeoMapper", returns: "GisViewPoint[]; lines?: GisViewLine[]; channels?: GisViewChannel[] }>(data: T, options?: GisViewGeoMapperOptions): T | null", doc: "createGeoMapper(options).mapData(data) 的快捷写法:把 { points, lines, channels } 整份搬到 GeoJSON 坐标,原数据不动;没有 GeoJSON 背景时是 null", examples: [{"group":"坐标、拾取与读数","code":"// createGeoMapper(options?) / mapDataToGeoJson(data, options?):把另一套坐标系的数据搬到画出来的底图上\nconst mapper = view.createGeoMapper({ mode: 'fit', padding: 0.08, data })\nconst mappedPoints = view.getData().points.map(mapper.mapPoint)\nconst mapped = view.mapDataToGeoJson({ points, lines, channels }, { mode: 'fit' })\nview.updateData(mapped.points, mapped.lines)\n","from":"header"}] },
109
+ { name: "createGeoMapper", kind: "method", params: "options?: GisViewGeoMapperOptions 映射选项(mode: 'lngLat' | 'fit' | 'world' | 'scale' / scale / scaleX / scaleY / offsetX / offsetY / center / bounds / data / padding / target)", returns: "GisViewGeoMapper | null", doc: "建一个「数据坐标 → 最终 GeoJSON 坐标」的映射器:mode: 'lngLat' | 'fit' | 'world' | 'scale'、scale(节点与 geojson 的大小比例)、scaleX / scaleY / offsetX / offsetY / center / bounds / data / padding / target,返回 map / unmap / mapPoint / mapLine / mapChannel / mapData 与最终的比例偏移;lngLat / scale 以最终底图为基准,world 把画出来的世界坐标反投影成真实经纬度,坐标认 [x, y] / { x, y },mapLine 同时换 path / start / end 与固定坐标装饰,mapChannel 还换分组 path 并把 waypointOffsets 当向量换算;没有 GeoJSON 背景时是 null", examples: [{"group":"坐标、拾取与读数","code":"// createGeoMapper(options?) / mapDataToGeoJson(data, options?):把另一套坐标系的数据搬到最终底图上\nconst mapper = view.createGeoMapper({ mode: 'fit', padding: 0.08, data })\nconst mappedPoints = view.getData().points.map(mapper.mapPoint)\nconst mapped = view.mapDataToGeoJson({ points, lines, channels }, { mode: 'fit' })\nview.updateData(mapped.points, mapped.lines)\n","from":"header"}] },
110
+ { name: "mapDataToGeoJson", kind: "method", params: "data: { points?, lines?, channels? } 要搬的数据;options?: GisViewGeoMapperOptions 同 createGeoMapper", returns: "{ points?, lines?, channels? } 的复制(泛型 T);没有 GeoJSON 背景时 null", doc: "createGeoMapper(options).mapData(data) 的快捷写法:把 { points, lines, channels } 整份搬到 GeoJSON 坐标,原数据不动;没有 GeoJSON 背景时是 null", examples: [{"group":"坐标、拾取与读数","code":"// createGeoMapper(options?) / mapDataToGeoJson(data, options?):把另一套坐标系的数据搬到最终底图上\nconst mapper = view.createGeoMapper({ mode: 'fit', padding: 0.08, data })\nconst mappedPoints = view.getData().points.map(mapper.mapPoint)\nconst mapped = view.mapDataToGeoJson({ points, lines, channels }, { mode: 'fit' })\nview.updateData(mapped.points, mapped.lines)\n","from":"header"}] },
111
+ { name: "mapDataToLngLat", kind: "method", params: "data?: { points?, lines?, channels? } 不传时取视图当前数据;options?: GisViewGeoMapperOptions 额外比例 / 偏移,mode 固定为 'world'", returns: "{ points?, lines?, channels? } 的复制(泛型 T);没有 GeoJSON 背景时 null", doc: "把当前网架的世界坐标整份转成 GeoJSON 经纬度:不传 data 时取视图当前 points / lines / channels,使用调用当时最终画出来的底图投影(含 aspectScale、背景编辑后的 bounds 与 rotation);返回副本、不改原数据和视图,没有 GeoJSON 背景时是 null。不自动 updateData(),适合导出 / 持久化", examples: [{"group":"坐标、拾取与读数","code":"// mapDataToLngLat(data?, options?):背景编辑之后把网架当前世界坐标整份转成 GeoJSON 经纬度\nconst lngLatData = view.mapDataToLngLat() // 不传 data = 当前 points / lines / channels;返回副本\nconst lngLatSubset = view.mapDataToLngLat({ points: view.getData().points }) // 只转其中一列\n","from":"header"}] },
112
+ { name: "mapLngLatToData", kind: "method", params: "data: { points?, lines?, channels? } 的经纬度存档;options?: GisViewGeoMapperOptions 额外比例 / 偏移,mode 固定为 'lngLat'", returns: "{ points?, lines?, channels? } 按当前底图投影写回世界坐标后的复制(泛型 T);没有 GeoJSON 背景时 null。常用 view.updateData(view.mapLngLatToData(saved))", doc: "mapDataToLngLat() 的逆变换:把 x / y 存着经纬度的 points / lines / channels 按当前底图投影写回世界坐标,覆盖线路端点 / 路径、固定坐标装饰、通道分组路径与 waypointOffsets 向量;返回副本、不改入参,没有 GeoJSON 背景时是 null。存档回灌写成 view.updateData(view.mapLngLatToData(saved)),不要直接把经纬度数据 updateData()", examples: [{"group":"坐标、拾取与读数","code":"// mapLngLatToData(data, options?):mapDataToLngLat 的逆变换,把保存的经纬度按当前底图写回世界坐标\nconst restoredData = view.mapLngLatToData(lngLatData)\nview.updateData(restoredData) // 底图未变时世界坐标与屏幕落点原样恢复\n","from":"header"}] },
111
113
  { name: "getPointer", kind: "method", params: "无", returns: "GisViewPointerState", doc: "当前光标状态 { inside, screen, world, lngLat }(onPointer 回调的同一份数据)", examples: [{"group":"坐标、拾取与读数","code":"// getPointer():光标位置读数 { inside, screen, world, lngLat }\nconst pointer = view.getPointer()\n","from":"header"}] },
112
114
  { name: "pick", kind: "method", params: "x / y 画布 CSS 像素", returns: "GisViewPickResult | null", doc: "拾取指定画布位置上的要素(clickable: false 的要素不会返回)", examples: [{"group":"坐标、拾取与读数","code":"//(pick / getContextAt / nearestPointsAt 这些吃画布像素的方法先用这一对转;自动处理 canvas rect 与宿主缩放)\nconst [sx, sy] = view.clientToScreen(event.clientX, event.clientY)\nconst [fixedX, fixedY] = view.screenToClient(320, 200) // 摆 position: fixed 的浮层 / 菜单\n","from":"header"},{"group":"坐标、拾取与读数","code":"// pick(x, y) / pickAt(clientX, clientY):按画布像素 / 客户端坐标拾取\nconst hit = view.pick(320, 200)\nconst hitAt = view.pickAt(event.clientX, event.clientY)\nhitAt?.groups // 命中通道回线:同一通道的全部分组;hitAt.group 是当前这一组\n","from":"header"}] },
113
115
  { name: "pickAt", kind: "method", params: "clientX / clientY 浏览器客户端坐标(PointerEvent 原样给);命中通道回线时返回的 group 是当前组、groups 是整条通道的全部分组", returns: "GisViewPickAtResult | null", doc: "按浏览器客户端坐标拾取(就是鼠标事件里的 clientX / clientY):与指针事件载荷同一份结果(type / index / item / distance / screen / coordinate 与通道字段),另加 target('point' / 'line' / 'channel')与 lngLat;命中通道回线时 group 是当前组、groups 是整条通道的全部分组;没命中返回 null。只读,不改选中也不动光标", examples: [{"group":"坐标、拾取与读数","code":"// pick(x, y) / pickAt(clientX, clientY):按画布像素 / 客户端坐标拾取\nconst hit = view.pick(320, 200)\nconst hitAt = view.pickAt(event.clientX, event.clientY)\nhitAt?.groups // 命中通道回线:同一通道的全部分组;hitAt.group 是当前这一组\n","from":"header"}] },
@@ -127,8 +129,8 @@ export const API_SECTIONS = [
127
129
  { name: "getLine", kind: "method", params: "ref 线路定位(同上)", returns: "GisViewLineInfo | null", doc: "取一条线路:拾取载荷 + path / bounds / color / width(与 listLines() 的一项同一份口径),通道回线还带 channelId / groupId / channelIndex;找不到返回 null", examples: [{"group":"坐标、拾取与读数","code":"// getPoint(ref) / getLine(ref) / getChannel(ref):一个要素的几何读数\nconst pointInfo = view.getPoint('a')\nconst lineInfo = view.getLine('l1')\nconst channelInfo = view.getChannel('c1')\n","from":"header"}] },
128
130
  { name: "getChannel", kind: "method", params: "ref 通道定位:通道 id 或下标", returns: "GisViewChannelSnapshot | null", doc: "取一条通道:数据那一份(index / item / id)+ 成员表(lineIndexes / skeleton / offsets / paths)+ 分组上下文 context(几组 / 当前哪组 / 每组拐点数)、from / to / anchored / spacing;找不到返回 null", examples: [{"group":"坐标、拾取与读数","code":"// getPoint(ref) / getLine(ref) / getChannel(ref):一个要素的几何读数\nconst pointInfo = view.getPoint('a')\nconst lineInfo = view.getLine('l1')\nconst channelInfo = view.getChannel('c1')\n","from":"header"}] },
129
131
  { name: "listPoints", kind: "method", params: "options?: GisViewListOptions:indexes(只看这几个下标)、inView: boolean(只要当前视口里的)、withScreen: boolean(连屏幕坐标一起给)、visibleOnly: boolean(跳过隐藏的)、limit: number(最多几条)", returns: "GisViewListedPoint[]", doc: "按条件列节点快照 { index, item, x, y, radius, layerRank, screen? }:选项见 GisViewListOptions(indexes / inView / limit / withScreen / visibleOnly),可见判定与 isPointVisible 是同一份", examples: [{"group":"坐标、拾取与读数","code":"// listPoints(options?) / listLines(options?):按条件列一批\nconst listedPoints = view.listPoints({ limit: 5, withScreen: true })\nconst listedLines = view.listLines({ inView: true, limit: 200 })\n","from":"header"}] },
130
- { name: "getClusters", kind: "method", params: "无", returns: "GisViewCluster[]。当前展示组(含 count: 1 的单成员),screen 按当前相机现算;聚合关掉时是空表", doc: "当前点聚合的展示组 GisViewCluster[]:按数据顺序返回,含 count: 1 的单成员与 count > 1 的聚合圆;每项带 id / pointIndexes / pointIds / 世界坐标 coordinate / 当前屏幕坐标 screen / 半径 radius / 包围盒 bounds。原始 points 与 getData() 不变,聚合关掉时返回空表", examples: [{"group":"坐标、拾取与读数","code":"// getClusters() / getPointCluster():读当前点聚合展示组与配置\nview.updateOptions({ pointCluster: { radius: 64, minPoints: 3, maxZoom: 2.5 } })\nconst clusterGroups = view.getClusters()\nconst clusteredOnly = clusterGroups.filter(group => group.count > 1)\nconst clusterState = view.getPointCluster() // { enabled, radius, minPoints, maxZoom, active }\nview.updateOptions({ pointCluster: false }) // 关掉聚合,原始点全部恢复\n","from":"header"}] },
131
- { name: "getPointCluster", kind: "method", params: "无", returns: "GisViewPointClusterReadout | null。归一化配置 { enabled, radius, minPoints, maxZoom, active },超过 maxZoom 后 active 为 false", doc: "当前点聚合配置的归一化读数 GisViewPointClusterReadout | null:{ enabled, radius, minPoints, maxZoom, active },active 说明当前缩放档是否仍在聚合(超过 maxZoom 后为 false);引擎未就绪时为 null", examples: [{"group":"坐标、拾取与读数","code":"// getClusters() / getPointCluster():读当前点聚合展示组与配置\nview.updateOptions({ pointCluster: { radius: 64, minPoints: 3, maxZoom: 2.5 } })\nconst clusterGroups = view.getClusters()\nconst clusteredOnly = clusterGroups.filter(group => group.count > 1)\nconst clusterState = view.getPointCluster() // { enabled, radius, minPoints, maxZoom, active }\nview.updateOptions({ pointCluster: false }) // 关掉聚合,原始点全部恢复\n","from":"header"}] },
132
+ { name: "getClusters", kind: "method", params: "无", returns: "GisViewCluster[]。当前展示组(含 count: 1 的单成员),screen 按当前相机现算;多成员项另带 color / strokeColor / strokeWidth / opacity / textColor / fontSize / symbol / showCount / sizeScale,聚合关掉时是空表", doc: "当前点聚合的展示组 GisViewCluster[]:按数据顺序返回,含 count: 1 的单成员与 count > 1 的聚合圆;每项带 id / pointIndexes / pointIds / 世界坐标 coordinate / 当前屏幕坐标 screen / 半径 radius / 包围盒 bounds,多成员圆另带颜色、文字、symbol、showCount 与 sizeScale 读数。原始 points 与 getData() 不变,聚合关掉时返回空表", examples: [{"group":"坐标、拾取与读数","code":"// getClusters() / getPointCluster():读当前点聚合展示组与配置\nview.updateOptions({ pointCluster: {\n radius: 64,\n minPoints: 3,\n maxZoom: 2.5,\n color: '#f05a3c',\n strokeColor: '#1b3358',\n textColor: '#ffe08a',\n symbol: 'square',\n sizeScale: 1.2\n} })\nconst clusterGroups = view.getClusters()\nconst clusteredOnly = clusterGroups.filter(group => group.count > 1)\nconst clusterState = view.getPointCluster() // 归一化配置 + active\nview.updateOptions({ pointCluster: false }) // 关掉聚合,原始点全部恢复\n","from":"header"}] },
133
+ { name: "getPointCluster", kind: "method", params: "无", returns: "GisViewPointClusterReadout | null。归一化配置 { enabled, radius, minPoints, maxZoom, color, strokeColor, strokeWidth, opacity, textColor, fontSize, symbol, showCount, sizeScale, active },超过 maxZoom 后 active 为 false", doc: "当前点聚合配置的归一化读数 GisViewPointClusterReadout | null:包含 enabled / radius / minPoints / maxZoom / 颜色与描边 / 文字 / symbol / showCount / sizeScale,以及说明当前缩放档是否仍在聚合的 active(超过 maxZoom 后为 false);引擎未就绪时为 null", examples: [{"group":"坐标、拾取与读数","code":"// getClusters() / getPointCluster():读当前点聚合展示组与配置\nview.updateOptions({ pointCluster: {\n radius: 64,\n minPoints: 3,\n maxZoom: 2.5,\n color: '#f05a3c',\n strokeColor: '#1b3358',\n textColor: '#ffe08a',\n symbol: 'square',\n sizeScale: 1.2\n} })\nconst clusterGroups = view.getClusters()\nconst clusteredOnly = clusterGroups.filter(group => group.count > 1)\nconst clusterState = view.getPointCluster() // 归一化配置 + active\nview.updateOptions({ pointCluster: false }) // 关掉聚合,原始点全部恢复\n","from":"header"}] },
132
134
  { name: "getPointSpacing", kind: "method", params: "无", returns: "GisViewPointSpacing:count / minDistance / maxDistance / nearest / farthest。距离都是当前相机比例下的 CSS 像素,与 fitSpacing 同一套口径;最近 / 最远各带两端 { id, index, item } 与同口径的 distance,筛选 / 隐藏后的节点不参与", doc: "当前可见节点的最近正距离与最远距离 GisViewPointSpacing:minDistance / maxDistance 与 nearest / farthest.distance 都是当前相机比例下的 CSS 像素,和 fitSpacing 同一套口径;nearest / farthest 各带两端节点 { id, index, item }。筛选 / 隐藏后的节点不参与;少于两个可见节点时为 null,全部重合时最近为 null、最远为 0", examples: [{"group":"坐标、拾取与读数","code":"// getPointSpacing():最近 / 最远可见节点的中心距(CSS 像素,和 fitSpacing 同口径)\nconst spacing = view.getPointSpacing()\nif (spacing.nearest) {\n console.log('最近', spacing.nearest.from.id, spacing.nearest.to.id, spacing.nearest.distance, 'px')\n console.log('世界距离', spacing.nearest.distance / view.getScale())\n}\nif (spacing.farthest) {\n console.log('最远', spacing.farthest.from.id, spacing.farthest.to.id, spacing.farthest.distance, 'px')\n}","from":"header"}] },
133
135
  { name: "getPointsSpacing", kind: "method", params: "refs: GisViewFeatureRef | GisViewFeatureRef[]:一个或一组节点引用,支持 id / 下标 / { id } / { index };重复引用去重,非法引用忽略", returns: "GisViewPointSpacing:只统计显式传入的节点(筛选 / 隐藏不影响),count / minDistance / maxDistance / nearest / farthest 与 getPointSpacing() 同形,距离为当前相机比例下的 CSS 像素;少于两个有效节点时两列为 null,全部重合时最近为 null、最远为 0", doc: "指定一组节点的整体最近正距离与最远距离 GisViewPointSpacing,口径与 getPointSpacing() 相同:refs 可传单个或数组的 id / 下标 / { id } / { index },重复引用去重、非法引用忽略;只统计显式传入的节点,筛选 / 隐藏不影响。少于两个有效节点时两列为 null,全部重合时最近为 null、最远为 0", examples: [{"group":"坐标、拾取与读数","code":"// getPointsSpacing(refs):指定一组节点的整体中心距(CSS 像素;筛选 / 隐藏不影响)\nconst groupSpacing = view.getPointsSpacing(['a', { index: 2 }, { id: 'b' }])\nif (groupSpacing.nearest) {\n console.log('组内最近', groupSpacing.nearest.from.id, groupSpacing.nearest.to.id, groupSpacing.minDistance, 'px')\n}\nconsole.log('组内最远', groupSpacing.maxDistance, 'px', '世界距离', groupSpacing.maxDistance / view.getScale())\n","from":"header"}] },
134
136
  { name: "listLines", kind: "method", params: "同 listPoints(inView / withScreen / limit…)", returns: "GisViewListedLine[]", doc: "按条件列线路快照 { index, item, path, bounds, color, width, channelId?, groupId?, channelIndex?, screen? }:选项与 listPoints 同一套;通道回线给的是骨架推导出来的那条折线(跟图上画的一致)", examples: [{"group":"坐标、拾取与读数","code":"// listPoints(options?) / listLines(options?):按条件列一批\nconst listedPoints = view.listPoints({ limit: 5, withScreen: true })\nconst listedLines = view.listLines({ inView: true, limit: 200 })\n","from":"header"}] },
@@ -160,8 +162,8 @@ export const API_SECTIONS = [
160
162
  { name: "getSelectionData", kind: "method", params: "无", returns: "GisViewSelectionData:{ ids, count, pointCount, lineCount, channelCount, points, lines, channels, items, pointIndexes, lineIndexes, bounds }(bounds 是世界包围盒,空选中时 null;items 的线路不带通道字段,channels 是去重后的所属通道)", doc: "选中集一次取全:读数(ids / count / pointCount / lineCount / channelCount)加要素本身(points / lines / channels(按通道去重,与 getData() 同一批对象)),另有 items(与 getSelections() 同一份,通道回线不带通道字段)、pointIndexes / lineIndexes 与 bounds(世界包围盒,空选中时 null),见「接口选中(selectFeatures)与读回选中」", examples: [{"group":"选中、复制与合并","code":"// getSelections() / getSelectionInfo() / getSelectionData() / isSelected(ref):读回选中\nconst picks = view.getSelections()\nconst info = view.getSelectionInfo() // { ids, count, points, lines, channels }\nconst selection = view.getSelectionData() // { ids, count, points, lines, channels, bounds, … }\nconst selected = view.isSelected('a')\n","from":"header"}] },
161
163
  { name: "getCascadeHighlight", kind: "method", params: "无", returns: "GisViewCascadeHighlightInfo | null:当前点击级联高亮的来源、范围与节点 / 线路读数 { origin, scope, points, lines, pointIndexes, lineIndexes },不改变选中集", doc: "当前点击级联高亮的读数:{ origin, scope, points, lines, pointIndexes, lineIndexes };没有高亮或 cascadeHighlight 关着时返回 null。它只读、不改选中集,见「点击级联高亮」", examples: [{"group":"选中、复制与合并","code":"// getCascadeHighlight():读点击级联高亮(与选中集分开;没有高亮时为 null)\nconst cascade = view.getCascadeHighlight() // { origin, scope, points, lines, pointIndexes, lineIndexes }\nview.updateOptions({\n cascadeHighlight: {\n scope: 'points',\n triggerOnClick: true,\n triggerOnMultiSelect: false,\n triggerOnBoxSelect: true,\n clearOnEmptyClick: true\n }\n})\nview.on('cascadeHighlight', payload => console.log(payload?.pointIndexes, payload?.lineIndexes))\n","from":"header"}] },
162
164
  { name: "selectInBox", kind: "method", params: "box: GisViewRect 画布 CSS 像素矩形 { x, y, width, height }(与手动框选同一套坐标);options?: GisViewSelectInBoxOptions:selectionKinds 临时覆盖这一次保留哪几类('point' / 'line' / 'channel' / 'lineEndpoints' / 'channelEndpoints'、数组、'all' / true / false / null,不写沿用全局 selectionKinds),originalEvent 原样带进 onSelect。空框不会选中任何要素", returns: "this(每次框选都发 onSelect,空框为 null;集合变化时再发 onSelectChange,顺序 onSelect → onSelectChange,cause 为 boxSelect)", doc: "按画布 CSS 像素矩形框选,等价于 Shift + 拖动(跟随 shortcuts.boxSelect);options.selectionKinds 可只覆盖这一次的节点 / 线路 / 通道类型过滤,也支持 'lineEndpoints' / 'channelEndpoints' 要求两端节点都在框内。每次框选操作都发 onSelect(空框的载荷为 null),选中集实际变化时再发 onSelectChange,顺序 onSelect → onSelectChange(cause: 'boxSelect');通道回线的分组字段与鼠标框选同一份", examples: [{"group":"选中、复制与合并","code":"// selectInBox(box, options?):用矩形框选(画布 CSS 像素,与手动框选同一套判定)\nview.selectInBox({ x: 40, y: 40, width: 320, height: 200 })\nview.selectInBox({ x: 40, y: 40, width: 320, height: 200 }, { selectionKinds: ['line', 'channel'] })\nview.selectInBox({ x: 40, y: 40, width: 320, height: 200 }, { selectionKinds: ['lineEndpoints', 'channelEndpoints'] }) // 两端节点都在框内才命中\n","from":"header"}] },
163
- { name: "clearSelectionBox", kind: "method", params: "无:收起当前保留的框选矩形;只动矩形,不改变选中集,也不发选中回调", returns: "this", doc: "收起当前保留的框选矩形 / 读当前矩形:clearSelectionBox() 只动框,不改变选中集;getSelectionBox() 返回画布 CSS 像素的 { x, y, width, height } 副本,没有框时返回 null。保留框由 selectionBoxPersistent 打开,见「框选」", examples: [{"group":"选中、复制与合并","code":"// selectionBoxPersistent / clearSelectionBox() / getSelectionBox():框选后保留矩形,并手动收起\nview.updateOptions({ boxSelect: true, draggable: true, selectionBoxPersistent: true })\nview.selectInBox({ x: 40, y: 40, width: 320, height: 200 })\nconst retainedBox = view.getSelectionBox() // 画布 CSS 像素;没有框时为 null\nview.clearSelectionBox() // 只收框,不改选中集\n","from":"header"}] },
164
- { name: "getSelectionBox", kind: "method", params: "无", returns: "GisViewRect | null:保留框的画布 CSS 像素矩形(已归整为左上角 + 宽高),没有时为 null", doc: "收起当前保留的框选矩形 / 读当前矩形:clearSelectionBox() 只动框,不改变选中集;getSelectionBox() 返回画布 CSS 像素的 { x, y, width, height } 副本,没有框时返回 null。保留框由 selectionBoxPersistent 打开,见「框选」", examples: [{"group":"选中、复制与合并","code":"// selectionBoxPersistent / clearSelectionBox() / getSelectionBox():框选后保留矩形,并手动收起\nview.updateOptions({ boxSelect: true, draggable: true, selectionBoxPersistent: true })\nview.selectInBox({ x: 40, y: 40, width: 320, height: 200 })\nconst retainedBox = view.getSelectionBox() // 画布 CSS 像素;没有框时为 null\nview.clearSelectionBox() // 只收框,不改选中集\n","from":"header"}] },
165
+ { name: "clearSelectionBox", kind: "method", params: "无:收起当前保留的框选矩形;只动矩形,不改变选中集,也不发选中回调", returns: "this", doc: "收起当前保留的框选矩形 / 读当前矩形:clearSelectionBox() 只动框,不改变选中集;getSelectionBox() 返回画布 CSS 像素的 { x, y, width, height } 副本,没有框时返回 null。保留框由 selectionBoxPersistent 打开,见「框选」", examples: [{"group":"选中、复制与合并","code":"// selectionBoxPersistent / clearSelectionBox() / getSelectionBox():框选后保留矩形,并手动收起\nview.updateOptions({ features: { boxSelect: true, drag: true }, selectionBoxPersistent: true })\nview.selectInBox({ x: 40, y: 40, width: 320, height: 200 })\nconst retainedBox = view.getSelectionBox() // 画布 CSS 像素;没有框时为 null\nview.clearSelectionBox() // 只收框,不改选中集\n","from":"header"}] },
166
+ { name: "getSelectionBox", kind: "method", params: "无", returns: "GisViewRect | null:保留框的画布 CSS 像素矩形(已归整为左上角 + 宽高),没有时为 null", doc: "收起当前保留的框选矩形 / 读当前矩形:clearSelectionBox() 只动框,不改变选中集;getSelectionBox() 返回画布 CSS 像素的 { x, y, width, height } 副本,没有框时返回 null。保留框由 selectionBoxPersistent 打开,见「框选」", examples: [{"group":"选中、复制与合并","code":"// selectionBoxPersistent / clearSelectionBox() / getSelectionBox():框选后保留矩形,并手动收起\nview.updateOptions({ features: { boxSelect: true, drag: true }, selectionBoxPersistent: true })\nview.selectInBox({ x: 40, y: 40, width: 320, height: 200 })\nconst retainedBox = view.getSelectionBox() // 画布 CSS 像素;没有框时为 null\nview.clearSelectionBox() // 只收框,不改选中集\n","from":"header"}] },
165
167
  { name: "beginCopyPoint", kind: "method", params: "options: GisViewCopyStartOptions:id / index / item 三选一给来源,都不给就进入「下一次点选节点」模式;coordinate / screen 是落点(缺省按 copy.offset);follow 让预览跟指针;autoCommit 覆盖 copy.autoCommit;commit: true 出预览即落地;via / originalEvent 原样带回。必须配置 copy(enabled 不为 false),否则返回 null。预览节点插在 points 末尾且不带 id,不会把绑定源 id 的线路 / 通道带过去", returns: "GisViewPointCopyPayload | null:phase: 'start' 的预览载荷;来源认不出来时返回 null", doc: "开始复制一个节点:给 id / index 直接出预览(commit: true 出预览即落地,follow: true 跟随鼠标,autoCommit: false 停在预览态),不给来源就是「先点按钮再选节点」模式,见「节点复制」", examples: [{"group":"选中、复制与合并","code":"// beginCopyPoint(options) / getPointCopy() / updateCopyPoint(patch) / commitPointCopy() / cancelPointCopy()\nview.updateOptions({ copy: { enabled: true, autoCommit: false } }) // API 复制入口要先开 copy 配置\nview.beginCopyPoint({ id: 'a', coordinate: [40, 30] }) // 出预览并钉在世界坐标 [40, 30]\nview.beginCopyPoint({ id: 'a', follow: true }) // 预览跟着指针走(autoCommit 缺省停住)\nview.beginCopyPoint() // 先点按钮再选节点(点中下一个节点当来源)\nview.updateCopyPoint({ name: '新节点' }) // 字段级改预览节点(落点仍由 coordinate / 指针决定)\nconst copyEnd = view.commitPointCopy() // 落地:返回 end 载荷;没有预览时为 null\nview.cancelPointCopy() // 取消:预览撤掉,数据与历史不动\nconst copyState = view.getPointCopy() // 当前预览 / 选点态(没有就是 null)\n","from":"header"}] },
166
168
  { name: "commitPointCopy", kind: "method", params: "无:把当前复制预览落地,先发 commit、落地后发 end;commit 阶段载荷里的 cancel() 可以拦住这次复制", returns: "GisViewPointCopyPayload | null:成功的 end 载荷;没有预览或已被取消时为 null", doc: "落地 / 取消预览中的复制(返回 end / cancel 载荷)", examples: [{"group":"选中、复制与合并","code":"// beginCopyPoint(options) / getPointCopy() / updateCopyPoint(patch) / commitPointCopy() / cancelPointCopy()\nview.updateOptions({ copy: { enabled: true, autoCommit: false } }) // API 复制入口要先开 copy 配置\nview.beginCopyPoint({ id: 'a', coordinate: [40, 30] }) // 出预览并钉在世界坐标 [40, 30]\nview.beginCopyPoint({ id: 'a', follow: true }) // 预览跟着指针走(autoCommit 缺省停住)\nview.beginCopyPoint() // 先点按钮再选节点(点中下一个节点当来源)\nview.updateCopyPoint({ name: '新节点' }) // 字段级改预览节点(落点仍由 coordinate / 指针决定)\nconst copyEnd = view.commitPointCopy() // 落地:返回 end 载荷;没有预览时为 null\nview.cancelPointCopy() // 取消:预览撤掉,数据与历史不动\nconst copyState = view.getPointCopy() // 当前预览 / 选点态(没有就是 null)\n","from":"header"}] },
167
169
  { name: "cancelPointCopy", kind: "method", params: "无:撤掉预览并补一条 cancel,数据与历史不动", returns: "GisViewPointCopyPayload | null:取消载荷;没有预览时为 null", doc: "落地 / 取消预览中的复制(返回 end / cancel 载荷)", examples: [{"group":"选中、复制与合并","code":"// beginCopyPoint(options) / getPointCopy() / updateCopyPoint(patch) / commitPointCopy() / cancelPointCopy()\nview.updateOptions({ copy: { enabled: true, autoCommit: false } }) // API 复制入口要先开 copy 配置\nview.beginCopyPoint({ id: 'a', coordinate: [40, 30] }) // 出预览并钉在世界坐标 [40, 30]\nview.beginCopyPoint({ id: 'a', follow: true }) // 预览跟着指针走(autoCommit 缺省停住)\nview.beginCopyPoint() // 先点按钮再选节点(点中下一个节点当来源)\nview.updateCopyPoint({ name: '新节点' }) // 字段级改预览节点(落点仍由 coordinate / 指针决定)\nconst copyEnd = view.commitPointCopy() // 落地:返回 end 载荷;没有预览时为 null\nview.cancelPointCopy() // 取消:预览撤掉,数据与历史不动\nconst copyState = view.getPointCopy() // 当前预览 / 选点态(没有就是 null)\n","from":"header"}] },
@@ -194,7 +196,7 @@ export const API_SECTIONS = [
194
196
  title: "拐点与通道",
195
197
  items: [
196
198
  { name: "addWaypoint", kind: "method", params: "lineId 线路 id(通道里的回线也行,加点作用在它所在的分组骨架上);coordinate: GisViewCoordinate 新拐点的世界坐标([x, y] 或 { x, y })", returns: "this", doc: "在线路上增删拐点,等价于 Ctrl + 点击与 Delete", examples: [{"group":"拐点与通道","code":"// addWaypoint(lineId, coordinate) / addWaypoints(lineId, coordinates, options?) / removeWaypoint(lineId, pathIndex):线路拐点的加 / 删\nview.addWaypoint('l1', [200, 120])\nview.addWaypoint('l1', { x: 200, y: 120 })\nconst addedWaypoints = view.addWaypoints('l1', [[210, 130], [220, 140]], { label: '一次加两个拐点' })\nconsole.log(addedWaypoints.ok, addedWaypoints.count, addedWaypoints.coordinates)\nview.removeWaypoint('l1', 1) // 1 是 getWaypoints() 给的 pathIndex\n","from":"header"}] },
197
- { name: "addWaypoints", kind: "method", params: "lineId 线路 id;coordinates: GisViewCoordinate[] 要插入的一批拐点(按数组顺序);options?: { label?: string } 这一步历史的标签", returns: "GisViewAddWaypointsResult(coordinates 只含实际插入成功的坐标)", doc: "一次给一条线路插多个拐点:按数组顺序插入,多个拐点合成一步历史;坐标写错的条目跳过,返回 { ok, reason, count, lineId, lineIndex, coordinates }(coordinates 是实际插入成功的坐标),线路不存在或 waypointEditable: false 时 ok: false", examples: [{"group":"拐点与通道","code":"// addWaypoint(lineId, coordinate) / addWaypoints(lineId, coordinates, options?) / removeWaypoint(lineId, pathIndex):线路拐点的加 / 删\nview.addWaypoint('l1', [200, 120])\nview.addWaypoint('l1', { x: 200, y: 120 })\nconst addedWaypoints = view.addWaypoints('l1', [[210, 130], [220, 140]], { label: '一次加两个拐点' })\nconsole.log(addedWaypoints.ok, addedWaypoints.count, addedWaypoints.coordinates)\nview.removeWaypoint('l1', 1) // 1 是 getWaypoints() 给的 pathIndex\n","from":"header"}] },
199
+ { name: "addWaypoints", kind: "method", params: "lineId 线路 id;coordinates: GisViewCoordinate[] 要插入的一批拐点(按数组顺序);options?: { label?: string } 这一步历史的标签", returns: "GisViewAddWaypointsResult(coordinates 只含实际插入成功的坐标)", doc: "一次给一条线路插多个拐点:按数组顺序插入,多个拐点合成一步历史;坐标写错的条目跳过,返回 { ok, reason, count, lineId, lineIndex, coordinates }(coordinates 是实际插入成功的坐标),线路不存在、features.waypoint 关闭或线路自身 waypointEditable: false 时 ok: false", examples: [{"group":"拐点与通道","code":"// addWaypoint(lineId, coordinate) / addWaypoints(lineId, coordinates, options?) / removeWaypoint(lineId, pathIndex):线路拐点的加 / 删\nview.addWaypoint('l1', [200, 120])\nview.addWaypoint('l1', { x: 200, y: 120 })\nconst addedWaypoints = view.addWaypoints('l1', [[210, 130], [220, 140]], { label: '一次加两个拐点' })\nconsole.log(addedWaypoints.ok, addedWaypoints.count, addedWaypoints.coordinates)\nview.removeWaypoint('l1', 1) // 1 是 getWaypoints() 给的 pathIndex\n","from":"header"}] },
198
200
  { name: "removeWaypoint", kind: "method", params: "lineId 线路 id;pathIndex: number 要删的顶点下标(getWaypoints() 给的 pathIndex)", returns: "this", doc: "在线路上增删拐点,等价于 Ctrl + 点击与 Delete", examples: [{"group":"拐点与通道","code":"// addWaypoint(lineId, coordinate) / addWaypoints(lineId, coordinates, options?) / removeWaypoint(lineId, pathIndex):线路拐点的加 / 删\nview.addWaypoint('l1', [200, 120])\nview.addWaypoint('l1', { x: 200, y: 120 })\nconst addedWaypoints = view.addWaypoints('l1', [[210, 130], [220, 140]], { label: '一次加两个拐点' })\nconsole.log(addedWaypoints.ok, addedWaypoints.count, addedWaypoints.coordinates)\nview.removeWaypoint('l1', 1) // 1 是 getWaypoints() 给的 pathIndex\n","from":"header"}] },
199
201
  { name: "getWaypoints", kind: "method", params: "lineIndex?: number 只看这一条线路(不传给全部)", returns: "GisViewWaypointInfo[]", doc: "当前显示中的拐点(受 waypointVisibility 影响),带坐标与选中态", examples: [{"group":"拐点与通道","code":"// getWaypoints(lineIndex?) / getParallelGroups():拐点清单 / 新增拐点集合\nconst waypoints = view.getWaypoints()\nconst waypointsOfLine = view.getWaypoints(0) // 只看这一条线(线路下标)\nconst parallelGroups = view.getParallelGroups()\n","from":"header"}] },
200
202
  { name: "getParallelGroups", kind: "method", params: "无", returns: "GisViewParallelGroup[]", doc: "新增拐点集合:被加过 / 拖过拐点的通道与线路,结构与 test.json 的 parallel_groups 一致", examples: [{"group":"拐点与通道","code":"// getWaypoints(lineIndex?) / getParallelGroups():拐点清单 / 新增拐点集合\nconst waypoints = view.getWaypoints()\nconst waypointsOfLine = view.getWaypoints(0) // 只看这一条线(线路下标)\nconst parallelGroups = view.getParallelGroups()\n","from":"header"}] },
@@ -338,8 +340,8 @@ export const API_SECTIONS = [
338
340
  { name: "registerCommand", kind: "method", params: "name: string 命令名(会 trim,同名覆盖);handler: GisViewCommandHandler<Payload, Value> 同步处理函数 (payload, context) => Value,context 是冻结的 { view, name }。名字为空 / handler 不是函数只报 warning、不注册;视图销毁后注册不生效", returns: "() => void 幂等注销;同名已被后来者覆盖时不会误删新命令", doc: "注册一个实例命令:handler(payload, context) 同步执行,context 是冻结的 { view, name };同名覆盖,返回幂等的注销函数,旧卸载函数不会误删后来注册的同名命令,见「实例命令」", examples: [{"group":"事件、开关与历史","code":"// registerCommand(name, handler) / executeCommand(name, payload?) / listCommands():实例级业务命令\nconst disposeCommand = view.registerCommand('focusHighestLoad', (payload = {}, context) => {\n const threshold = Number(payload.threshold ?? 0.8)\n const target = view.getData().points\n .filter(point => Number(point.max_loading_rate ?? 0) >= threshold)\n .sort((left, right) => Number(right.max_loading_rate ?? 0) - Number(left.max_loading_rate ?? 0))[0]\n if (!target) return { focused: false, threshold }\n context.view.focusOn(target.id, { select: true, zoom: true, maxZoom: 12 })\n return { focused: true, id: target.id, command: context.name }\n})\nconst commandResult = view.executeCommand('focusHighestLoad', { threshold: 0.85 })\nconst commandNames = view.listCommands() // ['focusHighestLoad']\ndisposeCommand() // 幂等卸载;同名覆盖时不会误删新命令\n","from":"header"}] },
339
341
  { name: "executeCommand", kind: "method", params: "name: string 命令名;payload?: unknown 原样交给 handler", returns: "GisViewCommandResult<Value>:{ ok: true, name, value } / { ok: false, name, reason }(未注册 / 名字非法 / handler 抛错)", doc: "执行实例命令:成功给 { ok: true, name, value };名字非法 / 未注册 / handler 抛错给 { ok: false, name, reason },见「实例命令」", examples: [{"group":"事件、开关与历史","code":"// registerCommand(name, handler) / executeCommand(name, payload?) / listCommands():实例级业务命令\nconst disposeCommand = view.registerCommand('focusHighestLoad', (payload = {}, context) => {\n const threshold = Number(payload.threshold ?? 0.8)\n const target = view.getData().points\n .filter(point => Number(point.max_loading_rate ?? 0) >= threshold)\n .sort((left, right) => Number(right.max_loading_rate ?? 0) - Number(left.max_loading_rate ?? 0))[0]\n if (!target) return { focused: false, threshold }\n context.view.focusOn(target.id, { select: true, zoom: true, maxZoom: 12 })\n return { focused: true, id: target.id, command: context.name }\n})\nconst commandResult = view.executeCommand('focusHighestLoad', { threshold: 0.85 })\nconst commandNames = view.listCommands() // ['focusHighestLoad']\ndisposeCommand() // 幂等卸载;同名覆盖时不会误删新命令\n","from":"header"}] },
340
342
  { name: "listCommands", kind: "method", params: "无", returns: "string[] 当前注册的命令名(按注册顺序;同名覆盖不改变位置)", doc: "当前注册着哪些实例命令(按注册顺序;同名覆盖不改变原来的位置)", examples: [{"group":"事件、开关与历史","code":"// registerCommand(name, handler) / executeCommand(name, payload?) / listCommands():实例级业务命令\nconst disposeCommand = view.registerCommand('focusHighestLoad', (payload = {}, context) => {\n const threshold = Number(payload.threshold ?? 0.8)\n const target = view.getData().points\n .filter(point => Number(point.max_loading_rate ?? 0) >= threshold)\n .sort((left, right) => Number(right.max_loading_rate ?? 0) - Number(left.max_loading_rate ?? 0))[0]\n if (!target) return { focused: false, threshold }\n context.view.focusOn(target.id, { select: true, zoom: true, maxZoom: 12 })\n return { focused: true, id: target.id, command: context.name }\n})\nconst commandResult = view.executeCommand('focusHighestLoad', { threshold: 0.85 })\nconst commandNames = view.listCommands() // ['focusHighestLoad']\ndisposeCommand() // 幂等卸载;同名覆盖时不会误删新命令\n","from":"header"}] },
341
- { name: "getFeatures", kind: "method", params: "无", returns: "GisViewFeatureState", doc: "读当前生效的功能开关(跟随详细配置的也解析成 true / false)/ 换一份(预设名 / 开关表 / true / false),换完立刻生效并发一条 onFeatureChange", examples: [{"group":"事件、开关与历史","code":"// getFeatures() / setFeatures(next):功能开关的读 / 写(预设名 / 开关表 / 布尔值)\nconst features = view.getFeatures()\nview.setFeatures('view') // 'edit' / 'view' / 'static'\nview.setFeatures({ drag: false }) // 单个开关\nview.setFeatures(true) // 全开\n","from":"header"}] },
342
- { name: "setFeatures", kind: "method", params: "next: GisViewFeaturesInput 预设名('edit' / 'view' / 'static')、开关表或布尔值", returns: "this", doc: "读当前生效的功能开关(跟随详细配置的也解析成 true / false)/ 换一份(预设名 / 开关表 / true / false),换完立刻生效并发一条 onFeatureChange", examples: [{"group":"事件、开关与历史","code":"// getFeatures() / setFeatures(next):功能开关的读 / 写(预设名 / 开关表 / 布尔值)\nconst features = view.getFeatures()\nview.setFeatures('view') // 'edit' / 'view' / 'static'\nview.setFeatures({ drag: false }) // 单个开关\nview.setFeatures(true) // 全开\n","from":"header"}] },
343
+ { name: "getFeatures", kind: "method", params: "无", returns: "GisViewFeatureState:19 个规范开关的有效值,不返回别名键", doc: "读当前生效的功能开关(跟随详细配置的也解析成 true / false)/ 换一份(预设名 / 开关表 / true / false),换完立刻生效并发一条 onFeatureChange", examples: [{"group":"事件、开关与历史","code":"// getFeatures() / setFeatures(next):功能开关的读 / 写(预设名 / 开关表 / 布尔值)\nconst features = view.getFeatures()\nview.setFeatures('view') // 'edit' / 'view' / 'static'\nview.setFeatures({ drag: false }) // 单个开关\nview.setFeatures({ draggable: true, multiSelect: true }) // features 内的详细配置别名也能收口\nview.setFeatures(true) // 全开\n","from":"header"}] },
344
+ { name: "setFeatures", kind: "method", params: "next: GisViewFeaturesInput 预设名('edit' / 'view' / 'static')、开关对象或布尔值;对象可写 draggable / waypointEditable / interactive / clickable 别名,规范名优先", returns: "this", doc: "读当前生效的功能开关(跟随详细配置的也解析成 true / false)/ 换一份(预设名 / 开关表 / true / false),换完立刻生效并发一条 onFeatureChange", examples: [{"group":"事件、开关与历史","code":"// getFeatures() / setFeatures(next):功能开关的读 / 写(预设名 / 开关表 / 布尔值)\nconst features = view.getFeatures()\nview.setFeatures('view') // 'edit' / 'view' / 'static'\nview.setFeatures({ drag: false }) // 单个开关\nview.setFeatures({ draggable: true, multiSelect: true }) // features 内的详细配置别名也能收口\nview.setFeatures(true) // 全开\n","from":"header"}] },
343
345
  { name: "undo", kind: "method", params: "无:从撤销 / 重做栈回放一步;会同步数据、发 history 与对应的 dataChange,并更新相机以外的运行时读数", returns: "GisViewHistoryStepResult | null:{ type, label, canUndo, canRedo };历史没开或栈空时为 null", doc: "撤销 / 重做一步:返回 { type, label, canUndo, canRedo },历史没开或没得撤销 / 重做时返回 null", examples: [{"group":"事件、开关与历史","code":"// undo() / redo() / canUndo() / canRedo():撤销 / 重做(历史的开关在 options.history)\nview.undo()\nview.redo()\nconst canGoBack = view.canUndo()\nconst canGoForward = view.canRedo()\n","from":"header"}] },
344
346
  { name: "redo", kind: "method", params: "无:从撤销 / 重做栈回放一步;会同步数据、发 history 与对应的 dataChange,并更新相机以外的运行时读数", returns: "GisViewHistoryStepResult | null:{ type, label, canUndo, canRedo };历史没开或栈空时为 null", doc: "撤销 / 重做一步:返回 { type, label, canUndo, canRedo },历史没开或没得撤销 / 重做时返回 null", examples: [{"group":"事件、开关与历史","code":"// undo() / redo() / canUndo() / canRedo():撤销 / 重做(历史的开关在 options.history)\nview.undo()\nview.redo()\nconst canGoBack = view.canUndo()\nconst canGoForward = view.canRedo()\n","from":"header"}] },
345
347
  { name: "canUndo", kind: "method", params: "无:只读当前两份栈,不触发历史变化;界面据此灰掉按钮", returns: "boolean", doc: "能不能撤销 / 重做(界面据此灰掉按钮)", examples: [{"group":"事件、开关与历史","code":"// undo() / redo() / canUndo() / canRedo():撤销 / 重做(历史的开关在 options.history)\nview.undo()\nview.redo()\nconst canGoBack = view.canUndo()\nconst canGoForward = view.canRedo()\n","from":"header"}] },
@@ -379,14 +381,14 @@ export const API_SECTIONS = [
379
381
 
380
382
  /** options 的每个键(字段 / 类型 / 默认值 / 说明):与「## options」表同一份 */
381
383
  export const API_OPTIONS = [
382
- { name: "features", type: "GisViewFeaturesInput", defaultValue: "无(缺省全关)", doc: "功能开关:预设名('edit' 全开 / 'view' 打开查看类 / 'static' 与缺省一致(全关))、开关表或布尔值。什么都不写时只剩画布与数据:没写的开关跟随各自的详细配置(interactive / clickable / draggable / boxSelect / copy.enabled…),详细配置也没写就是关(提示 / 标签 / 鹰眼图配了才有),写了 true / false 就是强制打开 / 关闭;想一次打开一批就用 'view'(能平移缩放、能悬停选中看提示但改不动数据)或 'edit'(等价 features: true)。运行时用 setFeatures() 换、getFeatures() 读,见「功能开关」" },
384
+ { name: "features", type: "GisViewFeaturesInput", defaultValue: "无(缺省全关)", doc: "功能开关:对象({ 开关名: boolean },不是数组)、预设名('edit' 全开 / 'view' 打开查看类 / 'static' 与缺省一致(全关))或布尔值。交互权限只有这一个入口:pan / zoom / hover / select / multiSelect / toggleSelect / boxSelect / drag / waypoint / contextMenu / channelDraft / keyboard 没写就是关;旧的顶层 draggable / interactive / clickable / boxSelect / multiSelect / toggleSelect / waypointEditable 已移除,传入会经 onWarning 提示迁移并忽略。copy / merge / remove 仍保留详细配置(写了即开,可用 features 强制开关),标签 / 提示 / 鹰眼图仍是「配了才有」。对象里允许写 draggable / waypointEditable / interactive / clickable 作为别名;同一张表同时写别名与规范名时规范名优先,getFeatures() 只返回 19 个规范键。想一次打开一批就用 'view'(能平移缩放、能悬停选中看提示但改不动数据)或 'edit'(等价 features: true)。运行时用 setFeatures() 换、getFeatures() 读,见「功能开关」" },
383
385
  { name: "history", type: "GisViewHistoryOptions", defaultValue: "关", doc: "历史(撤销 / 重做):true 用缺省上限 50 条,{ limit } 自定义(0 表示不限),不配就是关。打开之后 Ctrl+Z 撤销、Ctrl+Shift+Z / Ctrl+Y 重做,接口是 undo() / redo() / canUndo() / canRedo(),见「历史」" },
384
386
  { name: "quality", type: "GisViewQualityOptions | GisViewQualityMode | boolean", defaultValue: "'high'", doc: "画质档位(自动档按渲染耗时与整帧间隔两路信号升降):'high' 全开 / 'balanced' 关光晕 / 'lean' 连装饰一起关 / 'fast' 再降到 0.75 倍渲染分辨率,'auto'(也可以直接写 true)按这两路信号自己升降,对象写法 { mode, budgetMs, minScale } 能换预算与分辨率下限。只改「画哪些批次 + 多大分辨率」,不重排场景、不动数据,见「画质」" },
385
387
  { name: "denseMode", type: "GisViewDenseModeInput", defaultValue: "'auto'", doc: "大数据展示模式:'auto' 按可见节点数、屏幕密度与数据总量自动进入密集档,'on' / 'off'(或 true / false)强制开关;对象写法可换 visibleThreshold / densityThreshold / pointThreshold 三档阈值。进入后标签自动走避让 / 截断,线路动点与节点涟漪暂停;读数看 getStats().dense,见「大数据展示模式(denseMode)」" },
386
388
  { name: "points", type: "GisViewPoint[]", defaultValue: "[]", doc: "点数据,x / y 为世界坐标(经纬度、投影米或任意平面坐标)" },
387
389
  { name: "lines", type: "GisViewLine[]", defaultValue: "[]", doc: "线数据,start / end 支持 { x, y } 或 [x, y],折线用 path,端点可用 from / to 绑定点 id" },
388
390
  { name: "channels", type: "GisViewChannel[]", defaultValue: "[]", doc: "通道:把若干条线路组织成并行敷设的多回线走廊,回线几何由骨架折线按偏移推导;两端可用 from / to 绑在节点上" },
389
- { name: "pointCluster", type: "boolean | GisViewPointClusterOptions", defaultValue: "关", doc: "点聚合:true 使用缺省档(屏幕半径 60px / 至少 2 点 / 不限缩放),对象写法可调 enabled / radius / minPoints / maxZoom。聚合圆替代密集原始点参与绘制、拾取、快照与导出;原始 points / getData() 不变,缩放超过 maxZoom 后恢复逐点显示,见「点聚合(pointCluster)」" },
391
+ { name: "pointCluster", type: "boolean | GisViewPointClusterOptions", defaultValue: "关", doc: "点聚合:true 使用缺省档(屏幕半径 60px / 至少 2 点 / 不限缩放),对象写法可调 enabled / radius / minPoints / maxZoom,并支持填充色 / 描边 / 透明度 / 数量文字 / circle、square、SVG、图片或自定义图元图标 / sizeScale。聚合圆替代密集原始点参与绘制、拾取、快照与导出;原始 points / getData() 不变,缩放超过 maxZoom 后恢复逐点显示,见「点聚合(pointCluster)」" },
390
392
  { name: "channelWidth", type: "number", defaultValue: "0.02", doc: "通道宽度的世界单位写法(tangent 模式下是最外侧两条回线中心的间距,inset 模式下先按它算再收),只在 nodeWidth 为 0 或两端没绑节点时才真正决定宽度;单个通道可用 width 覆盖" },
391
393
  { name: "channelInset", type: "number", defaultValue: "0", doc: "offsetMode: 'inset' 时从切点收 / 放的比例(占通道宽度):正数内收(最多 0.42),负数外收(最多 -0.5,可用区间撑到 2 倍宽);单个通道可用 inset 覆盖,见「通道与多回线」" },
392
394
  { name: "channelNodeWidth", type: "number", defaultValue: "1", doc: "节点宽度倍数:通道宽度按两端节点尺寸取的倍数,1 表示回线正好铺在节点两边、节点放大回线跟着变宽,0 表示所有通道退回 width / channelWidth 的世界单位写法,单个通道可用 nodeWidth 覆盖" },
@@ -432,18 +434,14 @@ export const API_OPTIONS = [
432
434
  { name: "lineZoomScale", type: "number", defaultValue: "1 / 0", doc: "两组系数各管一件事,不重复:pointSizeScale / lineWidthScale 是与地图无关的整体放大(常数倍数,屏幕上的大小因此恒定),pointZoomScale / lineZoomScale 是「跟随地图缩放」的指数(0 表示不跟随,1 表示与地图同倍率放大,0.5 是地图放大 4 倍、要素放大 2 倍),绘制尺寸是两者相乘;zoomBaseScale 是联动基准,缺省取首次适应视图时的比例" },
433
435
  { name: "zoomBaseScale", type: "number", defaultValue: "1 / 0", doc: "两组系数各管一件事,不重复:pointSizeScale / lineWidthScale 是与地图无关的整体放大(常数倍数,屏幕上的大小因此恒定),pointZoomScale / lineZoomScale 是「跟随地图缩放」的指数(0 表示不跟随,1 表示与地图同倍率放大,0.5 是地图放大 4 倍、要素放大 2 倍),绘制尺寸是两者相乘;zoomBaseScale 是联动基准,缺省取首次适应视图时的比例" },
434
436
  { name: "fitSpacing", type: "boolean | GisViewFitSpacingOptions", defaultValue: "关", doc: "适应视图的节点间距兜底:true 把最近节点中心距精确设为 24 CSS 像素;对象在 minDistance(精确屏距,可放大也可缩小)/ maxDistance(最远距离上限,只压低比例)中二选一,同时给出时只启用 maxDistance;maxZoom 仅为旧配置兼容保留,不再限制距离;只在首次默认适应与 fitView() 生效,远处孤立点允许落到视口外,见「适应视图的节点间距(fitSpacing)」" },
435
- { name: "boxSelect", type: "boolean", defaultValue: "false", doc: "是否允许按住框选快捷键(shortcuts.boxSelect,默认 Shift)拖动鼠标框选要素;缺省关,写 boxSelect: true 或 features: { boxSelect: true } 才开。框选默认只收节点,selectionKinds 可把线路 / 通道也纳入" },
436
- { name: "multiSelect", type: "boolean", defaultValue: "false", doc: "多选开关:打开后不用按修饰键,直接单击节点 / 线路就取反累加选中(与按住 shortcuts.multiSelect 点击同一套行为、同一个 multiSelectStyle;取反那一下不经过 onClick,onSelectChange.action.type 是 toggleSelect);缺省关,关着时多选走框选(boxSelect)与按住多选快捷键(默认 Ctrl / ⌘)。运行时 updateOptions({ multiSelect }) 切换只重画一帧、不重排场景" },
437
- { name: "toggleSelect", type: "boolean", defaultValue: "false", doc: "再次选中即取消:打开后普通单击已选中的节点 / 线路 / 通道会取消,selectTrigger: 'dblClick' 下再次双击取消,Ctrl / ⌘ 先选中后再普通单击也取消;取反时 onClick.action.type 与 onSelectChange.action.type 都是 toggleSelect。缺省关,运行时 updateOptions({ toggleSelect }) 只重画一帧、不重排场景,见「选中与拐点」" },
438
437
  { name: "selectTrigger", type: "'click' | 'dblClick'", defaultValue: "'click'", doc: "选中触发方式:click 第一次单击就改选中集;dblClick 第一下只派发 onClick,等双击判定命中的第二下才改选中集,并同时派发 onClick 与 onDblClick。Ctrl / ⌘ 多选与 multiSelect 在双击模式下同样等第二下才取反。运行时走 updateOptions({ selectTrigger }),只重画一帧、不重排场景" },
439
438
  { name: "selectionKinds", type: "GisViewSelectionKindsInput", defaultValue: "无(框选只收节点;Ctrl 多选收节点 / 线路 / 通道)", doc: "框选与 Ctrl / 多选点击的类型过滤:'point' 节点、'line' 通道外的独立线路、'channel' 通道回线;另有 'lineEndpoints' / 'channelEndpoints',只在框选时要求线路 / 通道两端节点都在框内。可写数组任意组合,'all' / true 全开,false / null 全关。通道回线只归 channel 管,通道外的线路只归 line 管;通道回线仍按 channelSelectMode 摊开。不写时保留旧口径;普通单选与 select / selectFeatures 不受它限制。运行时走 updateOptions({ selectionKinds }),见「框选」与「选择类型过滤」" },
440
439
  { name: "selectionBoxStyle", type: "GisViewSelectionBoxStyle", defaultValue: "蓝底 0.16 透明 + 1px 蓝框", doc: "框选矩形的样式:fill / fillOpacity / stroke / strokeWidth,以及 DOM 渲染用的 render: 'div' / className / borderStyle / radius;canvas 模式不画线型与圆角,见「框选」" },
441
- { name: "selectionBoxPersistent", type: "boolean", defaultValue: "false", doc: "框选结束后是否保留选框:打开后拖动保留框会整组平移框内选中节点,线路与通道跟随;选中集变化、清空、关闭开关或调用 clearSelectionBox() 时收起。要用它同时打开 boxSelect 和 draggable,见「框选」的「保留框与整组拖动」" },
442
- { name: "selectionOnTop", type: "boolean", defaultValue: "true", doc: "选中的节点 / 拖动中的整组 / 待确认合并的目标节点,以及选中线路所在的整条通道,是否画在所有要素之上(与 zIndex 无关),见「选中 / 拖动的层级」" },
440
+ { name: "selectionBoxPersistent", type: "boolean", defaultValue: "false", doc: "框选结束后是否保留选框:打开后拖动保留框会整组平移框内选中节点,线路与通道跟随;选中集变化、清空、关闭开关或调用 clearSelectionBox() 时收起。要用它同时打开 features: { boxSelect: true, drag: true },见「框选」的「保留框与整组拖动」" },
441
+ { name: "selectionOnTop", type: "boolean", defaultValue: "true", doc: "选中的节点 / 拖动中的整组 / 待确认合并的目标节点,以及选中线路所在的整条通道,是否连同各自的 decorations 一起画在所有要素之上(与 zIndex 无关),见「选中 / 拖动的层级」" },
443
442
  { name: "channelSelectMode", type: "'line' | 'group' | 'channel'", defaultValue: "'line'", doc: "通道选中模式:点中通道里的一条回线时选中什么 —— 'line' 只选这一条、'group' 连它所在的分组一起、'channel' 整条通道(所有分组的回线);通道以外的线路永远只选自己。普通点选与 Ctrl / ⌘ + 点击加点共用这一档高亮范围;Ctrl / ⌘ + 点击在 group / channel 下按整批取反;切换走 updateOptions({ channelSelectMode }),只重画一帧、不重排场景,见「通道选中模式」" },
444
443
  { name: "channelWaypointDragMode", type: "'parallel' | 'vertex'", defaultValue: "'parallel'", doc: "分组拐点拖动模式:'parallel' 沿用共用骨架 + 平行偏移(和原有逻辑一致),'vertex' 只平移当前分组同一 pathIndex 的拐点,其他拐点与分组不动;普通线路始终只动自己。vertex 的结果写进 groups[].waypointOffsets,切换走 updateOptions({ channelWaypointDragMode }),下一次拖动生效,见「分组拐点拖动模式」" },
445
444
  { name: "channelHiddenLineLayout", type: "'preserve' | 'center'", defaultValue: "'preserve'", doc: "隐藏部分回线 / 分组后车道怎么排:'preserve' 保留原车道槽位,剩余回线不挪位;'center' 只把当前可见回线按原顺序压成连续槽位,再在通道宽度内重新居中。运行时走 updateOptions({ channelHiddenLineLayout }),见「隐藏回线后的车道」" },
446
- { name: "waypointEditable", type: "boolean", defaultValue: "false", doc: "是否允许编辑线路拐点(按住加点快捷键 shortcuts.addWaypoint(默认 Ctrl / ⌘)+ 点击添加、拖动移动、双击或删除快捷键删除),单条线可用 waypointEditable 覆盖;缺省关,写 waypointEditable: true 或 features: { waypoint: true } 才开。false(以及 features: { waypoint: false })会同时关闭新增、移动与删除,没有只关其中一项的细分开关;shortcuts.addWaypoint 仍只决定加点的组合键" },
447
445
  { name: "waypointVisibility", type: "'selected' | 'always'", defaultValue: "'selected'", doc: "拐点显示时机:selected 只显示选中线路(以及刚加过拐点的线路)的拐点,always 全部显示" },
448
446
  { name: "waypointStyle", type: "GisViewPointStyle", defaultValue: "无", doc: "拐点(线路 path 的中间顶点)的默认 / 选中样式,支持配置:字段与节点样式同款;缺省是白底蓝圈的 9px 圆点,waypointSelectStyle 没配时选中拐点用内置强调环,见「线路拐点」的「拐点样式」小节" },
449
447
  { name: "waypointSelectStyle", type: "GisViewPointStyle", defaultValue: "无", doc: "拐点(线路 path 的中间顶点)的默认 / 选中样式,支持配置:字段与节点样式同款;缺省是白底蓝圈的 9px 圆点,waypointSelectStyle 没配时选中拐点用内置强调环,见「线路拐点」的「拐点样式」小节" },
@@ -454,10 +452,7 @@ export const API_OPTIONS = [
454
452
  { name: "networkVisible", type: "boolean", defaultValue: "true", doc: "网架整体显隐:false 时节点、线路、通道、装饰、动画、标签与框选框一起不画,只保留背景、网格与自定义 WebGL 层;数据、选中与历史都不动,拾取 / 框选 / queryRect 也不再命中网架,适合先铺网架再添加背景" },
455
453
  { name: "networkOpacity", type: "number", defaultValue: "1", doc: "网架整体不透明度(夹在 0 ~ 1):在每类要素自身 opacity 之后再乘一层,节点、线路、通道、装饰、动画与标签一起变淡,背景不受影响。0 只是画面上看不见,拾取仍按网架存在;要完全收起请用 networkVisible: false" },
456
454
  { name: "backgroundEditing", type: "boolean", defaultValue: "false", doc: "背景编辑模式:true 时节点、线路、通道的悬停、点击、选中、拖拽、拐点编辑、框选、右键与数据快捷键全部停用,仍保留滚轮缩放与 fitView,方便让网架和背景一起缩放后只移动 / 旋转 / 缩放背景;缺省 false,可用 updateOptions() 随时开关" },
457
- { name: "interactive", type: "boolean", defaultValue: "(缺省全关)", doc: "查看类交互的「详细配置手动打开」写法:写 true 打开拖拽平移 / 滚轮缩放 / 右键回调(等价 pan / zoom / contextMenu 三个开关),不写就是全关" },
458
- { name: "preventContextMenu", type: "boolean", defaultValue: "true", doc: "组件接住右键时是否收掉浏览器的系统菜单(contextmenu 的默认行为):contextMenu 开着(或 interactive: true / features: 'view')时组件才接右键,接住时一律 preventDefault(),点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 false(onContextMenu 照常发)。右键整个不接时(contextMenu 缺省关)系统菜单自然照弹" },
459
- { name: "clickable", type: "boolean", defaultValue: "(缺省全关)", doc: "查看类交互的「详细配置手动打开」写法:写 true 打开悬停高亮与点击选中(等价 hover / select 两个开关),要素自身可用 clickable: false 逐个关掉" },
460
- { name: "draggable", type: "boolean", defaultValue: "false", doc: "点是否可拖拽,单个点可用 draggable 覆盖;缺省关,写 draggable: true 或 features: { drag: true } 才开" },
455
+ { name: "preventContextMenu", type: "boolean", defaultValue: "true", doc: "组件接住右键时是否收掉浏览器的系统菜单(contextmenu 的默认行为):只有 features: { contextMenu: true }(或 features: 'view')时才接右键,接住时一律 preventDefault(),点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 false(onContextMenu 照常发)。右键整个不接时系统菜单自然照弹" },
461
456
  { name: "liveDrag", type: "boolean", defaultValue: "true", doc: "拖动节点时线路是否实时跟随:false 时拖动期间线路与通道原地不动,松手(pointerup)后一次性更新到新位置" },
462
457
  { name: "lineFollowsNode", type: "boolean", defaultValue: "true", doc: "线路 / 通道是否跟随节点的生命周期:true 时隐藏、删除、筛选收起节点会连同绑定线路与通道一起处理;false 时节点退场只影响节点,线路 / 通道停在最后一份坐标与解析尺寸。筛选没有显式写 cascade 时,关闭它会把默认联动从 'binding' 改为 false;显式 cascade 始终优先。运行时用 updateOptions({ lineFollowsNode }) 切换,见「筛选」" },
463
458
  { name: "shortcuts", type: "GisViewShortcuts", defaultValue: "见「快捷键」", doc: "所有快捷键都可自定义:框选 / 多选 / 加点 / 复制 / 粘贴 / 取消 / 删除 / 适应视图,没写的动作沿用默认值,写 false 等于关掉这个动作的快捷键" },