gis-view-webgl2 0.1.2 → 0.1.4

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 (66) hide show
  1. package/README.md +21 -14
  2. package/dist/gis-view.css +1 -1
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +421 -76
  5. package/dist/index.js +1 -1
  6. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +151 -0
  7. package/docs/GisView.md +4 -4
  8. 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 +137 -0
  9. package/docs/api/index.html +23 -9
  10. package/docs/demos/ApiCoverageDemo.vue +644 -0
  11. package/docs/demos/ChannelDemo.vue +14 -12
  12. package/docs/demos/ClusterDemo.vue +251 -0
  13. package/docs/demos/CopyMergeDemo.vue +3 -4
  14. package/docs/demos/EventDemo.vue +3 -3
  15. package/docs/demos/ExtensionDemo.vue +69 -12
  16. package/docs/demos/GisViewDemo.vue +64 -8809
  17. package/docs/demos/SearchFilterDemo.vue +13 -11
  18. package/docs/demos/api-index.js +58 -47
  19. package/docs/demos/demos.js +34 -17
  20. package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +29 -0
  21. package/docs/demos/gis-view-demo/GisViewDemoDialogs.vue +82 -0
  22. package/docs/demos/gis-view-demo/GisViewDemoFooter.vue +16 -0
  23. package/docs/demos/gis-view-demo/GisViewDemoHeading.vue +16 -0
  24. package/docs/demos/gis-view-demo/GisViewDemoPanelStack.vue +71 -0
  25. package/docs/demos/gis-view-demo/GisViewDemoStage.vue +219 -0
  26. package/docs/demos/gis-view-demo/demo.css +1230 -0
  27. package/docs/demos/gis-view-demo/demoContext.js +12 -0
  28. package/docs/demos/gis-view-demo/panels/DataPanels.vue +303 -0
  29. package/docs/demos/gis-view-demo/panels/OpsPanels.vue +174 -0
  30. package/docs/demos/gis-view-demo/panels/StylePanels.vue +561 -0
  31. package/docs/demos/gis-view-demo/panels/ViewPanels.vue +396 -0
  32. package/docs/demos/gis-view-demo/useGisViewDemo.js +6188 -0
  33. package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +3 -2
  34. package/docs/index.html +41 -4
  35. package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +1 -1
  36. package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +3 -2
  37. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +104 -16
  38. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +15 -14
  39. 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 +40 -3
  40. package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +50 -3
  41. package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +6 -2
  42. 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 +39 -7
  43. package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +13 -4
  44. package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +3 -0
  45. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +121 -49
  46. package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +37 -3
  47. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +17 -8
  48. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +45 -25
  49. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +91 -20
  50. 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 +3 -3
  51. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +25 -1
  52. 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 +11 -8
  53. package/docs/steps/README.md +17 -11
  54. package/docs/steps/selftest-actions.html +3 -5
  55. package/docs/steps/selftest-demo.html +27 -18
  56. package/docs/steps/selftest-demos.html +409 -32
  57. package/docs/steps/selftest-docs.html +33 -4
  58. package/docs/steps/selftest-effects.html +200 -0
  59. package/docs/steps/selftest-features.html +603 -21
  60. package/docs/steps/selftest-labels.html +74 -0
  61. package/docs/steps/selftest-perf.html +293 -20
  62. package/docs/steps/selftest-scale.html +150 -29
  63. package/docs/steps/selftest-select.html +90 -0
  64. package/docs/steps/step-10-full.js +1 -1
  65. package/docs/steps/step-12-events.js +1 -1
  66. package/package.json +1 -1
@@ -41,8 +41,9 @@
41
41
  | `onLabelClick` / `onLabelDrag` / `onLabelContextMenu` / `onLabelDblClick` | `kind` / `index` / `id` / `labelId` / `item` / `label`(这一条标签的 id、内容、来源、合并后的样式)/ `parent`(挂在哪条要素上,线路在通道里时带通道 / 分组)/ `state` |
42
42
  | `onQualityChange` | `{ mode, name, level, glow, ornament, scale, budgetMs, frameMs }` |
43
43
 
44
- 四个事件的总线载荷是 `{ payload, event }`(要原样带回 DOM 事件):`pointerDown` / `pointerUp` / `contextMenu` /
45
- `channelContextMenu`,回调那一侧仍是 `(payload, event)` 两个参数,`view.on` 订阅时记得拆包。
44
+ 三个事件的总线载荷是 `{ payload, event }`(要原样带回 DOM 事件):`pointerDown` / `pointerUp` / `contextMenu`,
45
+ 回调那一侧仍是 `(payload, event)` 两个参数,`view.on` 订阅时记得拆包;通道回线的右键上下文在同一份
46
+ `contextMenu` 载荷的 `channelContext` 上。
46
47
 
47
48
  ### 自建面板常用的读数
48
49
 
package/docs/index.html CHANGED
@@ -1323,7 +1323,7 @@
1323
1323
  { file: 'manual/14-视图与筛选.md', title: '手册 14 · 视图与筛选', blurb: '筛选 / 搜索 / 视图与背景', parent: 'GisView.md' },
1324
1324
  { file: 'manual/15-导出与集成.md', title: '手册 15 · 导出与集成', blurb: '导出 / 插件 / 自建界面', parent: 'GisView.md' },
1325
1325
  { file: 'manual/16-实例方法总表.md', title: '手册 16 · 实例方法总表', blurb: '实例方法主表', parent: 'GisView.md' },
1326
- { file: 'manual/17-参数速查.md', title: '手册 17 · 参数速查', blurb: '248 个成员逐个列全', parent: 'GisView.md' },
1326
+ { file: 'manual/17-参数速查.md', title: '手册 17 · 参数速查', blurb: '256 个成员逐个列全', parent: 'GisView.md' },
1327
1327
  { file: 'manual/18-逐方法示例.md', title: '手册 18 · 逐方法示例', blurb: '每个成员一段示例', parent: 'GisView.md' },
1328
1328
  { file: 'manual/19-读数与拖拽预览.md', title: '手册 19 · 读数与拖拽预览', blurb: '读数 / 定位 / 落点预览', parent: 'GisView.md' },
1329
1329
  { file: 'manual/20-性能与大数据.md', title: '手册 20 · 性能与大数据', blurb: '大数据开关与实测', parent: 'GisView.md' },
@@ -1399,7 +1399,7 @@
1399
1399
  { file: '../src/gis-view/README.md', title: 'src/gis-view/README.md', blurb: '源码导读与逐轮实现记录' }
1400
1400
  ]
1401
1401
  const PAGES = [
1402
- { href: 'api/index.html', title: 'API 速查(方法 + 示例)', blurb: '258 个实例成员 / 88 个 options,逐个带示例与复制' },
1402
+ { href: 'api/index.html', title: 'API 速查(方法 + 示例)', blurb: '256 个实例成员 / 96 个 options,逐个带示例与复制' },
1403
1403
  { href: 'playground/index.html', title: '调试演示页(粘代码就跑)', blurb: '左侧编辑、右侧真实实例 + GeoJSON 底图:内置示例与「这一份文档」的示例同在一个下拉,粘代码直接跑' },
1404
1404
  { href: 'guide/sample-document.html', title: 'sample-document.json 图解', blurb: '点数据看端点 / 拐点 / 分组骨架' },
1405
1405
  { href: 'steps/index.html', title: '从 0 手写分步 demo', blurb: '13 步,每步一个文件' },
@@ -1576,7 +1576,44 @@
1576
1576
  continue
1577
1577
  }
1578
1578
  if (/^\s*\|/.test(line) && index + 1 < lines.length && /^\s*\|[\s:|-]+\|\s*$/.test(lines[index + 1])) {
1579
- const parseRow = row => row.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map(cell => cell.trim())
1579
+ /**
1580
+ * 表格行拆分:反引号里的竖线(类型联合 `'a' \| 'b'` 很常见)不是分隔符,
1581
+ * `\|` 按转义后的普通竖线收进单元格;只看分隔位置,不做第二次 replace。
1582
+ */
1583
+ const parseRow = (row) => {
1584
+ const text = row.trim()
1585
+ const start = text.startsWith('|') ? 1 : 0
1586
+ let trailingBackslashes = 0
1587
+ for (let cursor = text.length - 2; cursor >= 0 && text[cursor] === '\\'; cursor -= 1) {
1588
+ trailingBackslashes += 1
1589
+ }
1590
+ const trailing = text.endsWith('|') && trailingBackslashes % 2 === 0
1591
+ const end = trailing ? text.length - 1 : text.length
1592
+ const cells = []
1593
+ let cell = ''
1594
+ let inCode = false
1595
+ for (let cursor = start; cursor < end; cursor += 1) {
1596
+ const character = text[cursor]
1597
+ if (character === '\\' && text[cursor + 1] === '|') {
1598
+ cell += '|'
1599
+ cursor += 1
1600
+ continue
1601
+ }
1602
+ if (character === '`') {
1603
+ inCode = !inCode
1604
+ cell += character
1605
+ continue
1606
+ }
1607
+ if (character === '|' && !inCode) {
1608
+ cells.push(cell.trim())
1609
+ cell = ''
1610
+ continue
1611
+ }
1612
+ cell += character
1613
+ }
1614
+ cells.push(cell.trim())
1615
+ return cells
1616
+ }
1580
1617
  const head = parseRow(line)
1581
1618
  index += 2
1582
1619
  const rows = []
@@ -1919,7 +1956,7 @@
1919
1956
  { badge: 'START', title: '第一次使用', desc: '引入、最小数据与首屏配置,先把图画出来。', meta: '01 快速开始 · 约 10 分钟', file: 'manual/01-快速开始.md' },
1920
1957
  { badge: 'BACKEND', title: '接后端与导出', desc: '编辑接口、导出回灌、插件与自建界面。', meta: '15 导出与集成', file: 'manual/15-导出与集成.md' },
1921
1958
  { badge: 'PERF', title: '十万级性能', desc: 'Worker 筛选 / 异步搜索、批量更新与质量档。', meta: '20 性能与大数据', file: 'manual/20-性能与大数据.md' },
1922
- { badge: 'API', title: '查方法与参数', desc: '实例方法、options 与可复制示例集中检索。', meta: 'API 速查 · 248 个成员', href: 'api/index.html', external: true }
1959
+ { badge: 'API', title: '查方法与参数', desc: '实例方法、options 与可复制示例集中检索。', meta: 'API 速查 · 256 个成员', href: 'api/index.html', external: true }
1923
1960
  ]
1924
1961
  const renderReadingPaths = () => `<section class='doc-paths' aria-label='推荐阅读路径'>` +
1925
1962
  `<div class='doc-paths__head'><strong>按任务选一条阅读路径</strong><span>第一次进来先看快速开始;带着明确目标时直接跳到对应章节。</span></div>` +
@@ -97,7 +97,7 @@
97
97
 
98
98
  ### 16.70 自适应比例为什么给「联动指数」留一个没写的状态、updateLine 的拐点口径怎么定、API 速查页的数据是怎么长出来的(本轮已做 / 留档)
99
99
 
100
- - **为什么「自适应比例」不是把默认值改成 1,而是给联动指数加一个「没写」的状态**:`pointZoomScale` / `lineZoomScale` 原来的缺省是 0(尺寸不跟缩放走)。想让整个视图像地图一样等比放缩,直觉是把缺省改成 1,但那样一改,所有**显式写过 0** 的调用方语义就分不出来了 —— 写 0 是「我明确要它恒定」,没写才是「跟不跟随交给开关」。所以 settings 里这两项改成没写存 `null`,新增 `zoomScaleAuto`(默认关);`zoomExponent(value)` 一处收口:`null` / `undefined` 时看开关(开 → 1、关 → 0),写了就按写的算 —— 显式指数永远优先。开关与 `setZoomBaseScale(scale?)` 都走 `SCALE_ONLY_KEYS` 那条「只改尺寸」的增量路(不动几何、不重解析);基准不给参数就取当前 `camera.scale`,配合 `getZoomBaseScale()` 读回。自测的六条断言就是钉这套口径:缺省基准是谁、放大 2 倍尺寸是不是翻倍、`setZoomBaseScale` 把基准改回 1 之后是不是不再联动、显式指数压过开关、关掉开关回到原来的恒定尺寸。
100
+ - **缩放联动指数的历史取舍与现行口径**:`pointZoomScale` / `lineZoomScale` 现在仍以 `0` 为缺省(尺寸不跟缩放走),要跟随地图就显式写指数;原来自适应比例开关负责给「没写」的项兜底,后来确认它只是把两项指数隐式设成 `1`,无法解决远处孤立点撑大包围盒导致的节点拥挤,因此已移除。`setZoomBaseScale(scale?)` 保留:它仍走 `SCALE_ONLY_KEYS` 那条「只改尺寸」的增量路(不动几何、不重解析),基准不给参数就取当前 `camera.scale`,配合 `getZoomBaseScale()` 读回。包围盒撑大时的间距问题改由 `fitSpacing` 处理:适应视图时按可见节点最近中心距反推比例,**相机中心仍取普通适应的包围盒中心**;只有比例被提高,不再按最近节点对或节点中位数另算中心。`measurePointSpacing()` 因此只保留距离扫描,`reflowChannelWidths()` 对单组通道在宽度输入未变时直接跳过车道与折线重算,见主手册「适应视图的节点间距(fitSpacing)」。
101
101
  - **为什么「单独改某个线路」不新开一个方法,而是把 `updateLine` 的口径写清楚**:线路能改的就三类 —— 属性(颜色 / 宽度 / 线形…)、几何(拐点)、分组归属(`groupId` / `groupIndex` / `channelId`),`updateLine(id, patch)` 本来就都收:`path` 是整条折线(含首尾两端,`[x, y]` 或 `{ x, y }`),只想改中间拐点用 `setLineWaypoints`(两端从 `from` / `to` 或 `start` / `end` 推),属性与分组指令一次下发时按「线路数据只发一条 `updateLine`、分组再发一条 `channelGroups`」走,指令本身不写进线路数据。这三条口径补进主表、参数速查与逐方法示例,自测新增三条钉住「一次下发只发一条」与「指令不进数据」。
102
102
  - **为什么 API 速查页的数据不手写**:手写的清单一改源码就漂。数据还是同一份《GisView.md》:`docs/steps/gen-api-index.mjs` 原来就导出成员 / 分组 / options,这一轮给每条补上 `examples: [{ group, code, from }]` —— 示例来自「逐方法示例」一节,解析时按 `####` 分组、围栏切块,成员名取首行注释冒号前的标识符;首行没点名、但代码里真实出现 `view.某成员(` 的块按兜底认领。`check-doc-coverage.mjs` 因此多了一项「实例成员 × 逐方法示例」:235 个成员每个都要在示例里露面,缺一个就 FAIL(补「自定义运算符」那一段就是这个对账逼出来的)。页面(`docs/api/index.html`)自包含:左侧分组 + 搜索 + 类型开关,右侧成员卡(参数 / 返回 / 说明 / 示例 + 高亮 + 复制),底部 options 逐条列四件套;自测页通过 `__gisApi` 钩子读它的 DOM,钉住 235 张卡都有示例、86 个 options 四件套、搜索过滤与 `select(名字)` 高亮、类型开关能单独收起属性。
103
103
  - **验证**:`npx tsc --noEmit` 0 错;十三页自测共 **1625** 条 0 FAIL(`selftest-scale.html` 95 / `selftest-features.html` 522 / `selftest-demos.html` 150 / `selftest-docs.html` 73);`npm run check:docs` 全绿;`npm run build` 通过。
@@ -14,7 +14,7 @@
14
14
  - **选哪一对**:只要世界坐标(读落点、对账)用 `clientToWorld` / `worldToClient`;要把鼠标位置喂给只吃画布像素的接口 —— `pick` / `getContextAt` / `nearestPointsAt` / `nearestLineAt` / `getFeaturesInRect` / `selectInBox` / `setDropPreview({ screen })` / `zoomTo(scale, origin)` 的锚点 —— 先用 `clientToScreen` 转一档(世界坐标喂不进这些接口);手上已经握着画布像素(`getPointer().screen`、拾取载荷的 `screen`、`listPoints({ withScreen })`),要摆 `position: fixed` 的浮层用 `screenToClient`。两对数学上可互推(`clientToScreen` 的结果就等于 `project(clientToWorld(...))`),专名省掉绕世界坐标那一步,也与指针事件共用同一份 canvas rect 缓存 —— 组件自己的提示框跟随(`handlePointerMove`)走的也是它。
15
15
  - **像素比**:默认取 `window.devicePixelRatio`,用 `pixelRatio` 覆盖。线宽、节点尺寸、虚线图案、光标热点都是 **CSS 像素**,在高分屏上会按像素比放大到设备像素,因此屏幕上看起来始终一样大。另外有一层画布**像素预算**(`maxCanvasPixels`,默认 400 万):全屏 / 高分屏下 CSS 面积一大,渲染倍率就按 `sqrt(预算 / CSS 面积)` 压下来 —— 填充率不随屏幕面积线性上涨(全屏 + dpr 2 曾经是窗口态的十倍像素),线宽与字号仍然是 CSS 像素,观感基本不变。
16
16
  - **宿主缩放**:页面用 CSS `zoom`(大屏适配的常见做法:`document.body.style.zoom = innerWidth / 1920`)或祖先的 `transform: scale` 把这一层整体放大 / 缩小时,组件里的 **CSS 像素一律是布局像素** —— 线宽、节点尺寸、字号跟着页面一起变大,与旁边的 DOM 一致(不是保持设备像素大小不变)。画布备份像素按「设备像素比 × 显示比例」开,缩放之后仍然 1:1 落在设备像素上,画面不发糊。`getViewport()` 里的 `displayScale` 就是这个比例(1 表示没有缩放):自建 DOM 覆盖层时,`getBoundingClientRect()` 量到的是缩放后的视觉像素,除以它才是与 `project()` / `unproject()` 同一套坐标。指针事件(`onHover` / `onPointer` / `getPointer`)与 `project` / `unproject` 已经是布局像素,直接用;`onViewport` 回调也会带上这个字段。**运行期**改 CSS `zoom` 不用自己做什么:内容盒与设备像素两档 `ResizeObserver` 各挂一个实例(同一个实例对同一元素再 `observe` 一次是改选项、会把前一次顶掉),放大 / 缩小都会自己补一次重排(逐帧实测:第 2 帧起显示比例 / 备份像素 / 标签偏移 / 指针命中全部对齐,demo 的「宿主缩放」滑杆来回拉也跟得上),运行期改祖先的 `transform: scale` 不动布局、观察不到,把新比例交给 `view.resize()`(或在窗口 resize 时一起改)即可同步;显示比例 1% 以内按 1 处理、夹在 0.1 ~ 8,鹰眼图的备份像素在这条乘积小于 1 时按 1 倍开(轻微超采样,只会更清楚)。
17
- - **尺寸的三个层次**(节点与线路都有):要素自身的 `size` / `width` → 规则的 `size` / `width` → 全局 `pointStyle` / `lineStyle`;再乘 `pointSizeScale` / `lineWidthScale`(常数倍数)与地图缩放的联动指数 `pointZoomScale` / `lineZoomScale`(0 表示不跟随,1 表示与地图同倍率;没写联动指数时由自适应比例开关 `zoomScaleAuto` 兜底,打开就按 1 算,见「自适应比例(zoomScaleAuto)」)。
17
+ - **尺寸的三个层次**(节点与线路都有):要素自身的 `size` / `width` → 规则的 `size` / `width` → 全局 `pointStyle` / `lineStyle`;再乘 `pointSizeScale` / `lineWidthScale`(常数倍数)与地图缩放的联动指数 `pointZoomScale` / `lineZoomScale`(0 表示不跟随,1 表示与地图同倍率)。
18
18
 
19
19
  - **经纬度与 GeoJSON**:配了 GeoJSON 背景(`background.geoJson`)就多一套换算:`lngLatToWorld` / `worldToLngLat` 把经纬度接到画出来的世界坐标上(`aspectScale` 只压经度、绕整体范围中心),`lngLatToScreen` / `screenToLngLat` 直接连到画布 CSS 像素;数据不在底图的坐标系里时用 `createGeoMapper` / `mapDataToGeoJson` 把点、线路与通道搬到画出来的底图上,见「经纬度与数据映射」。
20
20
 
@@ -28,7 +28,7 @@
28
28
  | `lngLatToWorld(lng, lat)` / `worldToLngLat(x, y)` | 经纬度 ↔ 世界坐标 | 已经含 `aspectScale`:`lngLatToWorld(rawBounds.minX, …)` 正好落在底图左边缘,与画出来的底图完全对齐 |
29
29
  | `lngLatToScreen(lng, lat)` / `screenToLngLat(x, y)` | 经纬度 ↔ 画布 CSS 像素 | 画布内的像素坐标(左上角为原点),与 `onPointer` / `getPointer()` 用的是同一套 |
30
30
 
31
- `getPointer()` 与 `onPointer` 回调共用同一份光标状态 `{ inside, screen, world, lngLat }`:`screen` 是画布 CSS 像素、`world` 是世界坐标、`lngLat` 是反算出来的经纬度(没有 GeoJSON 背景时为 `null`);鼠标移出画布时 `inside: false`,其余字段保留最后一次的值。各类指针载荷(`onHover` / `onClick` / `onDblClick` / `onPointerDown` / `onPointerUp` / `onContextMenu` / `onChannelContextMenu`)在配了 GeoJSON 背景时也会多带一个 `lngLat` 字段。
31
+ `getPointer()` 与 `onPointer` 回调共用同一份光标状态 `{ inside, screen, world, lngLat }`:`screen` 是画布 CSS 像素、`world` 是世界坐标、`lngLat` 是反算出来的经纬度(没有 GeoJSON 背景时为 `null`);鼠标移出画布时 `inside: false`,其余字段保留最后一次的值。各类指针载荷(`onHover` / `onClick` / `onDblClick` / `onPointerDown` / `onPointerUp` / `onContextMenu`)在配了 GeoJSON 背景时也会多带一个 `lngLat` 字段。
32
32
 
33
33
  数据本身不在底图的坐标系里(例如底图是经纬度、数据是另一套投影坐标)时,用映射器把数据搬到画出来的底图上:
34
34
 
@@ -138,6 +138,7 @@ createGisView(container, { points, lines, channels })
138
138
  | `visible` | `boolean` | `false` 时不画 |
139
139
  | `decorations` | `GisViewLineDecoration[]` | 沿线挂的图片 / SVG,见「线路上的装饰」 |
140
140
  | `direction` | `GisViewLineDirectionInput` | 这条线自己的方向箭头(静态,不跑动):`true` 用 `options.lineDirection`、对象逐字段覆盖、`false` / `null` 关掉;按弧长摆、朝向跟着切向(起点 → 终点),见「线路方向箭头(lineDirection)」 |
141
+ | `forward` | `boolean` | 业务正向,默认 `true`:从 `from` / `path[0]` 走向 `to` / `path[last]`。写 `false` 时装饰位置与朝向、方向箭头、线路动画都按反方向解释;它与 `effect.reverse` / `direction.reverse` 是异或关系(反两次等于正一次),不改写 `path` 本身 |
141
142
  | `lineLabel` / `lineLabelPosition` / `lineLabelVisibility` / `lineLabelOffset` / `lineLabelParallel` | | 这条线自己的标签配置(`lineLabelParallel` 是这条线自己的「与线路平行」开关) |
142
143
  | `labels` | `GisViewItemLabel \| GisViewItemLabel[]` | 这条线上的标签清单(逐条写,与节点的 `labels` 同一套,额外认 `parallel`),见「标签(labels,导入即回显)」 |
143
144
  | `tooltip` | `GisViewTooltipContent \| false` | 这条线的提示内容 |
@@ -19,6 +19,7 @@
19
19
  | `points` | `GisViewPoint[]` | `[]` | 点数据,`x` / `y` 为世界坐标(经纬度、投影米或任意平面坐标) |
20
20
  | `lines` | `GisViewLine[]` | `[]` | 线数据,`start` / `end` 支持 `{ x, y }` 或 `[x, y]`,折线用 `path`,端点可用 `from` / `to` 绑定点 id |
21
21
  | `channels` | `GisViewChannel[]` | `[]` | 通道:把若干条线路组织成并行敷设的多回线走廊,回线几何由骨架折线按偏移推导;两端可用 `from` / `to` 绑在节点上 |
22
+ | `pointCluster` | `boolean \| GisViewPointClusterOptions` | 关 | 点聚合:`true` 使用缺省档(屏幕半径 60px / 至少 2 点 / 不限缩放),对象写法可调 `enabled` / `radius` / `minPoints` / `maxZoom`。聚合圆替代密集原始点参与绘制、拾取、快照与导出;原始 `points` / `getData()` 不变,缩放超过 `maxZoom` 后恢复逐点显示,见「点聚合(pointCluster)」 |
22
23
  | `channelWidth` | `number` | `0.02` | 通道宽度的世界单位写法(`tangent` 模式下是最外侧两条回线中心的间距,`inset` 模式下先按它算再收),**只在 `nodeWidth` 为 0 或两端没绑节点时才真正决定宽度**;单个通道可用 `width` 覆盖 |
23
24
  | `channelInset` | `number` | `0` | `offsetMode: 'inset'` 时从切点收 / 放的比例(占通道宽度):正数内收(最多 0.42),**负数外收**(最多 -0.5,可用区间撑到 2 倍宽);单个通道可用 `inset` 覆盖,见「通道与多回线」 |
24
25
  | `channelNodeWidth` | `number` | `1` | 节点宽度倍数:通道宽度按两端节点尺寸取的倍数,`1` 表示回线正好铺在节点两边、节点放大回线跟着变宽,`0` 表示所有通道退回 `width` / `channelWidth` 的世界单位写法,单个通道可用 `nodeWidth` 覆盖 |
@@ -37,24 +38,26 @@
37
38
  | `prebake` | `GisViewPrebakeOptions \| true` | 关 | 贴图符号预热:把「这次画面会出现什么」提前告诉组件 —— 两种写法:`combos` 逐组合(精确,`getSymbolCombos()` 收出来的那份)/ `symbols × pointColors × sizes` 与 `lineColors × 线路动画图标` 全排列(省事,颜色与尺寸由同一个字段决定时会多烤很多)—— 第一帧之前就烤好,画到它们时命中的是现成的贴图(省掉解码 + 光栅化那一下);组合数与数据量无关,不配就是从前那条按需路径。读数 `getPrebake()` / `getSymbolCombos()`、手动再烤一遍 `runPrebake()`,见「贴图符号预热(prebake)」 |
38
39
  | `label` | `GisViewLabelOptions \| GisViewLabelOptions[]` | 无 | 节点标签:内容 / 位置 / 显隐 / 避让 / 拖动与编辑都可配,**写数组就是同一个节点上多条标签**,与 `lineLabel` 共用一套避让网格,见「节点标签」 |
39
40
  | `labelOptimize` | `boolean` | `false` | 标签显示优化开关:**缺省关 = 强制显示**(不避让、不按 `maxCount` 截断、不设 `scanLimit` / 扫描预算,节点 / 线路的每个可见要素该出现的标签一条不少);写 `true` 与从前一致 —— 避让 / 换落点 / 数量上限 / 候选上限全部生效。标签订得密、数据量大(十万级)时建议打开,见「标签显示优化开关」 |
40
- | `labelRender` | `'auto' \| 'dom'` | `'auto'` | 纯文本标签的容器渲染方式:`auto` 在强制显示档(`labelOptimize` 未开且未进密集档)下,同一层纯文本标签达到 1024 条时改画到一张 Canvas,不再逐条建 `div`;HTML / 元素内容 / 可拖拽 / 可编辑 / 超出 Canvas 样式能力的标签仍走 DOM。`dom` 始终逐条建 `.gis-view__label`。只影响容器,不改内容、锚点、显隐与 `html` 解析,见「标签容器渲染(labelRender)」 |
41
+ | `labelRender` | `'auto' \| 'canvas' \| 'dom'` | `'auto'` | 纯文本标签的容器渲染方式:`auto` 在强制显示档(`labelOptimize` 未开且未进密集档)下,同一层纯文本标签达到 1024 条时改画到一张 Canvas;`canvas` 不受数量阈值限制,优化档 / 密集档也强制画布并直接用画布尺寸参与避让;`dom` 始终逐条建 `.gis-view__label`。HTML / 元素内容 / 可拖拽 / 可编辑 / 超出 Canvas 样式能力的标签仍走 DOM。只影响容器,不改内容、锚点、显隐以及 `maxCount` / `when` / `scanLimit` 等优化策略,见「标签容器渲染(labelRender)」 |
41
42
  | `lineLabel` | `GisViewLineLabelOptions \| GisViewLineLabelOptions[]` | 无 | 线路标签:位置沿线走(比例 / 百分比 / 绝对距离),内容、避让、显隐都可配,与节点标签共用一套避让网格;**写数组就是同一条线上多条标签**,见「线路标签」 |
42
43
  | `labelStates` | `GisViewLabelState[]` | 无 | 标签的运行时状态(`getLabelStates()` 的那一份:拖出来的偏移、改过的内容与样式、运行时加的标签、隐藏覆盖):创建时喂回来就回显,等价于创建之后调 `setLabelStates(states)`;`updateOptions({ labelStates })` 同样认。`exportData({ format: 'input' })` 的结果里就叫 `labelStates`,存档 → 回灌直接接上 |
43
44
  | `graphics` | `GisViewGraphic[]` | 无 | 与节点无关的扩展图形:按世界坐标放 SVG 路径 / 图片 / Canvas,支持锚点、偏移、旋转、透明度、`zIndex` 与 `scaleWithView`;位于画布之上、标签之下,运行时用 `addGraphic` / `addGraphics` / `updateGraphic` / `removeGraphic` / `clearGraphics` 增删改,见「扩展图形(graphic)」 |
44
45
  | `tooltip` | `GisViewTooltipOptions` | 无 | 悬停提示:点 / 线 / 通道分别配置内容,见「悬浮提示」 |
45
46
  | `minimap` | `GisViewMinimapOptions \| null` | 无 | 内置鹰眼图:位置、尺寸、节点 / 线路 / 视口框三份样式与交互都可配,`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,见「鹰眼图」 |
46
- | `filter` | `GisViewFilterInput \| null` | 无 | 初始筛选条件,等价于创建之后调用 `setFilter`,见「筛选」 |
47
+ | `filter` | `GisViewFilterInput \| null` | 无 | 初始筛选条件,等价于创建之后调用 `setFilter`;`mode` 缺省 `'hide'`,写 `'remove'` 则删除不命中的要素,见「筛选」 |
47
48
  | `filterWorker` | `boolean \| GisViewFilterWorkerOptions` | `{ threshold: 20000 }` | 筛选是否用 Web Worker 计算:默认点 + 线总数超过阈值才用,`false` 表示始终同步算 |
48
49
  | `searchWorker` | `boolean \| GisViewSearchWorkerOptions` | `{ threshold: 20000 }` | [异步搜索](14-视图与筛选.md#搜索与定位)是否用 Web Worker 计算:默认点 + 线总数超过阈值才用,`false` 表示 `searchAsync` 也同步算;同步的 `search` 一直在主线程算,不受这一项影响 |
49
50
  | `pointHoverStyle` / `pointSelectStyle` | `GisViewPointStyle` | 无 | 点的悬停 / 选中样式;没配就不改变外观(悬停仍然触发回调与提示、选中仍然进选中集合),要反馈就自己配一份 |
50
51
  | `lineHoverStyle` / `lineSelectStyle` | `GisViewLineStyle` | 无 | 线的悬停 / 选中样式;没配同样不改变外观 |
52
+ | `cascadeHighlight` | `boolean \| GisViewCascadeHighlightOptions` | 关 | 点击级联高亮:`true` 亮节点 + 直连线路;对象可设 `enabled`、`scope`、`triggerOnClick`、`triggerOnMultiSelect`、`triggerOnBoxSelect`、独立的 `pointStyle` / `lineStyle` 与 `clearOnEmptyClick`。它不改选中集,见「点击级联高亮」 |
51
53
  | `pointMultiSelectStyle` / `pointBoxSelectStyle` | `GisViewPointStyle` | 无 | 多选(Ctrl + 点击)/ 框选(Shift + 拖动,可改 `shortcuts.boxSelect`)命中的点样式,缺省与 `pointSelectStyle` 一致,单个点可用 `multiSelectStyle` / `boxSelectStyle` 覆盖 |
52
54
  | `lineMultiSelectStyle` | `GisViewLineStyle` | 无 | 多选命中时的线样式,缺省与 `lineSelectStyle` 一致,单条线可用 `multiSelectStyle` 覆盖;框选出来的线路仍走 `lineSelectStyle`(线路没有单独的框选样式) |
53
55
  | `defaultHighlight` | `boolean` | `false` | 没有配置状态样式时是否仍用尺寸 / 宽度增幅与强调环画出内置强调;默认 `false`(不配样式就不改变外观),设为 `true` 可退回旧行为 |
54
- | `pointSizeScale` / `lineWidthScale`、`pointZoomScale` / `lineZoomScale`、`zoomBaseScale` | `number` | `1` / `0` | 两组系数各管一件事,不重复:`pointSizeScale` / `lineWidthScale` 是与地图无关的整体放大(常数倍数,屏幕上的大小因此恒定),`pointZoomScale` / `lineZoomScale` 是「跟随地图缩放」的指数(0 表示不跟随,1 表示与地图同倍率放大,0.5 是地图放大 4 倍、要素放大 2 倍),绘制尺寸是两者相乘;`zoomBaseScale` 是联动基准,缺省取首次适应视图时的比例;`zoomScaleAuto` 是自适应比例开关(见下一行) |
55
- | `zoomScaleAuto` | `boolean` | `false` | 自适应比例开关:打开后**没写联动指数的** `pointZoomScale` / `lineZoomScale` 按 1(与地图同倍率)算 —— 包围盒变大、相机比例变小,节点与线宽跟着一起小,节点大小与通道间距的比例不再被带跑;显式写过指数以显式值为准,见「自适应比例(zoomScaleAuto)」 |
56
+ | `pointSizeScale` / `lineWidthScale`、`pointZoomScale` / `lineZoomScale`、`zoomBaseScale` | `number` | `1` / `0` | 两组系数各管一件事,不重复:`pointSizeScale` / `lineWidthScale` 是与地图无关的整体放大(常数倍数,屏幕上的大小因此恒定),`pointZoomScale` / `lineZoomScale` 是「跟随地图缩放」的指数(0 表示不跟随,1 表示与地图同倍率放大,0.5 是地图放大 4 倍、要素放大 2 倍),绘制尺寸是两者相乘;`zoomBaseScale` 是联动基准,缺省取首次适应视图时的比例 |
57
+ | `fitSpacing` | `boolean \| GisViewFitSpacingOptions` | 关 | 适应视图的节点间距兜底:`true` 用 `{ minDistance: 24, maxZoom: 0 }`,对象可改最小中心距(CSS 像素)与相对普通适应比例的上限(`0` 表示不限);只在首次默认适应与 `fitView()` 生效,远处孤立点允许落到视口外,见「适应视图的节点间距(fitSpacing)」 |
56
58
  | `boxSelect` | `boolean` | `false` | 是否允许按住框选快捷键(`shortcuts.boxSelect`,默认 Shift)拖动鼠标框选要素;**缺省关**,写 `boxSelect: true` 或 `features: { boxSelect: true }` 才开。框选默认只收节点,`selectionKinds` 可把线路 / 通道也纳入 |
57
59
  | `multiSelect` | `boolean` | `false` | 多选开关:打开后不用按修饰键,直接单击节点 / 线路就取反累加选中(与按住 `shortcuts.multiSelect` 点击同一套行为、同一个 `multiSelectStyle`;取反那一下不经过 `onClick`,`onSelectChange.action.type` 是 `toggleSelect`);**缺省关**,关着时多选走框选(`boxSelect`)与按住多选快捷键(默认 Ctrl / ⌘)。运行时 `updateOptions({ multiSelect })` 切换只重画一帧、不重排场景 |
60
+ | `selectTrigger` | `'click' \| 'dblClick'` | `'click'` | 选中触发方式:`click` 第一次单击就改选中集;`dblClick` 第一下只派发 `onClick`,等双击判定命中的第二下才改选中集,并同时派发 `onClick` 与 `onDblClick`。Ctrl / ⌘ 多选与 `multiSelect` 在双击模式下同样等第二下才取反。运行时走 `updateOptions({ selectTrigger })`,只重画一帧、不重排场景 |
58
61
  | `selectionKinds` | `GisViewSelectionKindsInput` | 无(框选只收节点;Ctrl 多选收节点 / 线路 / 通道) | 框选与 Ctrl / 多选点击的类型过滤:`'point'` 节点、`'line'` 通道外的独立线路、`'channel'` 通道回线;可写数组任意组合,`'all'` / `true` 全开,`false` / `null` 全关。通道回线只归 `channel` 管,通道外的线路只归 `line` 管;通道回线仍按 `channelSelectMode` 摊开。不写时保留旧口径;普通单选与 `select` / `selectFeatures` 不受它限制。运行时走 `updateOptions({ selectionKinds })`,见「框选」与「选择类型过滤」 |
59
62
  | `selectionBoxStyle` | `GisViewSelectionBoxStyle` | 蓝底 0.16 透明 + 1px 蓝框 | 框选矩形的填充与边框:`fill`、`fillOpacity`、`stroke`、`strokeWidth` |
60
63
  | `selectionOnTop` | `boolean` | `true` | 选中的节点 / 拖动中的整组 / 待确认合并的目标节点,以及选中线路所在的整条通道,是否画在所有要素之上(与 `zIndex` 无关),见「选中 / 拖动的层级」 |
@@ -68,10 +71,11 @@
68
71
  | `gridSize` | `number` | `96` | 网格线的屏幕目标间距,实际步长吸附到 1 / 2 / 5 × 10^n |
69
72
  | `showGrid` | `boolean` | `false` | 是否绘制网格:与其它功能开关同一条口径,**缺省关**,写 `showGrid: true` 才画 |
70
73
  | `interactive` | `boolean` | (缺省全关) | 查看类交互的「详细配置手动打开」写法:写 `true` 打开拖拽平移 / 滚轮缩放 / 右键回调(等价 `pan` / `zoom` / `contextMenu` 三个开关),不写就是全关 |
71
- | `preventContextMenu` | `boolean` | `true` | 组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):`contextMenu` 开着(或 `interactive: true` / `features: 'view'`)时组件才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` / `onChannelContextMenu` 照常发)。右键整个不接时(`contextMenu` 缺省关)系统菜单自然照弹 |
74
+ | `preventContextMenu` | `boolean` | `true` | 组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):`contextMenu` 开着(或 `interactive: true` / `features: 'view'`)时组件才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` 照常发)。右键整个不接时(`contextMenu` 缺省关)系统菜单自然照弹 |
72
75
  | `clickable` | `boolean` | (缺省全关) | 查看类交互的「详细配置手动打开」写法:写 `true` 打开悬停高亮与点击选中(等价 `hover` / `select` 两个开关),要素自身可用 `clickable: false` 逐个关掉 |
73
76
  | `draggable` | `boolean` | `false` | 点是否可拖拽,单个点可用 `draggable` 覆盖;**缺省关**,写 `draggable: true` 或 `features: { drag: true }` 才开 |
74
77
  | `liveDrag` | `boolean` | `true` | 拖动节点时线路是否实时跟随:`false` 时拖动期间线路与通道原地不动,松手(`pointerup`)后一次性更新到新位置 |
78
+ | `lineFollowsNode` | `boolean` | `true` | 线路 / 通道是否跟随节点的生命周期:`true` 时隐藏、删除、筛选收起节点会连同绑定线路与通道一起处理;`false` 时节点退场只影响节点,线路 / 通道停在最后一份坐标与解析尺寸。筛选没有显式写 `cascade` 时,关闭它会把默认联动从 `'binding'` 改为 `false`;显式 `cascade` 始终优先。运行时用 `updateOptions({ lineFollowsNode })` 切换,见「筛选」 |
75
79
  | `shortcuts` | `GisViewShortcuts` | 见「快捷键」 | 所有快捷键都可自定义:框选 / 多选 / 加点 / 复制 / 粘贴 / 取消 / 删除 / 适应视图,没写的动作沿用默认值,写 `false` 等于关掉这个动作的快捷键 |
76
80
  | `copy` | `GisViewCopyOptions` | 缺省关 | 复制节点(**写了这份配置才算开**,`enabled: false` 显式关掉):`enabled` 开关、`offset` 落点偏移(默认 `[28, 24]` 像素)、`autoCommit`(交互入口落地前是否自动确认:不写就是落地,`false` 停在预览态等 `commitPointCopy()` / 载荷里的 `payload.commit()`)、`idPrefix`(默认 `copy-`)、`createId` / `createItem` 定制复制品,见「节点复制」 |
77
81
  | `remove` | `GisViewRemoveOptions` | 缺省关 | 删除选中的拐点 / 线路 / 节点(**写了这份配置才算开**,`enabled: false` 显式关掉):`enabled` 开关、`scope`(`confirm()` 不带参数时的缺省范围:`'lines'` 只删选中的回线 / `'groups'` 连所在分组 / `'channels'` 连整条通道,缺省 `'lines'`)、`confirm`(不写停在 `pending` 等使用方裁决 / `false` 直接删 / 函数自行裁决,支持 Promise)。Delete 键与 `requestRemove()` 都先出一份「要删什么、连带什么」的载荷,确认之后才动数据;删节点会连挂在它身上的线路与被它锚定的通道一起删,见「删除(remove)」 |
@@ -103,11 +107,11 @@
103
107
  | 分组 | 字段 |
104
108
  | --- | --- |
105
109
  | 总开关与历史 | `features` · `history` |
106
- | 数据与背景 | `points` · `lines` · `channels` · `background` |
110
+ | 数据与背景 | `points` · `lines` · `channels` · `pointCluster` · `background` |
107
111
  | 通道几何 | `channelWidth` · `channelInset` · `channelNodeWidth` · `channelTangentInset` · `channelMaxGroups` · `channelGroupMaxLines` · `channelDraftStyle` · `channelSelectMode` · `channelWaypointDragMode` |
108
112
  | 样式与规则 | `pointStyle` · `lineStyle` · `pointRules` / `lineRules` · `heatmap` · `effects` · `lineDirection` · `prebake` · `label` · `labelOptimize` · `labelRender` · `lineLabel` · `labelStates` · `tooltip` · `pointSizeScale` / `lineWidthScale`、`pointZoomScale` / `lineZoomScale`、`zoomBaseScale` · `cursorStyle` · `gridColor` · `gridSize` · `showGrid` |
109
113
  | 状态样式与选中 | `pointHoverStyle` / `pointSelectStyle` · `lineHoverStyle` / `lineSelectStyle` · `pointMultiSelectStyle` / `pointBoxSelectStyle` · `lineMultiSelectStyle` · `defaultHighlight` · `selectionBoxStyle` · `selectionOnTop` · `waypointStyle` / `waypointSelectStyle` |
110
- | 交互开关 | `boxSelect` · `multiSelect` · `waypointEditable` · `waypointVisibility` · `interactive` · `preventContextMenu` · `clickable` · `draggable` · `liveDrag` · `shortcuts` · `copy` · `remove` · `merge` |
114
+ | 交互开关 | `boxSelect` · `multiSelect` · `selectTrigger` · `waypointEditable` · `waypointVisibility` · `interactive` · `preventContextMenu` · `clickable` · `draggable` · `liveDrag` · `lineFollowsNode` · `shortcuts` · `copy` · `remove` · `merge` |
111
115
  | 视图与相机 | `minScale` / `maxScale` · `fitPadding` · `fitOnInit` · `initialView` · `zoomStep` |
112
116
  | 画质与渲染 | `quality` · `width` / `height` · `pixelRatio` · `maxCanvasPixels` · `antialias` · `interactionThinLines` · `interactionNoMsaa` · `renderer` · `webglLayers` |
113
117
  | 大数据(筛选 / 搜索 / 读数) | `denseMode` · `filter` · `filterWorker` · `searchWorker` · `progressiveInit` · `statsInterval` · `renderEmitInterval` |
@@ -192,6 +196,24 @@ const channels = [{
192
196
  }]
193
197
  ```
194
198
 
199
+ #### `pointCluster`
200
+
201
+ - **类型**:`boolean | GisViewPointClusterOptions`
202
+ - **默认值**:关
203
+ - **说明**:点聚合。`true` 等价于 `{ enabled: true, radius: 60, minPoints: 2, maxZoom: 0 }`;对象写法里的 `enabled` 缺省 `true`,`radius` 是屏幕 CSS 像素半径(缺省 60,夹在 8 ~ 500),`minPoints` 是合成一个聚合圆所需的最少点数(缺省 2,最小 2),`maxZoom` 是相机比例相对 `zoomBaseScale` / 首次适应视图比例的倍数,超过后展开原始点(`0` 表示不限)。聚合用固定网格建组,单次按数据量 O(n) 扫描;平移复用当前成员,只在缩放、配置、数据、选中、拖动、合并或复制预览变化时重算。聚合期间发散初始化和追加式补点暂停;选中、拖动、待确认合并与复制预览中的点强制保持单成员。它只改展示与命中结果:`getData()` 仍是原始数据,`listPoints({ visibleOnly: false })` 仍能列全,默认 `visibleOnly: true` 会跳过被聚合圆收起的成员;`getClusters()` 含单成员展示组,`getPointCluster()` 返回归一化配置与当前 `active`,统计在 `getStats().clusterCount` / `clusterPointCount` / `clusterMs`。点击聚合圆只发 `clusterClick` / `onClusterClick`,不选中内部节点。推荐与 `denseMode` 配合使用;需要看真实符号时用 `maxZoom`,或直接把 `pointCluster` 设为 `false`。演示见 `?demo=cluster`
204
+ - **示例**:
205
+
206
+ ```js
207
+ const view = createGisView(container, {
208
+ points,
209
+ pointCluster: { radius: 64, minPoints: 3, maxZoom: 2.5 }
210
+ })
211
+
212
+ view.updateOptions({ pointCluster: false }) // 关掉,原始点全部恢复
213
+ const groups = view.getClusters() // 含 count: 1 的单成员
214
+ const { active, radius } = view.getPointCluster() // 当前缩放档是否仍在聚合
215
+ ```
216
+
195
217
  #### `background`
196
218
 
197
219
  - **类型**:`GisViewBackground`
@@ -388,9 +410,9 @@ view.updateOptions({ prebake: { combos: view.getSymbolCombos().combos } })
388
410
 
389
411
  #### `labelRender`
390
412
 
391
- - **类型**:`'auto' | 'dom'`
413
+ - **类型**:`'auto' | 'canvas' | 'dom'`
392
414
  - **默认值**:`'auto'`
393
- - **说明**:纯文本标签的画法。`auto` 只在**强制显示档**(缺省 `labelOptimize: false` 且 `denseMode` 未激活)启用:节点层或线路层里可画布的纯文本标签达到 **1024** 条时,该层改用一张 Canvas,不再逐条建 `.gis-view__label`;`html: true`、函数返回 `HTMLElement`、可拖拽 / 可编辑,以及带 Canvas 不支持样式的标签仍保留 DOM。`dom` 始终走逐条 DOM,保持旧行为。它和 `html` 是两件事:`html` 只控制字符串按文本还是 HTML 解析,`html: false` 仍然会建 `div`;见「标签容器渲染(labelRender)」
415
+ - **说明**:纯文本标签的容器画法。`auto` 只在**强制显示档**(缺省 `labelOptimize: false` 且 `denseMode` 未激活)启用:节点层或线路层里可画布的纯文本标签达到 **1024** 条时,该层改用一张 Canvas,不再逐条建 `.gis-view__label`。`canvas` 显式选择画布,不受 1024 条阈值限制,`labelOptimize: true` 或密集档也生效;`maxCount` / `when` / `scanLimit` / 避让与换落点仍照常执行,只是尺寸直接取画布测量结果,不创建也不测量 DOM。`dom` 始终逐条建 DOM,保持旧行为。`html: true`、函数返回 `HTMLElement`、可拖拽 / 可编辑,以及带 Canvas 不支持样式的标签在三种模式下都仍保留 DOM。它和 `html` 是两件事:`html` 只控制字符串按文本还是 HTML 解析,`html: false` 仍然会建 `div`;见「标签容器渲染(labelRender)」
394
416
  - **参考**:见「标签容器渲染(labelRender)」一节
395
417
 
396
418
  #### `lineLabel`
@@ -441,12 +463,12 @@ view.clearGraphics()
441
463
  - **说明**:两组系数各管一件事,不重复:`pointSizeScale` / `lineWidthScale` 是与地图无关的整体放大(常数倍数,屏幕上的大小因此恒定),`pointZoomScale` / `lineZoomScale` 是「跟随地图缩放」的指数(0 表示不跟随,1 表示与地图同倍率放大,0.5 是地图放大 4 倍、要素放大 2 倍),绘制尺寸是两者相乘;`zoomBaseScale` 是联动基准,缺省取首次适应视图时的比例
442
464
  - **参考**:见「点配置」一节,子字段在那里的表里逐个展开
443
465
 
444
- #### `zoomScaleAuto`
466
+ #### `fitSpacing`
445
467
 
446
- - **类型**:`boolean`
447
- - **默认值**:`false`
448
- - **说明**:自适应比例开关 —— 打开后**没写联动指数**的 `pointZoomScale` / `lineZoomScale` 按 `1`(与地图同倍率)算:包围盒变大、相机比例变小时节点与线宽跟着一起小,节点大小与通道间距的比例保持不变(不打开时节点在屏幕上是恒定大小,包围盒一大就显得挤);显式写过指数(哪怕写 `0`)的项以显式值为准,开关只给没写的兜底;基准仍是 `zoomBaseScale`(缺省首次适应视图时的比例),想拿当下这一屏重新当基准调 `setZoomBaseScale()`
449
- - **参考**:见「自适应比例(zoomScaleAuto)」一节
468
+ - **类型**:`boolean | GisViewFitSpacingOptions`
469
+ - **默认值**:关
470
+ - **说明**:适应视图的节点间距兜底。普通适应按数据包围盒铺满,远处多一个孤立点就会把比例压小、主体节点挤成一团;打开后先按普通规则算出适应比例,再用**可见节点最近的正中心距**反推一个更高的比例(最近距离达到 `minDistance` 为止)。**只改比例,不改中心**:相机中心仍取普通适应的包围盒中心,最近节点对并列时不挑中点、不跳视点。`maxZoom` 是相对普通适应比例的上限,`0` 表示不限;两种情况仍受 `maxScale` 约束。重合坐标不算有效间距,少于两个不同位置时不调整。显式写了 `initialView.scale` / `zoom` 时以显式值为准;运行时 `updateOptions` 不会当场移动相机,需要再调 `fitView()`
471
+ - **参考**:见「适应视图的节点间距(fitSpacing)」一节
450
472
 
451
473
  #### `cursorStyle`
452
474
 
@@ -505,6 +527,40 @@ createGisView(container, {
505
527
  })
506
528
  ```
507
529
 
530
+ #### `cascadeHighlight`
531
+
532
+ - **类型**:`boolean | GisViewCascadeHighlightOptions`
533
+ - **默认值**:关
534
+ - **说明**:点击级联高亮,和选中集是两份互不污染的状态。点节点时亮自身、直接相连的线路和另一端节点;点线路 / 通道回线时只亮两端节点。直接选中的节点 / 线路始终走选中样式,只有邻接项套 `pointStyle` / `lineStyle`。变化会通过 `onCascadeHighlight` / `cascadeHighlight` 事件给出,读数用 `getCascadeHighlight()`
535
+ - **参考**:见「点击级联高亮」一节
536
+ - **子字段**:
537
+
538
+ | 字段 | 类型 | 缺省 | 说明 |
539
+ | --- | --- | --- | --- |
540
+ | `enabled` | `boolean` | `true` | 开关;写对象且 `enabled: false` 等于关闭 |
541
+ | `scope` | `'points' \| 'both'` | `'both'` | `points` 只亮节点;`both` 同时亮直连线路(通道回线也按线路处理) |
542
+ | `triggerOnClick` | `boolean` | `true` | 普通单击是否触发 / 更新级联。设 `false` 时单击只走选中,并收掉旧级联 |
543
+ | `triggerOnMultiSelect` | `boolean` | `true` | Ctrl / ⌘ 累加点击、以及 `options.multiSelect` 的累加单击是否触发级联;设 `false` 时多选只改选中集 |
544
+ | `triggerOnBoxSelect` | `boolean` | `false` | 框选 / `selectInBox()` 是否触发级联。打开后框内所有命中项一起算邻接并集,直接命中的项仍走框选样式 |
545
+ | `pointStyle` | `GisViewPointStyle` | 琥珀色描边、`sizeScale: 1.2` | 级联节点样式,整份替换;字段同 `pointStyle` |
546
+ | `lineStyle` | `GisViewLineStyle` | 琥珀色、`width: 2.6`、`opacity: 0.95` | 级联线路样式,整份替换;字段同 `lineStyle` |
547
+ | `clearOnEmptyClick` | `boolean` | `true` | 点空白处是否收掉级联;`false` 时保留当前读数 |
548
+
549
+ `true` 等价于全部缺省值(`scope: 'both'`、`triggerOnClick: true`、`triggerOnMultiSelect: true`、`triggerOnBoxSelect: false`)。框选触发时 `origin` 取框内按数据顺序的第一个命中项,作为这一批级联的代表来源。
550
+ - **示例**:
551
+
552
+ ```js
553
+ createGisView(container, {
554
+ points, lines, channels,
555
+ cascadeHighlight: {
556
+ scope: 'points',
557
+ triggerOnBoxSelect: true,
558
+ pointStyle: { color: '#fff7ed', strokeColor: '#f59e0b', strokeWidth: 2, sizeScale: 1.2 }
559
+ },
560
+ onCascadeHighlight: payload => console.log(payload?.pointIndexes)
561
+ })
562
+ ```
563
+
508
564
  #### `pointMultiSelectStyle` / `pointBoxSelectStyle`
509
565
 
510
566
  - **类型**:`GisViewPointStyle`
@@ -579,6 +635,19 @@ createGisView(container, { multiSelect: true }) // 单击即取反累加,不
579
635
  // 按住快捷键的老写法照旧:Ctrl + 点击
580
636
  ```
581
637
 
638
+ #### `selectTrigger`
639
+
640
+ - **类型**:`'click' | 'dblClick'`(也认 `doubleClick` / `double` 两种别名)
641
+ - **默认值**:`'click'`
642
+ - **说明**:选中触发方式。`click` 保持缺省行为,第一次单击就改选中集;`dblClick` 下第一下只派发 `onClick`,同位置 320ms 内的第二下才应用选中 / 清空 / 取反,并在同一次点击里派发 `onClick`(`action.double: true`)与 `onDblClick`。Ctrl / ⌘ 多选与 `multiSelect` 也遵循这一档:双击模式下第二下才取反。普通双击在两种模式下都会于第二下派发 `onDblClick`,只是 `click` 模式第一次单击已经改过选中集;`click` 模式的 Ctrl / `multiSelect` 取反快捷路保持原口径,不额外发点击 / 双击事件。运行时切换会清掉上一次点击的双击判定状态,避免刚改完后的第一下被算成旧序列的第二下。
643
+ - **参考**:见「[08 选中与拐点](08-选中与拐点.md#点击选中与-dblclick)」与「[04 事件与回调](04-事件与回调.md#回调)」
644
+ - **示例**:
645
+
646
+ ```js
647
+ createGisView(container, { selectTrigger: 'dblClick' })
648
+ view.updateOptions({ selectTrigger: 'click' })
649
+ ```
650
+
582
651
  #### `selectionKinds`
583
652
 
584
653
  - **类型**:`GisViewSelectionKindsInput`
@@ -623,7 +692,7 @@ view.selectInBox(rect, { selectionKinds: 'channel' }) // 仅这一次只框通
623
692
 
624
693
  - **类型**:`boolean`
625
694
  - **默认值**:`true`
626
- - **说明**:组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):`contextMenu` 开着(或 `interactive: true` / `features: 'view'`)时组件才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` / `onChannelContextMenu` 照常发)。右键整个不接时(`contextMenu` 缺省关)系统菜单自然照弹
695
+ - **说明**:组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):`contextMenu` 开着(或 `interactive: true` / `features: 'view'`)时组件才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` 照常发)。右键整个不接时(`contextMenu` 缺省关)系统菜单自然照弹
627
696
 
628
697
  #### `clickable`
629
698
 
@@ -643,6 +712,22 @@ view.selectInBox(rect, { selectionKinds: 'channel' }) // 仅这一次只框通
643
712
  - **默认值**:`true`
644
713
  - **说明**:拖动节点时线路是否实时跟随:`false` 时拖动期间线路与通道原地不动,松手(`pointerup`)后一次性更新到新位置
645
714
 
715
+ #### `lineFollowsNode`
716
+
717
+ - **类型**:`boolean`
718
+ - **默认值**:`true`
719
+ - **说明**:线路 / 通道是否跟随节点的生命周期。默认 `true`:节点被 `visible: false`、`removePoints` 或筛选收起时,绑定的线路和以它为端点的通道一起退场,删节点也会连带删除这些线路 / 通道。写 `false` 后只动节点:节点隐藏或筛掉时,线路与通道停在最后一次解析出的坐标、骨架与宽度;删除节点也不删线路 / 通道;节点仍在时拖动它,线路照常实时更新。筛选没有显式写 `cascade` 时,关闭它会把默认联动从 `'binding'` 改为 `false`;条件里显式写了 `cascade` 仍以显式值为准。运行时用 `updateOptions({ lineFollowsNode })` 切换,会按新口径重算当前筛选
720
+ - **参考**:见「筛选」与「通道与多回线」;筛选条件自身的字段见「[筛选](../manual/14-视图与筛选.md#筛选)」
721
+ - **示例**:
722
+
723
+ ```js
724
+ const view = createGisView(container, { lineFollowsNode: false })
725
+
726
+ view.removePoints('station-1') // 只删节点;绑定线路 / 通道继续按最后坐标画
727
+ view.setFilter({ vn_kv: { gte: 525 } }) // 未写 cascade:只隐藏不符合条件的节点
728
+ view.updateOptions({ lineFollowsNode: true })
729
+ ```
730
+
646
731
  #### `shortcuts`
647
732
 
648
733
  - **类型**:`GisViewShortcuts`
@@ -870,11 +955,12 @@ const { dense } = view.getStats() // { mode, active, reason, visiblePoin
870
955
 
871
956
  - **类型**:`GisViewFilterInput | null`
872
957
  - **默认值**:无
873
- - **说明**:初始筛选条件,等价于创建之后调用 `setFilter`,见「筛选」
958
+ - **说明**:初始筛选条件,等价于创建之后调用 `setFilter`。`mode` 缺省 `'hide'` 只隐藏;写 `'remove'` 把不命中的要素从数据里删掉(先删线路、再删节点,只发一条 `filterRemove`),见「筛选」
874
959
  - **参考**:见「筛选」一节,子字段在那里的表里逐个展开
875
960
 
876
961
  ```js
877
962
  view.setFilter({ points: { voltage: { gte: 500 } }, cascade: 'binding' })
963
+ view.setFilter({ points: { voltage: { lt: 220 } }, mode: 'remove' }) // 不命中的要素直接从数据删除
878
964
  view.getFilterResult() // { hiddenPoints, hiddenLines, visiblePoints, visibleLines, worker, durationMs }
879
965
  view.setFilter(null) // 清掉筛选
880
966
  ```
@@ -963,4 +1049,6 @@ const view = createGisView(container, {
963
1049
 
964
1050
  ---
965
1051
 
1052
+ **相关章节**:[07 指针与要素编辑](07-指针与要素编辑.md) · [09 通道与多回线](09-通道与多回线.md) · [14 视图与筛选](14-视图与筛选.md) · [16 实例方法总表](16-实例方法总表.md)
1053
+
966
1054
  [← 02 坐标与数据](02-坐标与数据.md) · [手册目录](../GisView.md) · [04 事件与回调 →](04-事件与回调.md)
@@ -8,13 +8,15 @@
8
8
  | --- | --- | --- |
9
9
  | `onReady` | `{ canvas, gl }` | WebGL2 上下文与缓冲区就绪 |
10
10
  | `onSelect` | `GisViewSelectPayload \| null` | 选中状态变化:点击为 `{ items, box: null, originalEvent }`,框选(默认按住 Shift)为 `{ items, box, originalEvent }`,点击空白处清空时为 `null`;Ctrl 点击节点的累加选中也走这里 |
11
- | `onClick` | `GisViewClickPayload \| null` | 单击要素或空白处(空白处为 `null`):`type` / `item` / `index` / `screen` / `coordinate` / `button` / `selection` / `originalEvent` 之外还有 `action`(`type` 是 `select` / `clearSelection` / `click`,`double` 说明这一下是不是双击里的第二下,`press` / `duration` / `longPress` 说明这一次按下想做什么、按了多久、算不算长按),见「指针事件」 |
12
- | `onDblClick` | `GisViewClickPayload \| null` | 双击要素或空白处(320ms / 6px 内连续两次单击):`action.type` 是 `dblClick`、`action.double` 为 `true` |
11
+ | `onClick` | `GisViewClickPayload \| null` | 单击要素或空白处(空白处为 `null`):`type` / `item` / `index` / `screen` / `coordinate` / `button` / `selection` / `originalEvent` 之外还有 `action`(`type` 是 `select` / `toggleSelect` / `clearSelection` / `click`,`double` 说明这一下是不是双击里的第二下,`press` / `duration` / `longPress` 说明这一次按下想做什么、按了多久、算不算长按),见「指针事件」。`selectTrigger: 'dblClick'` 时第一下只发 `action.type: 'click'`,第二下才应用选中并带 `double: true` |
12
+ | `onClusterClick` | `GisViewClusterClickPayload` | 点中多成员聚合圆:带 `id` / `count` / `pointIndexes` / `pointIds` / `bounds` / `radius` / `coordinate` / `screen` / `selection` / `action`(`action.type` 恒为 `clusterClick`、`action.target` 为 `cluster`)。这一条**不会同时发 `onClick`**,也不会改动选中集;聚合圆内部的原始节点不参与普通点击 |
13
+ | `onDblClick` | `GisViewClickPayload \| null` | 双击要素或空白处(320ms / 6px 内连续两次单击):`action.type` 是 `dblClick`、`action.double` 为 `true`。普通双击在两种 `selectTrigger` 下都会于第二下派发;`dblClick` 模式下选中 / 清空 / 多选取反也在这一下应用,`click` 模式下第一下已经应用过。`click` 模式的 Ctrl / `multiSelect` 取反快捷路保持原口径,不额外发 `onClick` / `onDblClick` |
13
14
  | `onPointerDown` | `GisViewPointerPayload` | 左键 / 中键 / 右键按下:命中的要素、`button` / `buttons` 与光标位置;`action.type` 是这一次按下的**意图**(`dragPoint` / `dragWaypoint` / `boxSelect` / `pan` / `copy` / `channelDraft` / `contextMenu`…),`action.target` 说明点的是什么、`action.data` 直接给到那份数据,见「指针事件」 |
14
15
  | `onPointerUp` | `GisViewPointerPayload` | 左键 / 中键 / 右键抬起,与 `onPointerDown` 成对:`action.type` 是**刚做完的那件事**(拖过就是 `dragPoint` / `dragWaypoint` / `boxSelect` / `pan`,没拖就是 `click`),`action.moved` 说明这一次是拖还是点,另带 `action.press`(按下时的意图)/ `duration`(按了多久)/ `longPress`(长按) |
15
- | `onContextMenu` | `GisViewPointerPayload` | 每一次右键:点、线、空白都发(要素信息在载荷里);`action.type` 固定是 `contextMenu`、`action.target` 说明点的是点 / 线 / 通道 / 空白。要自己弹菜单就在回调里渲染。**右键回调缺省关**(`contextMenu` / `interactive` 都没写时组件不接右键),写 `interactive: true` / `contextMenu: true` 或 `features: 'view'` 才发;接住右键时系统菜单默认已被收掉(`preventContextMenu` 缺省 `true`),想看系统菜单写 `preventContextMenu: false` |
16
+ | `onContextMenu` | `GisViewPointerPayload` | 每一次右键:点、线、通道、空白都只发这一条(要素信息在载荷里);`action.type` 固定是 `contextMenu`、`action.target` 说明点的是点 / 线 / 通道 / 空白。命中通道回线时,同一载荷另带 `channelContext`(几组 / 当前哪组 / 可用交互),不再有单独的通道右键回调。要自己弹菜单就在回调里渲染。**右键回调缺省关**(`contextMenu` / `interactive` 都没写时组件不接右键),写 `interactive: true` / `contextMenu: true` 或 `features: 'view'` 才发;接住右键时系统菜单默认已被收掉(`preventContextMenu` 缺省 `true`),想看系统菜单写 `preventContextMenu: false` |
17
+ | `onCascadeHighlight` | `GisViewCascadeHighlightInfo \| null` | 点击级联高亮变了:载荷带来源 `origin`(节点 / 线路 / 通道回线)、范围 `scope` 与亮着的 `points` / `lines` / `pointIndexes` / `lineIndexes`,与 `getCascadeHighlight()` 同一份;点空白或关掉开关时是 `null`。这条高亮不改选中集,事件总线名为 `cascadeHighlight` |
16
18
  | `onHover` | `GisViewHoverPayload \| null` | 悬停要素变化(空白处 / 移出画布是 `null`):与拾取结果同一份读数(`type` / `index` / `item` + 通道字段,另有光标位置 `x` / `y` / `world` / `clientX` / `clientY`、选中集 `selection` 与 `lngLat`)+ `action`(`type` 恒为 `hover`、`target` 说明停在什么上面、`button` 是 `none`、`modifiers` 是当下的修饰键);程序化悬停 `hoverAt()` 也走同一条 |
17
- | `onFilter` | `GisViewFilterResult` | 筛选完成(含 Worker 异步返回):隐藏的点 / 线下标、剩余可见数量、是否用了 Worker 与计算耗时 |
19
+ | `onFilter` | `GisViewFilterResult` | 筛选完成(含 Worker 异步返回):隐藏的点 / 线下标、剩余可见数量、是否用了 Worker 与计算耗时。`filter.mode: 'remove'` 时删除完成后只发最终一次,`hiddenPoints` / `hiddenLines` 为空 |
18
20
  | `onCursor` | `[number, number] \| null` | 光标世界坐标,移出画布时为 `null` |
19
21
  | `onPointer` | `GisViewPointerState` | 光标每次移动(含移出画布)都发:`{ inside, screen, world, lngLat }`,屏幕坐标是画布 CSS 像素、`lngLat` 要有 GeoJSON 背景才有;`getPointer()` 随时能读同一份,移出时 `inside: false` |
20
22
  | `onPointDrag` | `GisViewPointDragPayload` | 拖拽点:`phase` 为 `start` / `drag` / `end`,载荷带最新坐标、本次移动的全部节点 `points` 与跟随移动的线路 `lines` |
@@ -22,7 +24,6 @@
22
24
  | `onPointMerge` | `GisViewPointMergePayload \| GisViewMergeResult \| GisViewMergeFailure \| GisViewMergeSelectedPayload` | 合并节点:拖到另一个节点上时先发 `pending`(组件不弹任何界面,在回调里调 `payload.confirm()` / `cancel()` 裁决,或存下来自己接界面),之后发 `confirm`(载荷里的 `item` 是合并结果,可改 / 可取消)、`cancel`,数据并完 / 并失败再发一条 `merge`(`ok: true` / `ok: false`);拖拽入口的载荷带 `trigger`(`'collision'` / `'center'`),说明这一次按哪种时机命中;框选 / 多选之后调 `mergeSelectedPoints()` 时**逐对发 `merge`**,收尾再发一条 `phase: 'mergeSelected'` 的汇总(`merged` 逐对列出、`skipped` 说明谁没并进去),见「节点合并」 |
23
25
  | `onRemove` | `GisViewRemovePayload` | 删除请求(Delete 键 / `requestRemove()`):`pending` 时给一份载荷说明要删什么、连带什么、能按哪些范围删(`points` / `lines` / `waypoints` / `groups` / `channels` / `actions` / `counts`),确认之后发 `phase: 'remove'`(`result` 是结果)、取消发 `phase: 'cancel'`;组件不弹任何界面,弹不弹、怎么弹都由使用方决定(在回调里调 `payload.confirm(scope)` / `payload.cancel()`,或存下来自己接界面),见「删除(remove)」 |
24
26
  | `onWaypointChange` | `GisViewWaypointPayload` | 拐点新增 / 选中 / 删除与拖动,`phase` 为 `add` / `select` / `remove` 或拖动时的 `start` / `move` / `end`,载荷带最新线路数据与所属集合的 `group_id`;通道内的回线共用骨架,载荷是**被操作的那条回线**的推导结果。同步后端只接改变数据的 `add` / `remove` / `end`,并用 `getChannelGroupWaypoints().path` 提交整份骨架,见「通道与多回线」 |
25
- | `onChannelContextMenu` | `GisViewChannelContextPayload \| null` | 右键点中通道回线:给出这条通道的上下文(有几组、当前是哪一组、分组列表与可用交互),点空白处给 `null`(用它收框);**落在节点上时也给 `null`**(点的层级高于线,见「通道右键交互框」) |
26
27
  | `onChannelDraft` | `GisViewChannelDraftPayload` | 新增通道草稿:`start` / `from` / `ready` / `pick` / `restart` / `cancel` / `commit` / `blocked`,两个节点的信息、`routing` / `path` 与可选方案都在载荷里(`path` 是中间拐点),见「新增通道」 |
27
28
  | `onChannelDraftClick` | `GisViewChannelDraftPayload` | 草稿里**每点一次**都发一条:`step` 为 `1` 时拿到起点、`2` 时拿到终点(线路到此结束);加上 `phase: 'pick'` 时是点在空白或点回起点;载荷与 `onChannelDraft` 相同 |
28
29
  | `onDecorationDraft` | `GisViewDecorationDraftPayload` | 动态装饰器草稿:`start` 开启 → `hover` 跟随命中线路或节点 / `leave` 移出收掉 → 左键在目标上松开发 `ready`(空白处不发)→ `commit` / `cancel`;`target` 为 `line` / `point` / `both`,`scope` 为 `line` / `group` / `channel`,节点侧 `placement` 为 `offset` / `fixed` / `pointer`,白名单、上限与重复策略见「动态装饰器草稿」 |
@@ -78,11 +79,11 @@ const view = createGisView('#map', {
78
79
 
79
80
  | 载荷字段 | 说明 |
80
81
  | --- | --- |
81
- | `type` | 动作名:`updateData` / `updatePoints` / `updateLines` / `updateOptions` / `updateBackground` / `updatePoint` / `updateLine` / `updateChannel` / `pointDrag` / `copyPoint` / `copyPoints` / `mergePoints` / `waypoint` / `addChannel` / `addChannels` / `addChannelGroups` / `addLine` / `addPoints` / `addLines` / `removePoints` / `removeSelected` / `removeLines` / `removeLine` / `removeChannel` / `channelGroups` / `batch` |
82
+ | `type` | 动作名:`updateData` / `updatePoints` / `updateLines` / `updateOptions` / `updateBackground` / `updatePoint` / `updateLine` / `updateChannel` / `pointDrag` / `copyPoint` / `copyPoints` / `mergePoints` / `waypoint` / `addChannel` / `addChannels` / `addChannelGroups` / `addLine` / `addPoints` / `addLines` / `removePoints` / `removeSelected` / `removeLines` / `filterRemove` / `removeLine` / `removeChannel` / `channelGroups` / `batch` |
82
83
  | `points` / `lines` / `channels` | 变更之后的最新数据(组件内部那一份的引用,只读,不要就地改) |
83
84
  | `counts` | `{ points, lines, channels }` 三个条数 |
84
85
  | `ok` / `reason` | 这一次调用成没成(`addPoints` / `addLines` / `removePoints` 这类批量接口会带上),`reason` 说明没成的原因 |
85
- | 动作专属字段 | 拖拽带 `movedPoints` / `movedLines`,复制带 `item` / `source` / `revert()`(`copyPoints` / 整组粘贴另有 `sources` / `items` / `indexes` / `lines` / `channels` / `count`),合并带 `source` / `target` / `removedLines` / `mergedChannels`,拐点带 `lineIndex` / `pathIndex` / `phase`,新增带 `lineIds` / `lineIndex` / `channelId` / `groupId`(`addPoints` / `addLines` 另有 `indexes` / `items` / `position`:新要素在数组里的位置与加进去的那几条,`addChannelGroups` 另有 `results` / `groupIds` / `items`),删除带 `indexes` / `item` / `removedLineIds`,分组带 `index` / `groups` / `action`(细分动作,与 `onChannelGroupChange` 同一份);批量补丁(`patchPoints` / `patchLines`)的 `updatePoints` / `updateLines` 带 `indexes` / `items` / `count` / `skipped`,`removeSelected` 带 `pointIds` / `lineIds` / `pointIndexes` / `lineIndexes` / `removedChannels` |
86
+ | 动作专属字段 | 拖拽带 `movedPoints` / `movedLines`,复制带 `item` / `source` / `revert()`(`copyPoints` / 整组粘贴另有 `sources` / `items` / `indexes` / `lines` / `channels` / `count`),合并带 `source` / `target` / `removedLines` / `mergedChannels`,拐点带 `lineIndex` / `pathIndex` / `phase`,新增带 `lineIds` / `lineIndex` / `channelId` / `groupId`(`addPoints` / `addLines` 另有 `indexes` / `items` / `position`:新要素在数组里的位置与加进去的那几条,`addChannelGroups` 另有 `results` / `groupIds` / `items`),删除带 `indexes` / `item` / `removedLineIds`,分组带 `index` / `groups` / `action`(细分动作,与 `onChannelGroupChange` 同一份);批量补丁(`patchPoints` / `patchLines`)的 `updatePoints` / `updateLines` 带 `indexes` / `items` / `count` / `skipped`,`removeSelected` 带 `pointIds` / `lineIds` / `pointIndexes` / `lineIndexes` / `removedChannels`,`filterRemove` 带 `mode: 'remove'` / `pointIndexes` / `removedPoints` / `removedLineIndexes` / `removedLineItems` / `lineIds` / `count` / `removedLines` / `removedChannels`(`points` / `lines` / `channels` 仍是删除后的最新数据) |
86
87
 
87
88
  `onSelectChange` 与 `onSelect` 的区别只有一个:**只在选中集真的变了时发** —— 框选过程中的重复同步、
88
89
  数据变化引起的补发都不发;并且**接口选中**(`selectFeatures()` / `selectFeatures(null)` / `selectInBox()`)也会发
@@ -92,7 +93,8 @@ const view = createGisView('#map', {
92
93
  `action` 是更细的一份判读(与指针事件同一套写法):`action.type` 是 `select`(点中一个或接口选中)/
93
94
  `toggleSelect`(Ctrl 点击取反,取反取没了也是它)/ `boxSelect`(框选,没框到东西也是它)/
94
95
  `clearSelection`(点空白、Esc 或接口清空),`action.target` 是 `selection` / `empty`,`action.data` 就是
95
- 这一批 `items`,`action.modifiers` 是这一次的修饰键。Ctrl 取反与点空白清空这两条路**不经过 `onClick`**,
96
+ 这一批 `items`,`action.modifiers` 是这一次的修饰键。单击触发模式下,Ctrl 取反与点空白清空这两条路**不经过 `onClick`**;
97
+ `selectTrigger: 'dblClick'` 时第一下仍会发 `onClick`,第二下应用选中 / 取反 / 清空后会同时发 `onClick` 与 `onDblClick`。
96
98
  要按操作分支的面板认这一个字段:
97
99
 
98
100
  ```js
@@ -154,12 +156,12 @@ view.off('*') // 不传 handler = 摘掉这个事件的全部订阅,返回摘
154
156
  | 约定 | 说明 |
155
157
  | --- | --- |
156
158
  | 事件名与回调一一对应 | `dataChange` 就是 `onDataChange`、`selectChange` 就是 `onSelectChange`,载荷完全同一份;`GisViewEventMap` 是事件名到载荷的类型表,`EVENT_NAMES` 是运行时的名字数组 |
157
- | 要 DOM 事件的四个 | `pointerDown` / `pointerUp` / `contextMenu` / `channelContextMenu` 的载荷是 `{ payload, event }`(与回调的两个参数一一对应),其余事件的载荷与回调相同 |
159
+ | 要 DOM 事件的三个 | `pointerDown` / `pointerUp` / `contextMenu` 的载荷是 `{ payload, event }`(与回调的两个参数一一对应),其余事件的载荷与回调相同 |
158
160
  | 回调抛错不扩散 | 订阅者抛错只走 `onError`:不打断其它订阅者,也不影响组件自己的流程 |
159
161
  | 高频事件不白算 | 组件内部按「有没有人订阅」短路(`count(name) \|\| count('*')`):没人听的时候不构造载荷,`pointer` / `hover` / `render` 这些每帧级别的事件也可以直接订阅 |
160
162
  | 订阅时机 | 创建时的 `ready` 与首次 `dataChange` 发生在 `createGisView` 返回之前,这两条要在 options 里写回调(`onReady` 等) |
161
163
 
162
- 全部事件名(与 `EVENT_NAMES` 一致):`ready`、`error`、`warning`、`viewport`、`cameraChange`、`cursor`、`hover`、`pointer`、`pointerDown`、`pointerUp`、`contextMenu`、`click`、`dblClick`、`labelClick`、`labelDrag`、`labelChange`、`labelContextMenu`、`labelDblClick`、`select`、`selectChange`、`pointDrag`、`pointCopy`、`pointMerge`、`remove`、`waypointChange`、`channelContextMenu`、`channelDraft`、`channelDraftClick`、`decorationDraft`、`channelGroupChange`、`dataChange`、`dataRendered`、`filter`、`history`、`featureChange`、`qualityChange`、`optionChange`、`shortcut`、`stats`、`render`、`destroy`,加上 `'*'`。
164
+ 全部事件名(与 `EVENT_NAMES` 一致):`ready`、`error`、`warning`、`viewport`、`cameraChange`、`cursor`、`hover`、`pointer`、`pointerDown`、`pointerUp`、`contextMenu`、`click`、`clusterClick`、`dblClick`、`labelClick`、`labelDrag`、`labelChange`、`labelContextMenu`、`labelDblClick`、`select`、`selectChange`、`cascadeHighlight`、`pointDrag`、`pointCopy`、`pointMerge`、`remove`、`waypointChange`、`channelDraft`、`channelDraftClick`、`decorationDraft`、`channelGroupChange`、`dataChange`、`dataRendered`、`filter`、`history`、`featureChange`、`qualityChange`、`optionChange`、`shortcut`、`stats`、`render`、`destroy`,加上 `'*'`。
163
165
 
164
166
  ## 功能开关
165
167
 
@@ -182,7 +184,7 @@ createGisView('#map', {
182
184
  | 预设 | 效果 |
183
185
  | --- | --- |
184
186
  | `'edit'` | 全开(编辑类也一起打开),等价于 `features: true` |
185
- | `'view'` | 只读浏览:一次打开查看类(平移缩放、悬停、选中、右键回调、键盘),`boxSelect` / `drag` / `waypoint` / `copy` / `merge` / `remove` / `channelDraft` / `channelMenu` 仍关着 —— 「能看能选、改不动数据」 |
187
+ | `'view'` | 只读浏览:一次打开查看类(平移缩放、悬停、选中、右键回调、键盘),`boxSelect` / `drag` / `waypoint` / `copy` / `merge` / `remove` / `channelDraft` 仍关着 —— 「能看能选、改不动数据」 |
186
188
  | `'static'` | 静态图:显式全关(含标签 / 提示 / 鹰眼图),只剩画布与数据;留着这个名字方便「从全开切回静态」 |
187
189
 
188
190
  | 开关 | 关掉之后 | 没写时跟随 |
@@ -198,8 +200,7 @@ createGisView('#map', {
198
200
  | `merge` | 拖到另一个节点上合并 | 写了 `merge` 且 `merge.enabled !== false`(缺省关) |
199
201
  | `remove` | Delete 删除选中的拐点 / 线路 / 节点(先出删除载荷,确认之后才动数据) | 写了 `remove` 且 `remove.enabled !== false`(缺省关) |
200
202
  | `channelDraft` | 新增通道草稿(程序化 `addChannel()` 不受限) | 关(缺省) |
201
- | `channelMenu` | 通道右键交互框 | 关(缺省) |
202
- | `contextMenu` | 右键回调(关掉之后组件不接右键,浏览器菜单照常弹;通道右键框也要它先开着) | `interactive === true`(缺省关) |
203
+ | `contextMenu` | 右键回调(关掉之后组件不接右键,浏览器菜单照常弹;通道回线的 `channelContext` 也随这条一起给) | `interactive === true`(缺省关) |
203
204
  | `tooltip` | 悬停提示(悬停样式与 `onHover` 还在) | 配了 `tooltip` 且没写 `enabled: false` |
204
205
  | `label` / `lineLabel` | 节点 / 线路标签图层 | 配了且没写 `enabled: false`(不配不产生 DOM) |
205
206
  | `minimap` | 内置鹰眼图 | 配了 `minimap` 且没写 `enabled: false` |
@@ -209,7 +210,7 @@ createGisView('#map', {
209
210
 
210
211
  ### 两层开关:`features` 与详细配置(重合的那几对)
211
212
 
212
- 同一件事常能写在两个地方(开关表 / 各自的详细配置),这不是重复,是「**一行说清整体口径**」与「**逐项微调**」两层。逐项对完 18 个开关的关系是三类:
213
+ 同一件事常能写在两个地方(开关表 / 各自的详细配置),这不是重复,是「**一行说清整体口径**」与「**逐项微调**」两层。逐项对完 17 个开关的关系是三类:
213
214
 
214
215
  | 能力 | 开关 | 详细配置 | 谁说了算 |
215
216
  | --- | --- | --- | --- |