gis-view-webgl2 0.1.4 → 0.1.6

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 (37) hide show
  1. package/README.md +10 -7
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.d.ts +225 -20
  4. package/dist/index.js +1 -1
  5. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +109 -0
  6. package/docs/GisView.md +53 -53
  7. 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 +91 -0
  8. package/docs/demos/ConnectDemo.vue +114 -3
  9. package/docs/demos/MinimapDemo.vue +216 -17
  10. package/docs/demos/SearchFilterDemo.vue +113 -1
  11. package/docs/demos/api-index.js +28 -20
  12. package/docs/demos/demos.js +1 -1
  13. package/docs/demos/gis-view-demo/GisViewDemoStage.vue +25 -16
  14. package/docs/demos/gis-view-demo/useGisViewDemo.js +27 -16
  15. package/docs/index.html +3 -3
  16. package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +8 -5
  17. package/docs/internals/16-01-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/212.md +18 -5
  18. package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +11 -1
  19. package/docs/internals/18-/346/200/247/350/203/275/345/256/236/346/265/213/344/270/216WebGPU/350/257/204/344/274/260.md +73 -1
  20. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +14 -5
  21. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +2 -2
  22. 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 +1 -1
  23. package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +1 -1
  24. 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 +423 -3
  25. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +99 -30
  26. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +12 -7
  27. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +14 -6
  28. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +137 -6
  29. 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 +4 -0
  30. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +8 -3
  31. 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 +6 -3
  32. package/docs/steps/README.md +6 -6
  33. package/docs/steps/selftest-demos.html +252 -9
  34. package/docs/steps/selftest-docs.html +3 -3
  35. package/docs/steps/selftest-full.html +357 -13
  36. package/docs/steps/selftest-perf.html +599 -382
  37. package/package.json +1 -1
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
11
11
  import { createGisView } from '../../src/index.js'
12
- import { buildDemoData } from './shared/demoData.js'
12
+ import { buildDemoData, DEMO_CENTER } from './shared/demoData.js'
13
13
  import './shared/demoStyle.css'
14
14
 
15
15
  const emit = defineEmits(['notice'])
@@ -44,8 +44,15 @@ const searchResult = ref(null)
44
44
  const filterResult = ref(null)
45
45
  const visibleDataText = ref('未读取')
46
46
  const activeFilter = ref('none')
47
+ const cameraReadout = ref('cameraAnimating: false')
48
+ const apiReadout = ref('dataVersion: -')
49
+ const lineStyleReadout = ref('getLineStyle(0): -')
50
+ const lastDataVersion = ref(null)
51
+ const lineStyleWarm = ref(false)
47
52
  let view = null
48
53
  let requestSeq = 0
54
+ let cameraReadoutFrame = 0
55
+ let pointTweakCursor = 0
49
56
 
50
57
  const SCOPES = [
51
58
  { id: 'all', label: '全部四类' },
@@ -186,6 +193,59 @@ const focusAllHits = () => {
186
193
  view?.focusOn(hits, { select: true, zoom: true })
187
194
  actionText.value = `focusOn(hits, { select: true, zoom: true }):${hits.length} 条命中一起进包围盒并选中`
188
195
  }
196
+ const syncCameraReadout = () => {
197
+ const animation = view?.getCameraAnimation() ?? null
198
+ cameraReadout.value = animation
199
+ ? `cameraAnimating: true · ${Math.round(animation.progress * 100)}% · ${animation.duration} ms`
200
+ : `cameraAnimating: ${view?.cameraAnimating === true} · 没有飞行`
201
+ }
202
+ const syncApiReadout = () => {
203
+ const data = view?.getData()
204
+ const style = view?.getLineStyle(0) ?? null
205
+ apiReadout.value = `dataVersion=${view?.dataVersion ?? '-'} · onDataChange=${lastDataVersion.value ?? '-'} · ${data?.points.length ?? 0} 节点 / ${data?.lines.length ?? 0} 线路`
206
+ lineStyleReadout.value = style
207
+ ? `getLineStyle(0):width=${style.width.toFixed(2)} · color=[${style.color.map(value => value.toFixed(2)).join(', ')}] · opacity=${style.opacity.toFixed(2)} · zIndex=${style.zIndex}`
208
+ : 'getLineStyle(0):null'
209
+ }
210
+ const pumpCameraReadout = () => {
211
+ if (cameraReadoutFrame) {
212
+ cancelAnimationFrame(cameraReadoutFrame)
213
+ }
214
+ const tick = () => {
215
+ cameraReadoutFrame = 0
216
+ syncCameraReadout()
217
+ if (view?.cameraAnimating) {
218
+ cameraReadoutFrame = requestAnimationFrame(tick)
219
+ }
220
+ }
221
+ cameraReadoutFrame = requestAnimationFrame(tick)
222
+ }
223
+ const flyToAllHits = () => {
224
+ const hits = searchResult.value?.hits ?? []
225
+ if (!hits.length) {
226
+ actionText.value = '当前没有可飞向的命中'
227
+ return
228
+ }
229
+ view?.flyTo(hits, { select: true, zoom: true, duration: 700, easing: 'ease-in-out' })
230
+ actionText.value = `flyTo(hits, { select: true, zoom: true, duration: 700 }):${hits.length} 条命中飞向同一包围盒`
231
+ syncCameraReadout()
232
+ pumpCameraReadout()
233
+ }
234
+ const flyToCameraPreset = () => {
235
+ if (!view) {
236
+ return
237
+ }
238
+ const scale = Math.max((view.getZoomBaseScale() || 1) * 1.6, 1)
239
+ view.flyToCamera({ center: [...DEMO_CENTER], scale }, { duration: 600, easing: 'ease-out' })
240
+ actionText.value = `flyToCamera({ center: [${DEMO_CENTER.join(', ')}], scale: ${scale.toFixed(2)} }, { duration: 600 }):不经过要素解析,直接飞相机`
241
+ syncCameraReadout()
242
+ pumpCameraReadout()
243
+ }
244
+ const stopCameraFlight = () => {
245
+ view?.stopCameraAnimation()
246
+ syncCameraReadout()
247
+ actionText.value = 'stopCameraAnimation():停在当前帧,不补跳到终点'
248
+ }
189
249
  const readVisibleData = () => {
190
250
  if (!view) {
191
251
  return null
@@ -214,6 +274,35 @@ const applyFilter = (filter) => {
214
274
  filterResult.value = view.getFilterResult()
215
275
  readVisibleData()
216
276
  }
277
+ const bumpDataVersion = () => {
278
+ if (!view) {
279
+ return
280
+ }
281
+ const points = view.getData().points
282
+ if (!points.length) {
283
+ return
284
+ }
285
+ const item = points[pointTweakCursor % points.length]
286
+ pointTweakCursor += 1
287
+ const nextLoading = Number(((Number(item.max_loading_rate) || 0) + 0.01).toFixed(3))
288
+ view.updatePoint(item.id ?? pointTweakCursor - 1, { max_loading_rate: nextLoading })
289
+ actionText.value = `updatePoint(${item.id ?? pointTweakCursor - 1}, { max_loading_rate: ${nextLoading} }):数据变了,dataVersion 应 +1`
290
+ syncApiReadout()
291
+ }
292
+ const toggleLineStyle = () => {
293
+ if (!view) {
294
+ return
295
+ }
296
+ lineStyleWarm.value = !lineStyleWarm.value
297
+ if (lineStyleWarm.value) {
298
+ view.updateLineStyle({ color: '#f59f4b', width: 3.2, opacity: 1 })
299
+ }
300
+ else {
301
+ view.updateLineStyle({ color: 'rgba(120, 168, 226, 0.43)', width: 1.2, opacity: 0.43 })
302
+ }
303
+ actionText.value = `updateLineStyle(${lineStyleWarm.value ? '暖色 + 3.2px' : '恢复 1.2px' }):只重算线宽 / 颜色链,dataVersion 不变`
304
+ syncApiReadout()
305
+ }
217
306
 
218
307
  onMounted(() => {
219
308
  view = createGisView(container.value, {
@@ -241,6 +330,10 @@ onMounted(() => {
241
330
  onReady: () => {
242
331
  emit('notice', `${dataset.points.length} 节点 / ${dataset.lines.length} 线路,搜索与筛选都是只读口径`)
243
332
  },
333
+ onDataChange: payload => {
334
+ lastDataVersion.value = payload.dataVersion
335
+ syncApiReadout()
336
+ },
244
337
  onFilter: result => syncFilterReadout(result),
245
338
  onWarning: text => {
246
339
  actionText.value = `warning:${text}`
@@ -248,10 +341,15 @@ onMounted(() => {
248
341
  })
249
342
  runSearch('sync')
250
343
  readVisibleData()
344
+ syncApiReadout()
251
345
  })
252
346
 
253
347
  onBeforeUnmount(() => {
254
348
  requestSeq += 1
349
+ if (cameraReadoutFrame) {
350
+ cancelAnimationFrame(cameraReadoutFrame)
351
+ cameraReadoutFrame = 0
352
+ }
255
353
  view?.destroy()
256
354
  view = null
257
355
  })
@@ -301,9 +399,12 @@ onBeforeUnmount(() => {
301
399
  <p class='dv-hint'>scope 可写 points / lines / channels / groups,也认 point / line / channel / group;显式 fields 能搜默认会跳过的 symbol。搜索基于全量数据,不隐藏、也不自动移动视角。</p>
302
400
  <div class='dv-row'>
303
401
  <button type='button' class='dv-button' data-demo='search-filter-focus-all' @click='focusAllHits'>聚焦并选中全部命中</button>
402
+ <button type='button' class='dv-button dv-button--primary' data-demo='search-filter-fly-all' @click='flyToAllHits'>飞向选中(flyTo)</button>
403
+ <button type='button' class='dv-button' data-demo='search-filter-stop-flight' @click='stopCameraFlight'>停止飞行</button>
304
404
  </div>
305
405
  <p class='dv-readout' data-demo='search-filter-summary'>{{ searchSummary }}</p>
306
406
  <p class='dv-readout' data-demo='search-filter-meta'>{{ searchMeta }}</p>
407
+ <p class='dv-readout' data-demo='search-filter-camera'>{{ cameraReadout }}</p>
307
408
  <p class='dv-hint' data-demo='search-filter-action'>{{ actionText }}</p>
308
409
  <ul v-if='hitRows.length' class='sf-hits'>
309
410
  <li v-for='row in hitRows' :key='row.key'>
@@ -335,6 +436,17 @@ onBeforeUnmount(() => {
335
436
  </div>
336
437
  <p class='dv-hint' data-demo='search-filter-filter-action'>{{ filterAction }}</p>
337
438
  </div>
439
+ <div class='dv-group'>
440
+ <h3>相机、版本与线路读数<span class='dv-chip'>flyToCamera</span></h3>
441
+ <div class='dv-row'>
442
+ <button type='button' class='dv-button dv-button--primary' data-demo='search-filter-fly-camera' @click='flyToCameraPreset'>飞向固定相机</button>
443
+ <button type='button' class='dv-button' data-demo='search-filter-data-bump' @click='bumpDataVersion'>改一个点(dataVersion +1)</button>
444
+ <button type='button' class='dv-button' data-demo='search-filter-line-recolor' @click='toggleLineStyle'>线路换色 / 加宽</button>
445
+ </div>
446
+ <p class='dv-readout' data-demo='search-filter-data-version'>{{ apiReadout }}</p>
447
+ <p class='dv-readout' data-demo='search-filter-line-style'>{{ lineStyleReadout }}</p>
448
+ <p class='dv-hint'>flyToCamera 直接收 <code>{ center?, scale? }</code>,没写的字段沿用当前相机;updateLineStyle 只改全局线宽 / 颜色时走窄路,dataVersion 不增加,getLineStyle 仍能读到最新解析值。</p>
449
+ </div>
338
450
  <div class='dv-group'>
339
451
  <h3>两件事别混用</h3>
340
452
  <p class='dv-hint'>search 只把要素找出来,结果带 bounds;要居中就再调 focusOn。setFilter 只隐藏显示、不删数据,也不清空选中集:条件放宽后,原本选中的要素仍保持选中。</p>
@@ -7,8 +7,8 @@
7
7
  * 源码或文档改过之后重跑一遍;`node docs/steps/check-doc-coverage.mjs` 会对账。
8
8
  */
9
9
 
10
- /** { members: 256, methods: 253, properties: 3, options: 96, sections: 16 } */
11
- export const API_SUMMARY = {"members":256,"methods":253,"properties":3,"options":96,"sections":16}
10
+ /** { members: 264, methods: 259, properties: 5, options: 96, sections: 16 } */
11
+ export const API_SUMMARY = {"members":264,"methods":259,"properties":5,"options":96,"sections":16}
12
12
 
13
13
  /** 实例成员,按 docs/GisView.md「参数速查」的分组排 */
14
14
  export const API_SECTIONS = [
@@ -51,16 +51,21 @@ export const API_SECTIONS = [
51
51
  { name: "zoomTo", kind: "method", params: "scale: number 目标缩放值(世界单位 → CSS 像素的比例);origin?: [x, y] 缩放中心", returns: "this", doc: "直接设成指定比例(像素 / 世界单位)", examples: [{"group":"相机与视图","code":"// zoomTo(scale, origin?):直接给目标比例(世界单位 → CSS 像素)\nview.zoomTo(12)\n","from":"header"}] },
52
52
  { name: "zoomToBounds", kind: "method", params: "bounds: GisViewBounds 要装进画面的世界矩形 [minX, minY, maxX, maxY](也认 { minX, minY, maxX, maxY });padding?: number 四周留白", returns: "this", doc: "把一段世界坐标矩形适应到视图内,退化成一点时只居中不改比例", examples: [{"group":"相机与视图","code":"// zoomToBounds(bounds, padding?):把世界矩形装进画面(数组与对象写法都认)\nview.zoomToBounds([0, 0, 400, 200], 40)\nview.zoomToBounds({ minX: 116, minY: 39, maxX: 117, maxY: 40 })\n","from":"header"}] },
53
53
  { name: "focusOn", kind: "method", params: "target: GisViewFocusTargets 要对准的东西:id / { type: 'point' | 'line' | 'channel' | 'group', id | index } / { channelId }(整条通道)/ { type: 'group', channelId?, groupId } / [x, y] 坐标 / 矩形 / { lngLat } / { center },或者一批(数组取整体包围盒)。字符串按「先节点、再线路」,裸字符串不能表示通道;同 id 用显式 type 消歧,分组必须用 type: 'group'(groupId 全局唯一时可省略 channelId)。缺省只居中、比例一点不动;options.zoom: true 才按解析出的真实范围适配,四类目标共用这一条、没有额外放大倍率;scale 优先级高于 zoom,maxZoom 只给单点 / 单线这类零面积目标补最小范围。options?: GisViewFocusOptions:zoom: true 显式适配、scale 直接给比例、center: true 显式只居中(与缺省一致)、padding 要四周留白(CSS 像素,缺省 40)、maxZoom 限制这次适配的最大比例、select 顺带选中(true 选这次目标,也可另给一批写法)、mode 决定怎么并进当前选中集。目标认不出来或参数非法时什么都不做", returns: "this(相机更新后发 onCameraChange;select 生效时另发 onSelect / onSelectChange)", doc: "把视角移到目标上:单条(搜索命中 / 要素 id / { type: 'point' | 'line' | 'channel', id | index } / { channelId } 整条通道 / { type: 'group', channelId?, groupId } / 坐标 / 矩形 / { lngLat })或一批(数组取整体包围盒 —— 搜到三个节点就是这三个点的中心当屏幕中心);字符串先节点后线路,裸字符串不表示通道,同 id 用 type 消歧,分组必须显式写 type: 'group'(全局唯一 groupId 可省略通道)。缺省只居中、比例一点不动;options.zoom: true 才按解析出的真实范围适配(节点 / 线路 / 通道 / 分组共用这一条,不额外放大),scale 优先级高于 zoom。options 还认 center: true(显式只居中,与缺省一致)、padding、maxZoom、select(顺带选中这批,mode 决定怎么并进当前选中集)", examples: [{"group":"相机与视图","code":"// focusOn(target, options?):对准一个或多个目标(id / 显式类型 / 坐标 / 矩形 / 搜索命中 / 一批)\nview.focusOn('a') // 缺省只居中,比例不动\nview.focusOn('a', { zoom: true, maxZoom: 8 }) // 显式按范围适配\nview.focusOn(['a', 'b'], { zoom: true, padding: 60, select: true })\nview.focusOn({ type: 'point', id: 'a' }) // 节点与线路同 id 时显式消歧\nview.focusOn({ type: 'line', id: 'a' })\nview.focusOn({ type: 'channel', channelId: 'c1' }) // 通道也可以直接写 { channelId: 'c1' }\nview.focusOn({ type: 'group', channelId: 'c1', groupId: 'c1::2' })\nview.focusOn({ type: 'group', groupId: 'global-group' }) // groupId 全局唯一时可省略通道\nview.focusOn([120, 80], { center: true }) // 显式只居中(与缺省一致)\nview.focusOn({ lngLat: [116.4, 39.9] })\n","from":"header"}] },
54
+ { name: "flyTo", kind: "method", params: "目标与世界坐标解析、zoom / scale / center / padding / maxZoom / select / mode 均与 focusOn 同义。options?: GisViewFlyOptions 另认 duration(毫秒,缺省 450;<= 0 立即落位)与 easing('linear' / 'ease-in' / 'ease-out' / 'ease-in-out',缺省 'ease-in-out',也可给自定义 (progress) => eased)。中心线性、比例按对数插值;系统 prefers-reduced-motion: reduce 时立即落位。再次 flyTo、用户拖拽 / 滚轮、setCamera / centerOn / zoom* / fitView / resetView / applyInitialView 都会停在当前帧", returns: "this(相机变化时发 onCameraChange;select 生效时另发选中事件)", doc: "平滑飞到与 focusOn 相同的目标:目标写法与 zoom / scale / center / padding / maxZoom / select / mode 完全同义,另认 duration(毫秒,缺省 450)与 easing(linear / ease-in / ease-out / ease-in-out 或 (progress) => progress)。中心线性插值、比例按对数插值;duration <= 0 或系统要求减少动态效果时立即落位。再次飞行、用户拖拽 / 滚轮或任何程序化改相机都会停在当前帧,不补跳到终点", examples: [{"group":"相机与视图","code":"// flyTo(target, options?):与 focusOn 同一套目标解析,平滑飞过去\nview.flyTo(hit, { zoom: true, duration: 600, easing: 'ease-in-out' })\nview.flyTo(['station-1', 'station-2'], { select: true, duration: 0 }) // 立即落位\nview.flyTo({ center: [120, 80], scale: 12, easing: progress => progress * progress })\n","from":"header"}] },
55
+ { name: "flyToCamera", kind: "method", params: "camera: GisViewCameraTarget 直接给相机目标 { center?, scale? }:没写 center / scale 的那一项沿用当前相机,不经过要素解析,也不改选中与数据;options?: GisViewFlyCameraOptions 的 duration / easing 与 flyTo 同义。中心 / 比例插值、减少动态效果、用户手势取消都走同一条相机动画", returns: "this(相机变化时发 onCameraChange;目标非法时保持当前相机)", doc: "直接飞到相机目标 { center?, scale? }:不写的那一项沿用当前相机,不经过要素解析、也不改选中与数据;duration / easing 与 flyTo 同义,参数非法时保持当前相机", examples: [{"group":"相机与视图","code":"// flyToCamera(camera, options?):直接飞相机目标;没写的 center / scale 沿用当前相机\nview.flyToCamera({ center: [120, 80], scale: 3 }, { duration: 600, easing: 'ease-out' })\nview.flyToCamera({ scale: 5 }, { duration: 0 }) // 中心不动,比例立即落到 5\nview.flyToCamera({ center: [116.4, 39.9] }) // 比例沿用当前相机\n","from":"header"}] },
54
56
  { name: "search", kind: "method", params: "input: string | GisViewSearchOptions 关键字,或 { keyword, scope, fields, limit } 这类选项。scope 可选 'points' / 'lines' / 'channels' / 'groups',也认与 focusOn 一致的单数别名 'point' / 'line' / 'channel' / 'group' 及数组;省略或写错时四类全搜。limit 对每一类分别计数。返回的 groups / hits 带分组命中(channelIndex / groupId / groupIndex,item 是轻量快照)—— 只返回结果、不动视角,要定位就自己调 focusOn(result.hits, …)", returns: "GisViewSearchResult:keyword / scope / points / lines / channels / groups / hits / total / truncated / durationMs", doc: "按关键字搜索节点 / 线路 / 通道 / 通道分组并返回命中:scope 认复数的 points / lines / channels / groups,也认与 focusOn 一致的单数别名 point / line / channel / group;结果分 points / lines / channels / groups 四类,分组命中带 channelIndex / groupId / groupIndex,可直接交给 focusOn / selectFeatures。不动视角:要不要聚焦由调用方自己调 focusOn,见「搜索与定位」", examples: [{"group":"相机与视图","code":"// search(input) / searchAsync(input):搜索(只返回结果、不动视角)\nconst hits = view.search('甲站')\nconst scoped = view.search({ keyword: '甲', scope: ['points', 'channels', 'groups'], limit: 10 })\nconst groupHits = view.search({ keyword: 'c1::2', scope: 'group' }) // 单数别名与 focusOn 一致\nview.focusOn(groupHits.groups[0], { zoom: true, select: true }) // 按整组范围适配并选中成员回线\nview.focusOn(hits.hits, { zoom: true, maxZoom: 8 }) // 命中集直接丢给 focusOn\nconst asyncHits = await view.searchAsync('甲站') // 大表走 Worker;stale 说明这一份不作数了\n","from":"header"}] },
55
57
  { name: "searchAsync", kind: "method", params: "同上;分组范围的当前轻量快照会随请求交给 Worker", returns: "Promise<GisViewSearchResult>:多一个 worker(是不是在 Worker 里算的)与 stale(回来时数据已经改过、或又搜过一次,这一份不作数了)", doc: "同一份口径的异步版:数据量大时把扫描放进 Worker(10 万点 / 10 万线下同步要占主线程 40 ms 上下的那一段),返回 Promise<GisViewSearchResult>;结果带 worker 与 stale 两个字段,见「异步搜索」", examples: [{"group":"相机与视图","code":"// search(input) / searchAsync(input):搜索(只返回结果、不动视角)\nconst hits = view.search('甲站')\nconst scoped = view.search({ keyword: '甲', scope: ['points', 'channels', 'groups'], limit: 10 })\nconst groupHits = view.search({ keyword: 'c1::2', scope: 'group' }) // 单数别名与 focusOn 一致\nview.focusOn(groupHits.groups[0], { zoom: true, select: true }) // 按整组范围适配并选中成员回线\nview.focusOn(hits.hits, { zoom: true, maxZoom: 8 }) // 命中集直接丢给 focusOn\nconst asyncHits = await view.searchAsync('甲站') // 大表走 Worker;stale 说明这一份不作数了\n","from":"header"}] },
56
58
  { name: "setCamera", kind: "method", params: "camera: Partial<GisViewCamera> 要改的相机字段 { x, y, scale }(只写一个也行)", returns: "this", doc: "读写 { center, scale }", examples: [{"group":"相机与视图","code":"// setCamera(camera) / getCamera():读写相机(只写一个字段也行)\nview.setCamera({ scale: 10 })\nview.setCamera({ x: 200, y: 120 })\nconst camera = view.getCamera()\n","from":"header"}] },
57
59
  { name: "getCamera", kind: "method", params: "无", returns: "GisViewCamera", doc: "读写 { center, scale }", examples: [{"group":"相机与视图","code":"// setCamera(camera) / getCamera():读写相机(只写一个字段也行)\nview.setCamera({ scale: 10 })\nview.setCamera({ x: 200, y: 120 })\nconst camera = view.getCamera()\n","from":"header"}] },
60
+ { name: "stopCameraAnimation", kind: "method", params: "无", returns: "this:停止当前相机飞行并停在当前帧;没有飞行时是空操作", doc: "停止当前相机飞行(停在当前帧,返回实例)/ 读当前飞行 { active, progress, duration, from, to }(没有动画时 null)", examples: [{"group":"相机与视图","code":"// stopCameraAnimation() / getCameraAnimation():停止飞行 / 读当前飞行读数(没有动画时为 null)\nconst flight = view.getCameraAnimation() // { active, progress, duration, from, to }\nview.stopCameraAnimation() // 停在当前帧,不补跳到终点\n","from":"header"}] },
61
+ { name: "getCameraAnimation", kind: "method", params: "无", returns: "GisViewCameraAnimation | null:{ active, progress, duration, from, to },progress 是时间进度(0 ~ 1),没有动画时 null", doc: "停止当前相机飞行(停在当前帧,返回实例)/ 读当前飞行 { active, progress, duration, from, to }(没有动画时 null)", examples: [{"group":"相机与视图","code":"// stopCameraAnimation() / getCameraAnimation():停止飞行 / 读当前飞行读数(没有动画时为 null)\nconst flight = view.getCameraAnimation() // { active, progress, duration, from, to }\nview.stopCameraAnimation() // 停在当前帧,不补跳到终点\n","from":"header"}] },
58
62
  { name: "getZoomBaseScale", kind: "method", params: "无", returns: "number 缩放联动的基准比例(pointZoomScale / lineZoomScale 共用那一份;缺省是首次适应视图时的 camera.scale)", doc: "读 / 重定缩放联动的基准比例(缺省是首次适应视图时的比例):不传参数就取当下相机的比例 —— 把现在这一屏当成「原样大小」,之后放大缩小都从它算倍数;配合 pointZoomScale / lineZoomScale 使用", examples: [{"group":"相机与视图","code":"// getZoomBaseScale() / setZoomBaseScale(scale?):缩放联动的基准比例(配合 pointZoomScale / lineZoomScale)\nconst zoomBase = view.getZoomBaseScale() // 基准(缺省首次适应视图时的 camera.scale)\nview.setZoomBaseScale() // 以当下这一屏为「原样大小」,之后放大缩小都从它算倍数\nview.setZoomBaseScale(2.5) // 也可以直接给一个比例\n","from":"header"}] },
59
63
  { name: "setZoomBaseScale", kind: "method", params: "scale?: number 新的基准比例;不传取当下相机的比例(把现在这一屏当成「原样大小」)", returns: "this", doc: "读 / 重定缩放联动的基准比例(缺省是首次适应视图时的比例):不传参数就取当下相机的比例 —— 把现在这一屏当成「原样大小」,之后放大缩小都从它算倍数;配合 pointZoomScale / lineZoomScale 使用", examples: [{"group":"相机与视图","code":"// getZoomBaseScale() / setZoomBaseScale(scale?):缩放联动的基准比例(配合 pointZoomScale / lineZoomScale)\nconst zoomBase = view.getZoomBaseScale() // 基准(缺省首次适应视图时的 camera.scale)\nview.setZoomBaseScale() // 以当下这一屏为「原样大小」,之后放大缩小都从它算倍数\nview.setZoomBaseScale(2.5) // 也可以直接给一个比例\n","from":"header"}] },
60
- { name: "getBounds", kind: "method", params: "无", returns: "GisViewBounds", doc: "数据范围(没有点时回退到背景 GeoJSON 的 rawBounds,也就是 aspectScale 之前的真实经纬度范围)", examples: [{"group":"相机与视图","code":"// getBounds() / getViewRect() / getViewport():数据范围 / 视口世界矩形 / 画布尺寸读数\nconst dataBounds = view.getBounds()\nconst viewRect = view.getViewRect()\nconst { cssWidth, cssHeight, pixelRatio } = view.getViewport()\n","from":"header"}] },
64
+ { name: "getBounds", kind: "method", params: "无", returns: "GisViewBounds:每次返回独立副本;重复调用命中数据包围盒缓存,改点 / 改线 / 追加或拖拽后下一次读数自动重建", doc: "数据范围(没有点时回退到背景 GeoJSON 的 rawBounds,也就是 aspectScale 之前的真实经纬度范围);每次返回独立副本,重复调用命中缓存", examples: [{"group":"相机与视图","code":"// getBounds() / getViewRect() / getViewport():数据范围 / 视口世界矩形 / 画布尺寸读数\nconst dataBounds = view.getBounds()\nconst viewRect = view.getViewRect()\nconst { cssWidth, cssHeight, pixelRatio } = view.getViewport()\n","from":"header"}] },
61
65
  { name: "getViewport", kind: "method", params: "无", returns: "{ cssWidth, cssHeight, pixelRatio, displayScale }:画布 CSS 尺寸、设备像素比与渲染分辨率比例", doc: "/** * 当前视口。", examples: [{"group":"相机与视图","code":"// getBounds() / getViewRect() / getViewport():数据范围 / 视口世界矩形 / 画布尺寸读数\nconst dataBounds = view.getBounds()\nconst viewRect = view.getViewRect()\nconst { cssWidth, cssHeight, pixelRatio } = view.getViewport()\n","from":"header"}] },
62
66
  { name: "getViewRect", kind: "method", params: "无", returns: "GisViewBounds", doc: "当前可视范围的世界坐标矩形;自建缩略图 / 坐标读数用它换算视口框,内置鹰眼图见「鹰眼图」", examples: [{"group":"相机与视图","code":"// getBounds() / getViewRect() / getViewport():数据范围 / 视口世界矩形 / 画布尺寸读数\nconst dataBounds = view.getBounds()\nconst viewRect = view.getViewRect()\nconst { cssWidth, cssHeight, pixelRatio } = view.getViewport()\n","from":"header"}] },
63
67
  { name: "isLayoutReady", kind: "method", params: "无", returns: "boolean", doc: "大数据发散渲染(progressiveInit)是否已经铺完;没铺完时标签与拾取只看得到已经画出去的那部分", examples: [{"group":"相机与视图","code":"// isLayoutReady():容器量到尺寸、首批布局完成没有\nconst layoutReady = view.isLayoutReady()\n","from":"header"}] },
68
+ { name: "getMinimapElement", kind: "method", params: "无", returns: "HTMLElement | null 鹰眼图根节点(没配 minimap 时为 null);外部整图拖动 / 自定义手势用它,配合 minimap.container 可挂到外部宿主", doc: "鹰眼图根节点(没配 minimap 时为 null):接整图拖动或自定义手势;配合 minimap.container 挂到外部宿主时拿到的仍是同一根节点", examples: [{"group":"相机与视图","code":"// getMinimapElement():拿鹰眼图根节点做整图拖动 / 自定义手势(没配 minimap 时为 null)\nconst minimapElement = view.getMinimapElement()\n","from":"header"}] },
64
69
  { name: "getMinimapZoom", kind: "method", params: "无", returns: "number", doc: "读 / 写鹰眼图自身的倍数:set 夹在 minimap.minZoom ~ maxZoom 之间,zoomMinimapBy 以画布 CSS 像素 origin 为锚点缩放(缺省小图中心),没配 minimap 时是空操作", examples: [{"group":"相机与视图","code":"// getMinimapZoom() / setMinimapZoom(zoom) / zoomMinimapBy(factor, origin?):鹰眼图缩放\nview.setMinimapZoom(4)\nview.zoomMinimapBy(1.25)\nconst minimapZoom = view.getMinimapZoom()\n","from":"header"}] },
65
70
  { name: "setMinimapZoom", kind: "method", params: "zoom: number 鹰眼图的目标倍数(被 minimap.minZoom / maxZoom 夹住)", returns: "this", doc: "读 / 写鹰眼图自身的倍数:set 夹在 minimap.minZoom ~ maxZoom 之间,zoomMinimapBy 以画布 CSS 像素 origin 为锚点缩放(缺省小图中心),没配 minimap 时是空操作", examples: [{"group":"相机与视图","code":"// getMinimapZoom() / setMinimapZoom(zoom) / zoomMinimapBy(factor, origin?):鹰眼图缩放\nview.setMinimapZoom(4)\nview.zoomMinimapBy(1.25)\nconst minimapZoom = view.getMinimapZoom()\n","from":"header"}] },
66
71
  { name: "zoomMinimapBy", kind: "method", params: "factor: number 倍数;origin?: [x, y] 以小图里的哪个点为中心(缺省小图中心)", returns: "this", doc: "读 / 写鹰眼图自身的倍数:set 夹在 minimap.minZoom ~ maxZoom 之间,zoomMinimapBy 以画布 CSS 像素 origin 为锚点缩放(缺省小图中心),没配 minimap 时是空操作", examples: [{"group":"相机与视图","code":"// getMinimapZoom() / setMinimapZoom(zoom) / zoomMinimapBy(factor, origin?):鹰眼图缩放\nview.setMinimapZoom(4)\nview.zoomMinimapBy(1.25)\nconst minimapZoom = view.getMinimapZoom()\n","from":"header"}] },
@@ -105,7 +110,8 @@ export const API_SECTIONS = [
105
110
  { name: "setDropPreview", kind: "method", params: "preview: GisViewDropPreviewInput | null:kind('point' / 'line' / 'channel' / 'waypoint' / 'decoration' / 'label',缺省按 item 推断)、screen(画布 CSS 像素)或 world(世界坐标)至少给一个、item 是准备落地的那一份字段(与 addPoint / addLine 同一份)、from(line / channel 的起点);传 null 清掉预览", returns: "GisViewDropPreview | null 解析后的读数 { kind, screen, world, size, styleSize, radius, color, strokeColor, strokeWidth, opacity, symbol, width, dashed }:走的是与真实要素完全相同的样式链(规则 -> 全局样式 -> 这一份字段),styleSize 与落地后 getPointStyle().size 是同一个数(size 是这一刻画出来的宽度,不悬停 / 不选中时两者相同)", doc: "/** * 拖拽落点预览。", examples: [{"group":"坐标、拾取与读数","code":"// setDropPreview(preview) / getDropPreview():拖拽落点预览(setDropPreview(null) 清掉)\nview.setDropPreview({ kind: 'point', screen: [320, 200], item: { name: '新站' } })\nconst preview = view.getDropPreview()\nview.setDropPreview(null)\n","from":"header"}] },
106
111
  { name: "getDropPreview", kind: "method", params: "无", returns: "GisViewDropPreview | null:当前拖拽落点预览的读数,没有预览时 null", doc: "/** * 拖拽落点预览。", examples: [{"group":"坐标、拾取与读数","code":"// setDropPreview(preview) / getDropPreview():拖拽落点预览(setDropPreview(null) 清掉)\nview.setDropPreview({ kind: 'point', screen: [320, 200], item: { name: '新站' } })\nconst preview = view.getDropPreview()\nview.setDropPreview(null)\n","from":"header"}] },
107
112
  { name: "getPoint", kind: "method", params: "ref: GisViewFeatureRef 节点定位:id、下标、{ id } / { index }", returns: "GisViewPointInfo | null", doc: "按 id / 下标 / { id } / { index } 取一个节点:拾取载荷(item / x / y / layer / layerRank)之外再给一份屏幕半径 radius;找不到返回 null。自建读数面板、右键菜单用它,省掉「自己遍历 getData() + 算投影」那一段", 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"}] },
108
- { name: "getPointStyle", kind: "method", params: "index: number 节点下标", returns: "GisViewPointStyleSnapshot | null:节点解析后的样式快照 { index, size, symbol, zIndex },size / symbol 取样式链(节点自身 > 规则 > pointStyle)定下来的那一份,含 sizeScale、不含悬停 / 选中样式与热力倍率;落点预览的读数与它同源", doc: "/** * 节点解析之后的样式快照(size / symbol / zIndex,含 sizeScale、不含状态样式与热力倍率)。", examples: [{"group":"坐标、拾取与读数","code":"// getPointStyle(index):节点解析后的样式快照(样式链定下来的那一份)\nconst pointStyle = view.getPointStyle(0)\n","from":"header"}] },
113
+ { name: "getPointStyle", kind: "method", params: "index: number 节点下标", returns: "GisViewPointStyleSnapshot | null:节点解析后的样式快照 { index, size, symbol, zIndex },size / symbol 取样式链(节点自身 > 规则 > pointStyle)定下来的那一份,含 sizeScale、不含悬停 / 选中样式与热力倍率;落点预览的读数与它同源", doc: "节点 / 线路解析之后的样式快照:节点的 size / symbol 取样式链(自身 > 规则 > 全局),线路的 width / color / opacity / zIndex 同源;不含悬停 / 选中样式、整体倍率与热力倍率,下标不存在时返回 null。合并节点与鹰眼图沿用主图尺寸时走这一份", examples: [{"group":"坐标、拾取与读数","code":"// getPointStyle(index):节点解析后的样式快照(样式链定下来的那一份)\nconst pointStyle = view.getPointStyle(0)\n","from":"header"}] },
114
+ { name: "getLineStyle", kind: "method", params: "index: number 线路在 lines 里的下标", returns: "GisViewLineStyleSnapshot | null:{ index, width, color, opacity, zIndex };width 取样式链(线路自身 > 规则 > lineStyle)定下来的原始线宽,不含 lineWidthScale / 缩放联动 / 热力倍率,color 是解析后的 RGBA(0 ~ 1)。局部更新与线路窄样式快路径之后读到的都是最新值;下标不存在时 null", doc: "节点 / 线路解析之后的样式快照:节点的 size / symbol 取样式链(自身 > 规则 > 全局),线路的 width / color / opacity / zIndex 同源;不含悬停 / 选中样式、整体倍率与热力倍率,下标不存在时返回 null。合并节点与鹰眼图沿用主图尺寸时走这一份", examples: [{"group":"坐标、拾取与读数","code":"// getLineStyle(index):线路解析后的样式快照(原始线宽 / RGBA / 透明度 / 层级)\nconst lineStyle = view.getLineStyle(0) // { index, width, color, opacity, zIndex } 或 null\n","from":"header"}] },
109
115
  { 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"}] },
110
116
  { 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"}] },
111
117
  { 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"}] },
@@ -176,7 +182,7 @@ export const API_SECTIONS = [
176
182
  { 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"}] },
177
183
  { 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"}] },
178
184
  { name: "getChannels", kind: "method", params: "无", returns: "GisViewChannelInfo[]", doc: "通道运行时状态:骨架折线、每条回线的偏移、推导出来的折线路径与间距;分组之后一个通道的每个分组各一条,带 channelId / channelIndex(id 是分组 id)", examples: [{"group":"拐点与通道","code":"// getChannels() / getChannelMembers() / getChannelsInView():通道全量几何 / 只成员与偏移 / 视口里的通道\nconst channelGeometries = view.getChannels()\nconst channelMembers = view.getChannelMembers()\nconst channelsInView = view.getChannelsInView()\n","from":"header"}] },
179
- { name: "getChannelMembers", kind: "method", params: "无", returns: "GisViewChannelMembers[]:每条通道(分组之后每组各一条)的 { channelId, channelIndex, lineIndexes, offsets } —— 只要成员下标与偏移、不复制骨架 / 折线,自建覆盖层按通道分流绘制用它;整份几何(含 paths)用 getChannels()", doc: "通道成员表:每个通道的成员下标与各自的偏移(不复制几何),需要按通道分流绘制时用它(内置鹰眼图的「一个通道只画一根」就按它挑中间那条)", examples: [{"group":"拐点与通道","code":"// getChannels() / getChannelMembers() / getChannelsInView():通道全量几何 / 只成员与偏移 / 视口里的通道\nconst channelGeometries = view.getChannels()\nconst channelMembers = view.getChannelMembers()\nconst channelsInView = view.getChannelsInView()\n","from":"header"}] },
185
+ { name: "getChannelMembers", kind: "method", params: "无", returns: "GisViewChannelMembers[]:每条通道(分组之后每组各一条)的 { channelId, channelIndex, lineIndexes, offsets } —— 只要成员下标与偏移、不复制骨架 / 折线,自建覆盖层按通道分流绘制用它;整份几何(含 paths)用 getChannels()", doc: "通道成员表:每个通道的成员下标与各自的偏移(不复制几何),需要按通道分流绘制时用它(内置鹰眼图的「一个通道只画一根」按它取当前可见成员,再逐顶点平均成居中代表线)", examples: [{"group":"拐点与通道","code":"// getChannels() / getChannelMembers() / getChannelsInView():通道全量几何 / 只成员与偏移 / 视口里的通道\nconst channelGeometries = view.getChannels()\nconst channelMembers = view.getChannelMembers()\nconst channelsInView = view.getChannelsInView()\n","from":"header"}] },
180
186
  { name: "getChannelGroups", kind: "method", params: "channelId?: string | number 只看这一条通道(不传给全部)", returns: "GisViewChannelGroupInfo[]", doc: "通道分组现状:每组的成员(下标 + 线路 id)、骨架与可以直接写回声明的中间拐点(path);不传 channelId 返回所有分组,见「通道分组」", examples: [{"group":"拐点与通道","code":"// getChannelGroups(channelId?) / getChannelGroupWaypoints(channelId?, groupId?):分组现状 / 某一组的拐点\nconst groups = view.getChannelGroups('c1')\nconst allGroups = view.getChannelGroups()\nconst groupWaypoints = view.getChannelGroupWaypoints('c1', 'c1::2')\n","from":"header"}] },
181
187
  { name: "getChannelGroupWaypoints", kind: "method", params: "channelId?: string | number 通道 id(不传取「当前分组」所在的通道);groupId?: string | number 分组 id(不传取这条通道的第一组)", returns: "GisViewChannelGroupWaypoints | null:整组共用的 path(给后端那一份)+ lines[] 每条回线偏移后的 path / waypoints", doc: "一个分组的拐点现状:整组共用的骨架与中间拐点(path,给后端就是这一份)+ 组里每条回线按各自车道偏移后的折线与拐点(lines[].waypoints);channelId 不传取「当前分组」所在的通道、groupId 不传取第一组,见「分组拐点」", examples: [{"group":"拐点与通道","code":"// getChannelGroups(channelId?) / getChannelGroupWaypoints(channelId?, groupId?):分组现状 / 某一组的拐点\nconst groups = view.getChannelGroups('c1')\nconst allGroups = view.getChannelGroups()\nconst groupWaypoints = view.getChannelGroupWaypoints('c1', 'c1::2')\n","from":"header"}] },
182
188
  { name: "getLineChannel", kind: "method", params: "ref: GisViewFeatureRef 线路定位(id、lines 下标或 { id } / { index })", returns: "{ lineIndex, channelId, channelIndex, channel, groupId, group };这条线不在任何通道里时 null", doc: "这条线路属于哪条通道:getLineChannel('line-a')(id)/ getLineChannel(12)(lines 下标)给 { lineIndex, channelId, channelIndex, channel, groupId, group },不在通道里返回 null。点了一条回线想知道它归哪条通道 / 哪一组时用它,不必自己扫通道定义", examples: [{"group":"拐点与通道","code":"// getLineChannel(ref):这条线属于哪条通道 / 哪个分组(独立线路给 null)\nconst channelOfLine = view.getLineChannel('l1')\n","from":"header"}] },
@@ -193,21 +199,21 @@ export const API_SECTIONS = [
193
199
  { name: "addChannelGroupWaypoint", kind: "method", params: "channelId 通道 id;groupId 分组 id;coordinate 新拐点世界坐标", returns: "this", doc: "给某个分组加拐点、把这一组单独拉走:不给坐标就按默认位置放一个(第 n 组沿法线往外让 n 档),其余分组一根拐点都不动,见「分组拐点」", examples: [{"group":"拐点与通道","code":"// addChannelGroupWaypoint(channelId, groupId, coordinate) / removeChannelGroupWaypoint(channelId, groupId, pathIndex)\nview.addChannelGroupWaypoint('c1', 'c1::2', [200, 120])\nview.removeChannelGroupWaypoint('c1', 'c1::2', 1)\n","from":"header"}] },
194
200
  { name: "removeChannelGroupWaypoint", kind: "method", params: "channelId / groupId / pathIndex 要删的骨架顶点下标", returns: "this", doc: "删掉某个分组的拐点(缺省删最后一个)", examples: [{"group":"拐点与通道","code":"// addChannelGroupWaypoint(channelId, groupId, coordinate) / removeChannelGroupWaypoint(channelId, groupId, pathIndex)\nview.addChannelGroupWaypoint('c1', 'c1::2', [200, 120])\nview.removeChannelGroupWaypoint('c1', 'c1::2', 1)\n","from":"header"}] },
195
201
  { name: "runChannelAction", kind: "method", params: "actionId: GisViewChannelActionId 动作名('select-group' / 'deselect-group' / 'add-waypoint' / 'remove-waypoint' / 'split' / 'merge',见 getChannelContext().actions);options?: GisViewChannelActionOptions 动作自己的参数(groupId / lineId / coordinate…)", returns: "GisViewChannelContext | null", doc: "执行右键交互框里的动作:select-group / deselect-group / add-waypoint / remove-waypoint / split / merge(options 带 channelId / groupId / coordinate / pathIndex / size),返回执行之后的上下文", examples: [{"group":"拐点与通道","code":"// setCurrentChannelGroup(channelId, groupId) / runChannelAction(actionId, options?):当前分组与动作\nview.setCurrentChannelGroup('c1', 'c1::2')\nview.setCurrentChannelGroup('c1', null) // 不选中\nview.runChannelAction('select-group', { groupId: 'c1::2' })\nview.runChannelAction('split', { lineId: 'l2' })\n","from":"header"}] },
196
- { name: "beginAddChannel", kind: "method", params: "options?: GisViewChannelDraftOptions 草稿配置:from(预指定起点,下一步只选终点)/ kind / circuits / autoCommit / continuous / chain / name / lineId / id / lineProps / linePropsByMember / lineItems(第二段)/ routing / path(中间拐点)/ channelProps / channelDraftStyle(本次草稿的预览样式)/ handoff(交接模式)/ select(点中的节点顺手选中,走 onSelect 那一套)/ originalEvent", returns: "this(只负责那条示例假通道,真正新增走 addChannel)", doc: "进入「新增通道」:缺省点两个节点选起点 / 终点,或由 options.from 预指定起点、只需再点终点;options 还支持 kind('channel' 新建通道 / 'line' 新建独立线路,缺省 'channel')/ circuits(一次加几条,缺省 1)/ autoCommit / continuous / chain / name / lineId / id / lineProps / linePropsByMember / lineItems(第二段,连同线路一起新建)/ routing('straight' / 'orthogonal')/ path(中间拐点)/ channelProps / channelDraftStyle(本次草稿的预览样式)/ handoff(交接模式:ready 发完收起草稿,自己调 addChannel 落地)/ select(点中的节点顺手选中)/ originalEvent,见「新增通道」", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?) / commitAddChannel(resolution?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 预指定起点:接下来只点终点\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\nview.commitAddChannel() // 两个节点选完后落地(resolution 缺省用推荐方案)\nview.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2' }) // 指定方案\nview.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)\nview.cancelAddChannel() // 取消(Esc / 右键等价)\n","from":"header"}] },
197
- { name: "getAddChannelDraft", kind: "method", params: "无", returns: "GisViewChannelDraftPayload | null", doc: "当前新增通道草稿(两个节点的信息与可选方案),没有草稿时为 null", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?) / commitAddChannel(resolution?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 预指定起点:接下来只点终点\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\nview.commitAddChannel() // 两个节点选完后落地(resolution 缺省用推荐方案)\nview.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2' }) // 指定方案\nview.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)\nview.cancelAddChannel() // 取消(Esc / 右键等价)\n","from":"header"}] },
198
- { name: "updateAddChannelStyle", kind: "method", params: "patch?: GisViewChannelDraftStyle | null 字段级合并的预览样式(箭头位置 / 多回线间距 / 自绘等);传 null 回到全局 channelDraftStyle", returns: "this", doc: "字段级合并当前草稿的预览样式(color / width / dot / dotColor / arrow / arrowSize / arrowPositions / arrowReverse / arrowColor / circuitSpacing / opacity / customDraw);传 null 回到全局 channelDraftStyle,没有草稿时什么也不做", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?) / commitAddChannel(resolution?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 预指定起点:接下来只点终点\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\nview.commitAddChannel() // 两个节点选完后落地(resolution 缺省用推荐方案)\nview.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2' }) // 指定方案\nview.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)\nview.cancelAddChannel() // 取消(Esc / 右键等价)\n","from":"header"}] },
199
- { name: "continueAddChannel", kind: "method", params: "options?: GisViewChannelDraftOptions 开下一段草稿(写法与 beginAddChannel 一致,不传就沿用这一段原来的)", returns: "this", doc: "开下一段草稿:连续模式落地时内部自动调(发 phase: 'restart'),也可以手动调;草稿已经退出时按 options 重新进来", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?) / commitAddChannel(resolution?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 预指定起点:接下来只点终点\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\nview.commitAddChannel() // 两个节点选完后落地(resolution 缺省用推荐方案)\nview.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2' }) // 指定方案\nview.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)\nview.cancelAddChannel() // 取消(Esc / 右键等价)\n","from":"header"}] },
200
- { name: "cancelAddChannel", kind: "method", params: "无", returns: "this(收掉草稿并补一条 phase: 'cancel')", doc: "取消新增通道(草稿期间按 Esc 等价,会补一条 phase: 'cancel')", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?) / commitAddChannel(resolution?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 预指定起点:接下来只点终点\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\nview.commitAddChannel() // 两个节点选完后落地(resolution 缺省用推荐方案)\nview.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2' }) // 指定方案\nview.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)\nview.cancelAddChannel() // 取消(Esc / 右键等价)\n","from":"header"}] },
201
- { name: "endAddChannel", kind: "method", params: "无", returns: "this(静默收掉视图上的示例假通道 / 线路,数据不动、不补事件)", doc: "结束新增通道:静默收掉视图上的示例假通道 / 线路(不补 cancel 事件),数据一个字节都不改 —— 结束事件里决定「不新建了」就用它", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?) / commitAddChannel(resolution?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 预指定起点:接下来只点终点\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\nview.commitAddChannel() // 两个节点选完后落地(resolution 缺省用推荐方案)\nview.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2' }) // 指定方案\nview.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)\nview.cancelAddChannel() // 取消(Esc / 右键等价)\n","from":"header"}] },
202
- { name: "commitAddChannel", kind: "method", params: "resolution?: GisViewChannelAddResolution 落地方式('channel' / 'line',缺省看草稿);options?: GisViewChannelAddInput 要覆盖的字段(name / circuits / colors / lineWidth / routing / path…)", returns: "GisViewChannelAddResult", doc: "", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?) / commitAddChannel(resolution?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 预指定起点:接下来只点终点\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\nview.commitAddChannel() // 两个节点选完后落地(resolution 缺省用推荐方案)\nview.commitAddChannel({ type: 'joinGroup', groupId: 'c1::2' }) // 指定方案\nview.continueAddChannel({ kind: 'line' }) // 连续模式下开下一段(也能换这一段加什么)\nview.cancelAddChannel() // 取消(Esc / 右键等价)\n","from":"header"}] },
202
+ { name: "beginAddChannel", kind: "method", params: "options?: GisViewChannelDraftOptions 草稿配置:from(预指定起点,下一步只选终点)/ kind / circuits / autoCommit / continuous / chain / name / lineId / id / lineProps / linePropsByMember / lineItems(第二段)/ routing / path(中间拐点)/ channelProps / channelDraftStyle(本次草稿的预览样式,含 dots 与 layer)/ handoff(交接模式)/ select(点中的节点顺手选中,走 onSelect 那一套)/ originalEvent", returns: "this(只负责那条示例假通道,真正新增走 commitAddChannel / addChannel)", doc: "进入「新增通道」:缺省点两个节点选起点 / 终点,或由 options.from 预指定起点、只需再点终点;options 还支持 kind('channel' 新建通道 / 'line' 新建独立线路,缺省 'channel')/ circuits(一次加几条,缺省 1)/ autoCommit / continuous / chain / name / lineId / id / lineProps / linePropsByMember / lineItems(第二段,连同线路一起新建)/ routing('straight' / 'orthogonal')/ path(中间拐点)/ channelProps / channelDraftStyle(本次草稿的预览样式,含 dots / layer)/ handoff(交接模式:ready 发完收起草稿,自己调 addChannel 落地)/ select(点中的节点顺手选中)/ originalEvent,见「新增通道」", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?)","from":"header"}] },
203
+ { name: "getAddChannelDraft", kind: "method", params: "无", returns: "GisViewChannelDraftPayload | null", doc: "当前新增通道草稿(两个节点的信息与可选方案),没有草稿时为 null", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?)","from":"header"}] },
204
+ { name: "updateAddChannelStyle", kind: "method", params: "patch?: GisViewChannelDraftStyle | null 字段级合并的预览样式(箭头位置 / 多回线间距 / 两端 dots / 层级 layer / 自绘等);传 null 回到全局 channelDraftStyle", returns: "this", doc: "字段级合并当前草稿的预览样式(color / width / dot / dotColor / dots / layer / arrow / arrowSize / arrowPositions / arrowReverse / arrowColor / circuitSpacing / opacity / customDraw);传 null 回到全局 channelDraftStyle,没有草稿时什么也不做", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?)","from":"header"}] },
205
+ { name: "continueAddChannel", kind: "method", params: "options?: GisViewChannelDraftOptions 开下一段草稿(写法与 beginAddChannel 一致,不传就沿用这一段原来的)", returns: "this", doc: "开下一段草稿:连续模式落地时内部自动调(发 phase: 'restart'),也可以手动调;草稿已经退出时按 options 重新进来", examples: [{"group":"拐点与通道","code":"// beginAddChannel(options?) / getAddChannelDraft() / updateAddChannelStyle(patch?) / continueAddChannel(options?)","from":"header"}] },
206
+ { name: "cancelAddChannel", kind: "method", params: "无", returns: "this(收掉草稿并补一条 phase: 'cancel')", doc: "取消新增通道(草稿期间按 Esc 等价,会补一条 phase: 'cancel')", examples: [{"group":"拐点与通道","code":"// commitAddChannel(input?, options?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9,\n // 两端示例点:false 全关;对象可统一 size / color / shape,from / to 再分别覆盖\n dots: { size: 5, shape: 'square', from: { color: '#2f6fd0' }, to: { color: '#d9544d' } },\n layer: 'below-points' // 示例线 / 箭头 / customDraw 画在真实节点之下;示例点仍在节点之后\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 只预指定起点:接下来只点终点(没有 from + to 的写法)\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\n","from":"header"}] },
207
+ { name: "endAddChannel", kind: "method", params: "无", returns: "this(静默收掉视图上的示例假通道 / 线路,数据不动、不补事件)", doc: "结束新增通道:静默收掉视图上的示例假通道 / 线路(不补 cancel 事件),数据一个字节都不改 —— 结束事件里决定「不新建了」就用它", examples: [{"group":"拐点与通道","code":"// commitAddChannel(input?, options?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9,\n // 两端示例点:false 全关;对象可统一 size / color / shape,from / to 再分别覆盖\n dots: { size: 5, shape: 'square', from: { color: '#2f6fd0' }, to: { color: '#d9544d' } },\n layer: 'below-points' // 示例线 / 箭头 / customDraw 画在真实节点之下;示例点仍在节点之后\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 只预指定起点:接下来只点终点(没有 from + to 的写法)\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\n","from":"header"}] },
208
+ { name: "commitAddChannel", kind: "method", params: "input?: GisViewChannelAddResolution | GisViewChannel | GisViewLine | GisViewChannelAddCommitInput,五种写法:① 不传 / null = 用草稿 preferred;② 旧 resolution:字符串或只含非 undefined 的 type / groupId / groupIndex / channelId 的对象;③ 直接线路对象:没有 lineIds 数组,且写了 kind: 'line' / path / from / to / start / end 之一,业务字段与自己的 lineProps 透传到新线路;④ 直接通道对象:Array.isArray(lineIds) 且没有容器键,lineIds 逐条作新回线 id 种子、groups 作分组声明、自己的 lineProps 对所有新回线生效,其余字段进 channelProps,缺省 resolution: 'newChannel';⑤ 容器:出现 channel / line / lines / lineData / lineItems / channelGroups / group / channelProps / resolution 任一键(只写 lineProps 也算)。容器里 line 缺省是独立线路,lines 是成员表(给了几条建几条,空数组沿用草稿);channel.lineIds 只在没有 lines 时当种子。字段优先级:from / to = 顶层 → channel → line → lineItems 第一个对象项(裸 id 跳过)→ 草稿;path = 顶层 → line → channel → 草稿;分组 = channelGroups → group → channel.groups;容器 lineProps = 草稿 → 顶层 → line.lineProps → 逐条,不读 channel.lineProps;channelProps = 草稿 → channel 业务字段 → 顶层 channelProps。直接线路 / 直接通道对象默认新建,要并进已有分组必须用容器并显式写 resolution。options?: { auto?: boolean; continuous?: boolean; originalEvent?: Event | null }:auto 只标记自动提交,continuous: false 强制本次后结束,originalEvent 带回重开的草稿。ready 的方案对象不能整条回传,只取 { type, groupId, groupIndex, channelId } 子集。调用前草稿必须 ready,否则不写数据;没有草稿的程序化新增用 addChannel / addLine。完整示例见「10 通道编辑与新增」与「18 逐方法示例」", returns: "GisViewChannelAddResult", doc: "把已 ready 的草稿落到数据上。input 有五类:不传(用草稿 preferred)、旧 resolution(字符串,或只含 type / groupId / groupIndex / channelId 的对象)、直接线路对象(有 from / to / path / kind: 'line' 等线路形状)、直接通道对象(必须有 lineIds,缺省新建通道)、容器 { channel?, line?, lines?, lineData?, lineItems?, channelGroups?, group?, channelProps?, lineProps?, resolution?, ... }。对象字段优先、没写的沿用草稿;line 缺省表示独立线路,lines 才是成员回线表;直接通道对象缺省新建,直接线路对象缺省按独立线路落地,要并进已有分组必须用容器并显式写 resolution。options?: { auto?, continuous?, originalEvent? } 只影响本次事件与连续草稿。方案读数不能整条回传,只取 { type, groupId, groupIndex, channelId }。完整字段优先级与示例见「10 通道编辑与新增」的 commitAddChannel / 对象直传", examples: [{"group":"拐点与通道","code":"// commitAddChannel(input?, options?) / cancelAddChannel() / endAddChannel()\nview.beginAddChannel({\n kind: 'channel',\n circuits: 3,\n channelDraftStyle: {\n color: '#2f6fd0', width: 2.5, arrow: true, arrowSize: 14, arrowPositions: [0, 0.5, 1],\n circuitSpacing: 12, opacity: 0.9,\n // 两端示例点:false 全关;对象可统一 size / color / shape,from / to 再分别覆盖\n dots: { size: 5, shape: 'square', from: { color: '#2f6fd0' }, to: { color: '#d9544d' } },\n layer: 'below-points' // 示例线 / 箭头 / customDraw 画在真实节点之下;示例点仍在节点之后\n }\n}) // 点两个节点:第一个起点、第二个终点;通道 3 回时预览也画 3 条平行回线\nview.beginAddChannel({ from: 'station-a' }) // 只预指定起点:接下来只点终点(没有 from + to 的写法)\nconst draft = view.getAddChannelDraft() // 草稿读数(跟随光标的预览、两个节点、方案表与实际 style)\nview.updateAddChannelStyle({ arrowPositions: [0.25, 0.75], arrowReverse: true }) // 字段级合并,只改箭头\nview.updateAddChannelStyle(null) // 回到全局 channelDraftStyle\n","from":"header"},{"group":"拐点与通道","code":"// commitAddChannel 只负责把已 ready 的草稿落地;没有草稿的程序化新增用 addChannel / addLine。","from":"header"}] },
203
209
  { name: "getChannelAddOptions", kind: "method", params: "from / to 两端节点 id;count?: number 回数(缺省 1);kind?: 'channel' | 'line'(缺省 'channel')", returns: "GisViewChannelAddPlan | null", doc: "两个节点之间的新增方案:新通道 / 新分组 / 并进某一组 / 无法添加;count 是这一次要加的回数(余量按它判),kind: 'line' 时方案表为空(独立线路不挑方案)", examples: [{"group":"拐点与通道","code":"// getChannelAddOptions(from, to, count?, kind?):两个节点之间能怎么加(界面拿它让用户选)\nconst plan = view.getChannelAddOptions('a', 'b', 2)\nconst linePlan = view.getChannelAddOptions('a', 'b', 1, 'line')\n","from":"header"}] },
204
210
  { name: "getChannelBetween", kind: "method", params: "from / to 两端节点 id", returns: "GisViewChannelBetween | null", doc: "两个节点之间的通道读数(只读):有通道时 hasChannel: true,channelId / channelIndex / groupCount / currentGroupId 与 groups / group / channel(id / name / lineIds / 分组明细)都给全;没有通道时 hasChannel: false、channel: null、groups: [],端点认不出来返回 null —— 与 getChannelAddOptions 的读数同一份,界面做「这两个点之间已经有通道 / 有几组」的判断用它,不必算方案表", examples: [{"group":"拐点与通道","code":"// getChannelBetween(from, to):只要通道读数 —— 有没有通道 / 几组 / 当前哪组(不做方案判断,端点认不出返回 null)\nconst between = view.getChannelBetween('a', 'b')\nif (between?.hasChannel) {\n console.log(between.channelId, between.groupCount, between.groups.map(group => group.id), between.currentGroupId)\n}\n","from":"header"}] },
205
- { name: "addChannel", kind: "method", params: "channel: GisViewChannelAddSpec 通道信息 { from, to, name?, id?, lineData?, group? }(后两个是第二 / 第三段写在对象上的别名);lineData?: GisViewChannelLineData 线路数组(Partial<GisViewLine> / string / number 的一条或一批,按每项 id 建 lineIds);group?: GisViewChannelAddGroup | GisViewChannelAddGroup[] 可选分组信息 { id?, lineIds?, path? }。旧写法 addChannel(input, lineItems?)(GisViewChannelAddInput:circuits / resolution / channelId / groupId / groupIndex / kind / routing / path 那套)照样认;path 是中间拐点,独立线路落地成完整折线、通道只写骨架拐点", returns: "GisViewChannelAddResult(channelLineIds 是落地后通道的完整成员表,独立线路为 null)", doc: "程序化新增通道(新签名,只新建):第一段通道信息(from / to / name / id / path 等通道字段),第二段线路数组 line_data —— 一条或一批(字符串 / 数字按 id 认),按每项 id 建 lineIds,第三段可选分组信息({ id?, lineIds?, path? },单个 / 一组一个)—— 第二段 / 第三段显式给了才走这条新路;两个节点之间已经有通道时整条失败(reason 指路下面两条)。旧写法 addChannel(input, lineItems?)(带 circuits / resolution / channelId / groupId / groupIndex / kind,或只写 { from, to })照样认:按方案表新建或并进已有通道,第二段 lineItems 给了几条就建几条", examples: [{"group":"拐点与通道","code":"// addChannel(spec, lineData?, group?) / addChannels(input, options?) / addChannelGroups(channelId, groups, options?) / addLine(input):程序化新增\nconst channelAdd = view.addChannel({ from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] }, [{ id: 'm-1' }, { id: 'm-2' }])\nconst manyChannels = view.addChannels([{ from: 'a', to: 'b', circuits: 1 }, { from: 'b', to: 'a', circuits: 2, kind: 'line' }])\nconst manyGroups = view.addChannelGroups('c1', [\n { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-7' }, { id: 'm-8' }] },\n { path: [[150, 90]], lines: ['m-9'] }\n])\nconsole.log(manyGroups.count, manyGroups.lineIds, manyGroups.groupIds)\nconst lineAdd = view.addLine({ from: 'a', to: 'b', name: '三回' })","from":"header"}] },
211
+ { name: "addChannel", kind: "method", params: "channel: GisViewChannelAddSpec 通道信息 { from, to, name?, id?, lineData?, group? }(后两个是第二 / 第三段写在对象上的别名);lineData?: GisViewChannelLineData 线路数组(Partial<GisViewLine> / string / number 的一条或一批,按每项 id 建 lineIds);group?: GisViewChannelAddGroup | GisViewChannelAddGroup[] 可选分组信息 { id?, lineIds?, path? }。旧写法 addChannel(input, lineItems?)(GisViewChannelAddInput:circuits / resolution / channelId / groupId / groupIndex / kind / routing / path 那套)照样认;path 是中间拐点,独立线路落地成完整折线、通道只写骨架拐点", returns: "GisViewChannelAddResult(channelLineIds 是落地后通道的完整成员表,独立线路为 null)", doc: "程序化新增通道(新签名,只新建):第一段通道信息(from / to / name / id / path 等通道字段),第二段线路数组 line_data —— 一条或一批(字符串 / 数字按 id 认),按每项 id 建 lineIds,第三段可选分组信息({ id?, lineIds?, path? },单个 / 一组一个)—— 第二段 / 第三段显式给了才走这条新路;两个节点之间已经有通道时整条失败(reason 指路下面两条)。旧写法 addChannel(input, lineItems?)(带 circuits / resolution / channelId / groupId / groupIndex / kind,或只写 { from, to })照样认:按方案表新建或并进已有通道,第二段 lineItems 给了几条就建几条", examples: [{"group":"拐点与通道","code":"// commitAddChannel 只负责把已 ready 的草稿落地;没有草稿的程序化新增用 addChannel / addLine。","from":"header"},{"group":"拐点与通道","code":"// addChannel(spec, lineData?, group?) / addChannels(input, options?) / addChannelGroups(channelId, groups, options?) / addLine(input):程序化新增\nconst channelAdd = view.addChannel({ from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] }, [{ id: 'm-1' }, { id: 'm-2' }])\nconst manyChannels = view.addChannels([{ from: 'a', to: 'b', circuits: 1 }, { from: 'b', to: 'a', circuits: 2, kind: 'line' }])\nconst manyGroups = view.addChannelGroups('c1', [\n { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-7' }, { id: 'm-8' }] },\n { path: [[150, 90]], lines: ['m-9'] }\n])\nconsole.log(manyGroups.count, manyGroups.lineIds, manyGroups.groupIds)\nconst lineAdd = view.addLine({ from: 'a', to: 'b', name: '三回' })","from":"header"}] },
206
212
  { name: "addChannelGroupLines", kind: "method", params: "channelId 目标通道 id;lineData?: GisViewChannelLineData 这次新建的线路数组(按每项 id 建回线);group?: GisViewChannelAddGroup 新分组信息 { id?, path? }(缺省 ${通道 id}::N、不带拐点)", returns: "GisViewChannelAddResult", doc: "给已有通道新加一组回线(原 addChannel 的 newGroup 那一支):第二段线路数组 line_data 按每项 id 建回线,groupInfo({ id?, path? })给新分组的 id 与中间拐点,缺省 ${通道 id}::N、不带拐点;通道 / 线路认不出来时 ok: false + reason", examples: [{"group":"拐点与通道","code":"// 已经有通道时用拆出来的两条(语义直说,不用自己拼 resolution)\nview.addChannelGroupLines('c1', [{ id: 'm-5' }], { id: 'c1::3' }) // 新加一组\nview.joinChannelGroupLines('c1', 'c1::2', [{ id: 'm-6' }]) // 并进某一组(序号 1 也认)\nconsole.log(joined.channelLineIds) // 更新后这条通道的完整成员表(lineIds)\n","from":"code"}] },
207
213
  { name: "addChannelGroups", kind: "method", params: "channelId 目标通道 id;groups: GisViewChannelAddGroupInput | GisViewChannelAddGroupInput[] 每项 { id?, path?, lineData? | lines? | lineItems? }(线路数据与 addChannel 第二段同一份写法);options?: { emit?: boolean; label?: string } emit: false 只落地不通知", returns: "GisViewAddChannelGroupsResult", doc: "给已有通道一次新加多组回线:groups 每项 { id?, path?, lineData | lines | lineItems },线路数据与 addChannel 第二段同一份写法;逐组落地但只记一步历史、只发一条 addChannelGroups 数据变更,返回 { ok, reason, count, results, lineIds, groupIds, groups },options.emit: false 只落地不通知", examples: [{"group":"拐点与通道","code":"// addChannel(spec, lineData?, group?) / addChannels(input, options?) / addChannelGroups(channelId, groups, options?) / addLine(input):程序化新增\nconst channelAdd = view.addChannel({ from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] }, [{ id: 'm-1' }, { id: 'm-2' }])\nconst manyChannels = view.addChannels([{ from: 'a', to: 'b', circuits: 1 }, { from: 'b', to: 'a', circuits: 2, kind: 'line' }])\nconst manyGroups = view.addChannelGroups('c1', [\n { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-7' }, { id: 'm-8' }] },\n { path: [[150, 90]], lines: ['m-9'] }\n])\nconsole.log(manyGroups.count, manyGroups.lineIds, manyGroups.groupIds)\nconst lineAdd = view.addLine({ from: 'a', to: 'b', name: '三回' })","from":"header"}] },
208
214
  { name: "joinChannelGroupLines", kind: "method", params: "channelId 目标通道 id;groupId?: string | number 并进哪一组(认分组 id 也认序号,从 0 起;不传挑第一个还有余量的分组);lineData?: GisViewChannelLineData 这次新建的线路数组", returns: "GisViewChannelAddResult", doc: "把新回线并进已有通道的某一组(原 addChannel 的 joinGroup 那一支):第二段线路数组 line_data,groupId 认分组 id 也认序号(从 0 起、按声明顺序),不传就挑第一个还有余量的分组", examples: [{"group":"拐点与通道","code":"// 已经有通道时用拆出来的两条(语义直说,不用自己拼 resolution)\nview.addChannelGroupLines('c1', [{ id: 'm-5' }], { id: 'c1::3' }) // 新加一组\nview.joinChannelGroupLines('c1', 'c1::2', [{ id: 'm-6' }]) // 并进某一组(序号 1 也认)\nconsole.log(joined.channelLineIds) // 更新后这条通道的完整成员表(lineIds)\n","from":"code"}] },
209
215
  { name: "addChannels", kind: "method", params: "input: GisViewChannelAddInput | GisViewChannelAddInput[] 一条或一批声明(每条与 addChannel 同一套写法;routing: 'orthogonal' 自动正交折线,path 是中间拐点,独立线路落地成完整 path、通道只写骨架);options?: { emit?: boolean } emit: false 只改数据不发 dataChange", returns: "GisViewAddChannelsResult", doc: "addChannel 的批量版:一次新增多条通道 / 独立线路(每条是新签名或旧写法,逐条各自认;每项的 routing / path 走同一套解析,path 是中间拐点),逐条落地但只重排一次场景、只记一步历史、只发一条 addChannels 数据变更;返回 { ok, count, results, lineIds, channelIds, items },失败条目在各自的 reason 里说明原因、不影响其余条目,options.emit: false 时不发 onDataChange,见「动态换数据与加载完成」", examples: [{"group":"拐点与通道","code":"// addChannel(spec, lineData?, group?) / addChannels(input, options?) / addChannelGroups(channelId, groups, options?) / addLine(input):程序化新增\nconst channelAdd = view.addChannel({ from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] }, [{ id: 'm-1' }, { id: 'm-2' }])\nconst manyChannels = view.addChannels([{ from: 'a', to: 'b', circuits: 1 }, { from: 'b', to: 'a', circuits: 2, kind: 'line' }])\nconst manyGroups = view.addChannelGroups('c1', [\n { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-7' }, { id: 'm-8' }] },\n { path: [[150, 90]], lines: ['m-9'] }\n])\nconsole.log(manyGroups.count, manyGroups.lineIds, manyGroups.groupIds)\nconst lineAdd = view.addLine({ from: 'a', to: 'b', name: '三回' })","from":"header"}] },
210
- { name: "addLine", kind: "method", params: "input: GisViewChannelAddInput 同上(等价 addChannel({ ...input, kind: 'line' });routing: 'orthogonal' 自动正交折线,path 是中间拐点,落地成含两端的完整折线);第二段 GisViewChannelLineData 与 addChannel 的线路数组同一份 —— 每项可以只写一个 id(裸字符串 / 数字按 { id } 收)、写对象给逐条字段,不写就按 from-to-序号 自动编号", returns: "GisViewChannelAddResult", doc: "/** * 程序化新增独立线路(不挂通道)。", examples: [{"group":"拐点与通道","code":"// addChannel(spec, lineData?, group?) / addChannels(input, options?) / addChannelGroups(channelId, groups, options?) / addLine(input):程序化新增\nconst channelAdd = view.addChannel({ from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] }, [{ id: 'm-1' }, { id: 'm-2' }])\nconst manyChannels = view.addChannels([{ from: 'a', to: 'b', circuits: 1 }, { from: 'b', to: 'a', circuits: 2, kind: 'line' }])\nconst manyGroups = view.addChannelGroups('c1', [\n { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-7' }, { id: 'm-8' }] },\n { path: [[150, 90]], lines: ['m-9'] }\n])\nconsole.log(manyGroups.count, manyGroups.lineIds, manyGroups.groupIds)\nconst lineAdd = view.addLine({ from: 'a', to: 'b', name: '三回' })","from":"header"}] },
216
+ { name: "addLine", kind: "method", params: "input: GisViewChannelAddInput 同上(等价 addChannel({ ...input, kind: 'line' });routing: 'orthogonal' 自动正交折线,path 是中间拐点,落地成含两端的完整折线);第二段 GisViewChannelLineData 与 addChannel 的线路数组同一份 —— 每项可以只写一个 id(裸字符串 / 数字按 { id } 收)、写对象给逐条字段,不写就按 from-to-序号 自动编号", returns: "GisViewChannelAddResult", doc: "/** * 程序化新增独立线路(不挂通道)。", examples: [{"group":"拐点与通道","code":"// commitAddChannel 只负责把已 ready 的草稿落地;没有草稿的程序化新增用 addChannel / addLine。","from":"header"},{"group":"拐点与通道","code":"// addChannel(spec, lineData?, group?) / addChannels(input, options?) / addChannelGroups(channelId, groups, options?) / addLine(input):程序化新增\nconst channelAdd = view.addChannel({ from: 'a', to: 'b', name: '二号通道', path: [[200, 120]] }, [{ id: 'm-1' }, { id: 'm-2' }])\nconst manyChannels = view.addChannels([{ from: 'a', to: 'b', circuits: 1 }, { from: 'b', to: 'a', circuits: 2, kind: 'line' }])\nconst manyGroups = view.addChannelGroups('c1', [\n { id: 'c1::4', path: [[130, 70]], lineData: [{ id: 'm-7' }, { id: 'm-8' }] },\n { path: [[150, 90]], lines: ['m-9'] }\n])\nconsole.log(manyGroups.count, manyGroups.lineIds, manyGroups.groupIds)\nconst lineAdd = view.addLine({ from: 'a', to: 'b', name: '三回' })","from":"header"}] },
211
217
  { name: "removeLine", kind: "method", params: "lineId: string | number 要删的线路 id", returns: "GisViewRemoveLineResult", doc: "移除一条线路(撤销刚落地的新增线路):挂在通道上的回线会从通道声明里一起摘掉,通道空了整条删掉,返回 { ok, line, lineId, index, channelId, removedChannel }", examples: [{"group":"拐点与通道","code":"// removeLine(lineId) / removeLines(lineIds) / removeChannel(channelId):删线路 / 删多条 / 删整条通道\nview.removeLine('l1')\nview.removeLines(['l1', 'l2'])\nview.removeLines('l1') // 单条也认\nview.removeChannel('c1')","from":"header"}] },
212
218
  { name: "removeLines", kind: "method", params: "lineIds: string | number | 数组 一次删多条", returns: "GisViewRemoveLinesResult", doc: "一次移除多条线路(传数组或单条 id):通道声明与分组跟着收拾、删空的通道整条走,返回 { ok, lineIds, indexes, lines, entries, removedChannels }", examples: [{"group":"拐点与通道","code":"// removeLine(lineId) / removeLines(lineIds) / removeChannel(channelId):删线路 / 删多条 / 删整条通道\nview.removeLine('l1')\nview.removeLines(['l1', 'l2'])\nview.removeLines('l1') // 单条也认\nview.removeChannel('c1')","from":"header"}] },
213
219
  { name: "removeChannel", kind: "method", params: "channelId 通道 id", returns: "GisViewRemoveChannelResult", doc: "移除整条通道:它名下的回线一条条摘掉,返回 { ok, channelId, lineIds, lines, removedChannel }", examples: [{"group":"拐点与通道","code":"// removeLine(lineId) / removeLines(lineIds) / removeChannel(channelId):删线路 / 删多条 / 删整条通道\nview.removeLine('l1')\nview.removeLines(['l1', 'l2'])\nview.removeLines('l1') // 单条也认\nview.removeChannel('c1')","from":"header"}] },
@@ -343,9 +349,11 @@ export const API_SECTIONS = [
343
349
  {
344
350
  title: "属性",
345
351
  items: [
346
- { name: "el", kind: "property", params: "HTMLElement", returns: "HTMLElement", doc: "组件插入的 .gis-view 根节点(destroy() 会移除它;容器本身不动)", examples: [{"group":"属性","code":"// el / canvas / gl:容器、画布与 WebGL2 上下文(gl 可能为 null —— 还没建好或走了别的路)\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl","from":"header"}] },
347
- { name: "canvas", kind: "property", params: "HTMLCanvasElement", returns: "HTMLCanvasElement", doc: "WebGL2 画布元素(导出 / 截图 / 自己叠加 DOM 时定位用它)", 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":"// el / canvas / gl:容器、画布与 WebGL2 上下文(gl 可能为 null —— 还没建好或走了别的路)\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl","from":"header"}] },
348
- { name: "gl", kind: "property", params: "WebGL2RenderingContext | null", returns: "WebGL2RenderingContext | null", doc: "引擎拿到的 WebGL2 上下文,创建失败时 null(这时 onError 会报一条)", examples: [{"group":"属性","code":"// el / canvas / gl:容器、画布与 WebGL2 上下文(gl 可能为 null —— 还没建好或走了别的路)\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl","from":"header"}] },
352
+ { name: "el", kind: "property", params: "HTMLElement", returns: "HTMLElement", doc: "组件插入的 .gis-view 根节点(destroy() 会移除它;容器本身不动)", examples: [{"group":"属性","code":"// el / canvas / gl / cameraAnimating / dataVersion:容器、画布、WebGL2 上下文、相机是否在飞行、数据修订号\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl\nconst cameraAnimating = view.cameraAnimating\nconst dataVersion = view.dataVersion // 数据变更 +1;只改全局样式 / 配置不变","from":"header"}] },
353
+ { name: "canvas", kind: "property", params: "HTMLCanvasElement", returns: "HTMLCanvasElement", doc: "WebGL2 画布元素(导出 / 截图 / 自己叠加 DOM 时定位用它)", 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":"// el / canvas / gl / cameraAnimating / dataVersion:容器、画布、WebGL2 上下文、相机是否在飞行、数据修订号\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl\nconst cameraAnimating = view.cameraAnimating\nconst dataVersion = view.dataVersion // 数据变更 +1;只改全局样式 / 配置不变","from":"header"}] },
354
+ { name: "gl", kind: "property", params: "WebGL2RenderingContext | null", returns: "WebGL2RenderingContext | null", doc: "引擎拿到的 WebGL2 上下文,创建失败时 null(这时 onError 会报一条)", examples: [{"group":"属性","code":"// el / canvas / gl / cameraAnimating / dataVersion:容器、画布、WebGL2 上下文、相机是否在飞行、数据修订号\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl\nconst cameraAnimating = view.cameraAnimating\nconst dataVersion = view.dataVersion // 数据变更 +1;只改全局样式 / 配置不变","from":"header"}] },
355
+ { name: "cameraAnimating", kind: "property", params: "boolean", returns: "boolean", doc: "相机是否正在飞行(只读)。按钮据此在「飞向 / 停止」之间切换,不触发重绘", examples: [{"group":"属性","code":"// el / canvas / gl / cameraAnimating / dataVersion:容器、画布、WebGL2 上下文、相机是否在飞行、数据修订号\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl\nconst cameraAnimating = view.cameraAnimating\nconst dataVersion = view.dataVersion // 数据变更 +1;只改全局样式 / 配置不变","from":"header"}] },
356
+ { name: "dataVersion", kind: "property", params: "number", returns: "number", doc: "数据修订号(只读):每次数据变更 +1,onDataChange 载荷里是同一个数;只改全局配置 / 样式不推。外部缓存、Worker 请求与增量计算据它判断是否重算", examples: [{"group":"属性","code":"// el / canvas / gl / cameraAnimating / dataVersion:容器、画布、WebGL2 上下文、相机是否在飞行、数据修订号\nconst host = view.el\nconst canvas = view.canvas\nconst gl = view.gl\nconst cameraAnimating = view.cameraAnimating\nconst dataVersion = view.dataVersion // 数据变更 +1;只改全局样式 / 配置不变","from":"header"}] },
349
357
  ]
350
358
  },
351
359
  ]
@@ -366,7 +374,7 @@ export const API_OPTIONS = [
366
374
  { name: "channelTangentInset", type: "number | null", defaultValue: "null", doc: "两端切点往节点里收多少 CSS 像素:缺省(null)按成员回线里最粗那条的线宽收,线因此不会从节点边缘探出去;0 表示切点正好落在节点边上,负数外收(切点往节点外让、回线整段比节点宽);单个通道可用 tangentInset 覆盖;内收两侧合计最多收掉节点宽度的 40%,外收单侧最多让出 50%" },
367
375
  { name: "channelMaxGroups", type: "number", defaultValue: "0", doc: "一条通道最多几个分组(0 表示不限):新增通道时用来判断还能不能变成新分组,见「新增通道」" },
368
376
  { name: "channelGroupMaxLines", type: "number", defaultValue: "0", doc: "一个分组最多几条回线(0 表示不限):新增通道时用来判断还能不能并进某一组,见「新增通道」" },
369
- { name: "channelDraftStyle", type: "GisViewChannelDraftStyle", defaultValue: "蓝色 2px + 两端圆点,箭头关闭", doc: "新增通道草稿预览的全局样式:color / width / dot / dotColor / arrow / arrowSize / arrowPositions / arrowReverse / arrowColor / circuitSpacing / opacity / customDraw;beginAddChannel({ channelDraftStyle }) 可按这一次草稿覆盖,updateAddChannelStyle(patch) 可在草稿期间增量修改" },
377
+ { name: "channelDraftStyle", type: "GisViewChannelDraftStyle", defaultValue: "蓝色 2px + 两端圆点,箭头关闭", doc: "新增通道草稿预览的全局样式:color / width / dot / dotColor / dots(false 关示例点,或 { show, size, color, shape, from, to } 分端配置;示例点固定在点中的节点中心)/ layer('above' 默认,'below-points' 让示例线 / 箭头 / customDraw 位于真实节点之下)/ arrow / arrowSize / arrowPositions / arrowReverse / arrowColor / circuitSpacing / opacity / customDraw;beginAddChannel({ channelDraftStyle }) 可按这一次草稿覆盖,updateAddChannelStyle(patch) 可在草稿期间增量修改" },
370
378
  { name: "webglLayers", type: "GisViewWebGLLayer[]", defaultValue: "[]", doc: "WebGL 自定义绘制层:每层提供 draw({ gl, canvas, viewport, camera, frame, project, unproject, requestRender }),在内置内容之后按 order 绘制;运行时也可用 addWebGLLayer / updateWebGLLayer / removeWebGLLayer 维护" },
371
379
  { name: "background", type: "GisViewBackground", defaultValue: "浅色底", doc: "背景层,支持底色、图片与 GeoJSON" },
372
380
  { name: "pointStyle", type: "GisViewPointStyle", defaultValue: "见下", doc: "点的默认样式,可被单个点覆盖;运行时按字段合并用 updatePointStyle(patch)(updateOptions({ pointStyle }) 是整份换)" },
@@ -384,7 +392,7 @@ export const API_OPTIONS = [
384
392
  { name: "labelStates", type: "GisViewLabelState[]", defaultValue: "无", doc: "标签的运行时状态(getLabelStates() 的那一份:拖出来的偏移、改过的内容与样式、运行时加的标签、隐藏覆盖):创建时喂回来就回显,等价于创建之后调 setLabelStates(states);updateOptions({ labelStates }) 同样认。exportData({ format: 'input' }) 的结果里就叫 labelStates,存档 → 回灌直接接上" },
385
393
  { name: "graphics", type: "GisViewGraphic[]", defaultValue: "无", doc: "与节点无关的扩展图形:按世界坐标放 SVG 路径 / 图片 / Canvas,支持锚点、偏移、旋转、透明度、zIndex 与 scaleWithView;位于画布之上、标签之下,运行时用 addGraphic / addGraphics / updateGraphic / removeGraphic / clearGraphics 增删改,见「扩展图形(graphic)」" },
386
394
  { name: "tooltip", type: "GisViewTooltipOptions", defaultValue: "无", doc: "悬停提示:点 / 线 / 通道分别配置内容,见「悬浮提示」" },
387
- { name: "minimap", type: "GisViewMinimapOptions | null", defaultValue: "无", doc: "内置鹰眼图:位置、尺寸、节点 / 线路 / 视口框三份样式与交互都可配,extent 决定小图按数据范围还是「数据 ∪ 底图」铺(默认 auto,GeoJSON 底图不会缩成一个点),zoomable 打开后小图自身可整体放大缩小(空白处拖动可以平移小图或直接框选让主图显示过去,每格倍率 zoomStep),通道还能只画一根;小图同样支持规则(pointRules / lineRules,缺省沿用主图规则的颜色与层级),可以只画某几层(levels)、关掉分层叠画(layered)、按三组系数给节点与线路定尺寸(整体倍数 pointSizeScale / lineWidthScale、跟随小图倍数 pointZoomScale / lineZoomScale(只在小图自身倍数 ≠ 1 时起作用)、跟随主视图地图倍数 pointMapZoomScale / lineMapZoomScale),视口框与框选矩形的颜色 / 背景 / 边框 / 圆角也能配(boxStyle / selectStyle),extent 还能直接写死一份世界范围、容器可加 className / style、layers 按 placement 分数据前 / 数据后、段内按 order 拆出多层自绘内容(旧 draw 钩子仍兼容并排在最后)、onInteraction 在手势结束时给出类型 / 世界坐标 / 相机、maxPixelRatio 限制备份倍率上限,不配则不产生任何 DOM,见「鹰眼图」" },
395
+ { name: "minimap", type: "GisViewMinimapOptions | null", defaultValue: "无", doc: "内置鹰眼图:位置、尺寸、节点 / 线路 / 视口框三份样式与交互都可配,extent 决定小图按数据范围还是「数据 ∪ 底图」铺(默认 auto,GeoJSON 底图不会缩成一个点),extentFollowContent 默认 false,首次铺好后内容变化不再改小图范围;extentFollowCamera 默认 false、打开后才允许 auto 范围随相机收窄。zoomable 三态互斥:false 关闭、true 手动缩放小图自身、'follow' 让整体范围跟随主图(followZoomScale 是主图倍数的指数,跟随模式下 setMinimapZoom / zoomMinimapBy 不抢控制权);空白处拖动可平移小图或直接框选让主图显示过去(dragAction,box 不依赖缩放,手动模式下 Ctrl / ⌘ 才切换动作),每格倍率 zoomStep。通道还能只画一根;小图同样支持规则(pointRules / lineRules,缺省沿用主图规则的颜色、层级、最终节点尺寸与图标、样式链线宽,节点 / 线路尺寸按两边视口比例缩放且不受主图相机倍数影响),可以只画某几层(levels)、关掉分层叠画(layered)、按三组系数给节点与线路定尺寸(整体倍数 pointSizeScale / lineWidthScale 对自有与继承要素都生效;跟随小图倍数 pointZoomScale / lineZoomScale 在小图自身放大时也作用于继承要素;跟随主视图地图倍数 pointMapZoomScale / lineMapZoomScale 只作用于自有规则 / 样式),视口框与框选矩形的颜色 / 背景 / 边框 / 圆角也能配(boxStyle / selectStyle),extent 还能直接写死一份世界范围、容器可加 className / style、layers 按 placement 分数据前 / 数据后、段内按 order 拆出多层自绘内容(旧 draw 钩子仍兼容并排在最后)、onInteraction 在手势结束时给出类型 / 世界坐标 / 相机、maxPixelRatio 限制备份倍率上限,不配则不产生任何 DOM,见「鹰眼图」" },
388
396
  { name: "filter", type: "GisViewFilterInput | null", defaultValue: "无", doc: "初始筛选条件,等价于创建之后调用 setFilter;mode 缺省 'hide',写 'remove' 则删除不命中的要素,见「筛选」" },
389
397
  { name: "filterWorker", type: "boolean | GisViewFilterWorkerOptions", defaultValue: "{ threshold: 20000 }", doc: "筛选是否用 Web Worker 计算:默认点 + 线总数超过阈值才用,false 表示始终同步算" },
390
398
  { name: "searchWorker", type: "boolean | GisViewSearchWorkerOptions", defaultValue: "{ threshold: 20000 }", doc: "[异步搜索](14-视图与筛选.md#搜索与定位)是否用 Web Worker 计算:默认点 + 线总数超过阈值才用,false 表示 searchAsync 也同步算;同步的 search 一直在主线程算,不受这一项影响" },
@@ -444,7 +452,7 @@ export const API_OPTIONS = [
444
452
  { name: "pixelRatio", type: "number", defaultValue: "devicePixelRatio", doc: "手动指定设备像素比(夹在 0.5 ~ 3);宿主缩放(CSS zoom / transform: scale)的比例会乘在它上面,合计夹在 0.5 ~ 4" },
445
453
  { name: "maxCanvasPixels", type: "number", defaultValue: "4000000", doc: "画布总像素预算:cssWidth × cssHeight × 倍率² 超过它时按 sqrt(预算 / CSS 面积) 压渲染倍率(全屏与高分屏不因像素数翻十倍而掉帧);传 0 不设上限" },
446
454
  { name: "antialias", type: "boolean", defaultValue: "开(软件光栅自动关)", doc: "画布是否开 MSAA。同一份十万级线段实测:MSAA 4× 下单帧光栅化是关掉时的近 5 倍(见「画质」里的实测表),关掉能明显提速、代价是线条与圆形边缘有锯齿;当前实际状态见 getStats().antialias(软件光栅下引擎默认关,写 true 可强制打开)" },
447
- { name: "interactionThinLines", type: "boolean", defaultValue: "true", doc: "交互期细线快路径:可见线段全部细到 3px 以内且没有虚线时,拖动 / 缩放期间改用 gl.LINES 画(1 设备像素、无光晕与虚线),停手立刻回到四边形展开。位置不跳、悬停 / 选中不受影响;这一帧走没走快路径见 getStats().thinLines,实测见「画质」" },
455
+ { name: "interactionThinLines", type: "boolean", defaultValue: "true", doc: "交互期细线快路径:可见线段最大半宽 ≤ 0.6 设备像素(dpr = 1 时约 1.2px 线宽)且整批没有虚线时,拖动 / 缩放期间改用 gl.LINES 画(1 设备像素、无光晕与虚线),停手立刻回到四边形展开。2px 实线不会走这一档,但全实线四边形另有轻量片元。位置不跳、悬停 / 选中不受影响;这一帧走没走快路径见 getStats().thinLines,实测见「画质」" },
448
456
  { name: "interactionNoMsaa", type: "boolean", defaultValue: "true", doc: "非 MSAA 后备缓冲(交互 / 动画期):拖动 / 缩放 / 框选期间与动画在跑的那些帧,整帧先画进一张不带 MSAA 的后备贴图,帧末整幅贴回画布;停手(或动画停帧)之后那一帧照旧直接画进带 MSAA 的画布,所以静止画面逐像素不变(代价是这些活动帧的边缘暂时没有抗锯齿)。十万级实测交互期单帧 133 → 36.6 ms、拖动 p50 130 → 16 ~ 22 ms,动画帧 212 → 53 ms/帧(只开涟漪 187 → 48、只开线路动点 199 → 47);透明底 / 自定义底色的导出场景与没开 MSAA 的画布不走这条,这一帧走没走见 getStats().noMsaa,实测见「画质」" },
449
457
  { name: "renderer", type: "'auto' | 'webgl2' | 'webgpu'(也认布尔开关)", defaultValue: "'auto'", doc: "渲染后端开关:当前实装的绘制后端只有 WebGL2 —— 十万级下 frameMs 只有 0.05 ~ 0.2 ms,开销在填充率与 DOM 覆盖层上,换 WebGPU 省不到(第 18 章),所以 'auto' 只会在后台探一次 WebGPU 能力、绘制仍走 WebGL2,'webgl2' 连探测都不跑(无 GPU 的远程桌面里省一次 requestAdapter),'webgpu' 探完明确报一句「已回退 WebGL2、为什么」;true / false 等价于 'webgpu' / 'webgl2'。读数用 getRendererInfo(),见「渲染后端(WebGPU 开关)」" },
450
458
  { name: "ariaLabel", type: "string", defaultValue: "GIS 视图", doc: "画布的无障碍名称" },
@@ -57,7 +57,7 @@ export const DEMOS = [
57
57
  id: 'minimap',
58
58
  title: '鹰眼图配置',
59
59
  group: 'interact',
60
- blurb: '尺寸 / 位置 / 范围 / 规则 / 层级 / 小图缩放与备份倍率一页调完;另有两层自定义绘制演示 layers 的扩展方式。',
60
+ blurb: '尺寸 / 位置 / 范围 / 规则 / 层级 / 小图缩放与备份倍率一页调完;范围默认锁定、筛选不跳,顶部手柄可拖动整张鹰眼图并复位;另有两层自定义绘制演示 layers 的扩展方式。',
61
61
  doc: DOC_VIEW,
62
62
  component: () => import('./MinimapDemo.vue')
63
63
  },