gis-view-webgl2 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.d.ts +140 -60
  3. package/dist/index.js +1 -1
  4. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +73 -5
  5. package/docs/GisView.md +49 -49
  6. package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +87 -16
  7. package/docs/demos/ApiCoverageDemo.vue +1 -5
  8. package/docs/demos/BackendSyncDemo.vue +1 -2
  9. package/docs/demos/BackgroundDemo.vue +68 -1
  10. package/docs/demos/BasicDemo.vue +76 -76
  11. package/docs/demos/BatchAddDemo.vue +2 -1
  12. package/docs/demos/ChannelDemo.vue +1 -3
  13. package/docs/demos/ClusterDemo.vue +48 -1
  14. package/docs/demos/CopyMergeDemo.vue +899 -904
  15. package/docs/demos/DemoShell.vue +336 -336
  16. package/docs/demos/EventDemo.vue +300 -300
  17. package/docs/demos/ExtensionDemo.vue +200 -200
  18. package/docs/demos/GisViewDemo.vue +64 -64
  19. package/docs/demos/LabelDemo.vue +489 -489
  20. package/docs/demos/StyleDemo.vue +67 -67
  21. package/docs/demos/api-index.js +24 -29
  22. package/docs/demos/demos.js +92 -92
  23. package/docs/demos/gis-view-demo/useGisViewDemo.js +19 -14
  24. package/docs/demos/shared/demoData.js +107 -107
  25. package/docs/demos/shared/demoStyle.css +249 -249
  26. package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +3 -3
  27. package/docs/format/03-/346/213/220/347/202/271/350/243/205/351/245/260/344/270/216/346/240/267/345/274/217.md +3 -0
  28. package/docs/from0/01-/345/205/210/346/212/212/345/234/260/345/233/276/346/203/263/346/270/205/346/245/232.md +8 -7
  29. package/docs/index.html +3 -3
  30. package/docs/internals/04-/347/233/270/346/234/272/345/235/220/346/240/207/344/270/216/345/203/217/347/264/240.md +6 -4
  31. package/docs/internals/14-/350/203/214/346/231/257/345/261/202.md +2 -2
  32. package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +36 -16
  33. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +38 -84
  34. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +36 -19
  35. package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +1 -1
  36. package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +7 -7
  37. package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +15 -15
  38. package/docs/manual/10-/351/200/232/351/201/223/347/274/226/350/276/221/344/270/216/346/226/260/345/242/236.md +1 -1
  39. package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +6 -0
  40. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +1 -1
  41. package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +2 -0
  42. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +10 -8
  43. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +11 -7
  44. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +27 -8
  45. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +27 -4
  46. package/docs/manual/21-/350/257/264/346/230/216/344/270/216/345/270/270/350/247/201/351/227/256/351/242/230.md +3 -3
  47. package/docs/playground/index.html +1 -1
  48. package/docs/sample-document/345/255/227/346/256/265/350/257/246/350/247/243.md +2 -2
  49. package/docs/steps/check-doc-truth.mjs +1 -1
  50. package/docs/steps/selftest-actions.html +4 -4
  51. package/docs/steps/selftest-data.html +5 -5
  52. package/docs/steps/selftest-demos.html +1683 -1619
  53. package/docs/steps/selftest-docs.html +4 -4
  54. package/docs/steps/selftest-effects.html +1 -1
  55. package/docs/steps/selftest-features.html +548 -47
  56. package/docs/steps/selftest-full.html +566 -566
  57. package/docs/steps/selftest-labels.html +647 -647
  58. package/docs/steps/selftest-perf.html +2282 -2237
  59. package/docs/steps/selftest-select.html +53 -14
  60. package/docs/steps/step-10-full.js +1 -4
  61. package/docs/steps/step-12-events.js +1 -4
  62. package/package.json +43 -43
@@ -6,7 +6,7 @@
6
6
 
7
7
  - **世界坐标**:`x` / `y` 就是数据里的数值(经纬度、平面坐标都行),组件不做投影、不做坐标轴翻转。数据范围由节点与线路顶点共同决定(`getBounds()`)。
8
8
  - **相机**:`{ center: [x, y], scale }`,`scale` 的单位是**屏幕 CSS 像素 / 世界单位**(`getCamera()` / `setCamera()`)。`fitView(padding)` 适应整份数据,`zoomToBounds(bounds, padding)` 适应一段范围,`zoomTo(scale, origin)` 直接设比例,`zoomBy(factor, origin)` 按锚点缩放,`focusOn(target)` 缺省只挪中心。
9
- - **滚轮缩放**:以光标为锚点,`deltaY` 100 对应 1.12 倍 —— 缩放量按 `deltaY` 的大小算(`deltaMode` 是行 / 页时先折算成像素),触摸板的连续小位移因此是平滑的小步长,鼠标滚轮的一格仍是原来那一步,一次甩得再大也有上限(`deltaY` 夹在 ±240,免得某个设备一次甩出十倍)。**缩放缺省是关的**(`interactive` / `zoom` 都没写时滚轮完全不接),写 `interactive: true` 或 `features: { zoom: true }` / `features: 'view'` 才接;接着的时候一律 `preventDefault()`(含 Ctrl + 滚轮的浏览器缩放)。
9
+ - **滚轮缩放**:以光标为锚点,`deltaY` 100 对应 1.12 倍 —— 缩放量按 `deltaY` 的大小算(`deltaMode` 是行 / 页时先折算成像素),触摸板的连续小位移因此是平滑的小步长,鼠标滚轮的一格仍是原来那一步,一次甩得再大也有上限(`deltaY` 夹在 ±240,免得某个设备一次甩出十倍)。**缩放缺省是关的**,交互权限只从 `features` 读取:写 `features: { zoom: true }` 或 `features: 'view'` 才接;接着的时候一律 `preventDefault()`(含 Ctrl + 滚轮的浏览器缩放)。
10
10
  - **三档坐标口径**(`world` 不多余:数据 `x` / `y` 就是世界坐标,事件载荷里叫 `world` 只是标口径;三档之间是「客户端坐标 ↔ 画布 CSS 像素 ↔ 世界坐标」的换算链):
11
11
  - **世界坐标(`x` / `y`)**:数据、相机、几何读数、`focusOn` 与拖拽落点用的都是这一套,没有第二套;
12
12
  - **画布 CSS 像素**:以画布左上角为原点、布局像素为单位(`getViewport()` 的 `cssWidth` / `cssHeight` 就是它的范围);`project` / `unproject` / `pick` / `getContextAt` / `nearestPointsAt` / `setDropPreview({ screen })` 与 `onPointer` 的 `screen` 都是这一档,框选矩形同理;
@@ -16,7 +16,7 @@
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
17
  - **尺寸的四个层次**(节点与线路都有):全局 `pointStyle` / `lineStyle` → 普通规则的 `size` / `width` → 要素自身 → `override: true` 的规则。普通规则始终高于全局,但仍低于要素自身;只有 `override` 规则写了的字段才压过要素字段。再乘 `pointSizeScale` / `lineWidthScale`(常数倍数)与地图缩放的联动指数 `pointZoomScale` / `lineZoomScale`(0 表示不跟随,1 表示与地图同倍率)。
18
18
 
19
- - **经纬度与 GeoJSON**:配了 GeoJSON 背景(`background.geoJson`)就多一套换算:`lngLatToWorld` / `worldToLngLat` 把经纬度接到画出来的世界坐标上(`aspectScale` 只压经度、绕整体范围中心),`lngLatToScreen` / `screenToLngLat` 直接连到画布 CSS 像素;数据不在底图的坐标系里时用 `createGeoMapper` / `mapDataToGeoJson` 把点、线路与通道搬到画出来的底图上,见「经纬度与数据映射」。
19
+ - **经纬度与 GeoJSON**:配了 GeoJSON 背景(`background.geoJson`)就多一套换算:`lngLatToWorld` / `worldToLngLat` 把经纬度接到最终画出来的世界坐标上(`aspectScale`、`geoJsonStyle.bounds` 与 `rotation` 全部计入),`lngLatToScreen` / `screenToLngLat` 直接连到画布 CSS 像素;数据不在底图的坐标系里时用 `createGeoMapper` / `mapDataToGeoJson` 把点、线路与通道搬到画出来的底图上;网架 `x` / `y` 就是当前底图上的世界坐标,存档用 `mapDataToLngLat()`、回灌用 `mapLngLatToData()`,见「经纬度与数据映射」。
20
20
 
21
21
  ### 经纬度与数据映射
22
22
 
@@ -24,17 +24,19 @@
24
24
 
25
25
  | 方法 | 换算 | 说明 |
26
26
  | --- | --- | --- |
27
- | `getGeoProjection()` | — | `{ rawBounds, bounds, aspectScale, center, scaleX, scaleY, project, unproject }`;`project(lng, lat)` / `unproject(x, y)` 就是「经纬度 ↔ 画出来的世界坐标」 |
28
- | `lngLatToWorld(lng, lat)` / `worldToLngLat(x, y)` | 经纬度 ↔ 世界坐标 | 已经含 `aspectScale`:`lngLatToWorld(rawBounds.minX, …)` 正好落在底图左边缘,与画出来的底图完全对齐 |
27
+ | `getGeoProjection()` | — | `{ rawBounds, bounds, aspectScale, center, scaleX, scaleY, project, unproject }`;`project(lng, lat)` / `unproject(x, y)` 是「经纬度 ↔ 最终画出来的世界坐标」,含 `aspectScale`、`geoJsonStyle.bounds` 与 `rotation` |
28
+ | `lngLatToWorld(lng, lat)` / `worldToLngLat(x, y)` | 经纬度 ↔ 世界坐标 | 与当前底图完全对齐:只改 `aspectScale` 时落点仍在底图边缘;再改 `bounds` / `rotation` 也会跟着移动、旋转 |
29
29
  | `lngLatToScreen(lng, lat)` / `screenToLngLat(x, y)` | 经纬度 ↔ 画布 CSS 像素 | 画布内的像素坐标(左上角为原点),与 `onPointer` / `getPointer()` 用的是同一套 |
30
+ | `mapDataToLngLat(data?, options?)` | 世界坐标 → GeoJSON 经纬度 | 按当前最终投影整份转换 `points` / `lines` / `channels`;不传 `data` 就转视图当前数据,返回复制的数据(原数据与视图不变),没有 GeoJSON 背景时为 `null` |
31
+ | `mapLngLatToData(data, options?)` | GeoJSON 经纬度 → 世界坐标 | `mapDataToLngLat()` 的逆变换:按调用当时最终画出来的底图投影写回 `points` / `lines` / `channels`(含线路端点 / 路径、固定坐标装饰、通道分组路径与 `waypointOffsets` 向量);返回副本、不改入参,没有 GeoJSON 背景时为 `null` |
30
32
 
31
33
  `getPointer()` 与 `onPointer` 回调共用同一份光标状态 `{ inside, screen, world, lngLat }`:`screen` 是画布 CSS 像素、`world` 是世界坐标、`lngLat` 是反算出来的经纬度(没有 GeoJSON 背景时为 `null`);鼠标移出画布时 `inside: false`,其余字段保留最后一次的值。各类指针载荷(`onHover` / `onClick` / `onDblClick` / `onPointerDown` / `onPointerUp` / `onContextMenu`)在配了 GeoJSON 背景时也会多带一个 `lngLat` 字段。
32
34
 
33
- 数据本身不在底图的坐标系里(例如底图是经纬度、数据是另一套投影坐标)时,用映射器把数据搬到画出来的底图上:
35
+ 数据本身不在底图的坐标系里(例如底图是经纬度、数据是另一套投影坐标)时,用映射器把数据搬到画出来的底图上。`lngLat` / `scale` 都以最终底图(含 `bounds` / `rotation`)为基准,只有 `fit` 把输入当作任意坐标系:
34
36
 
35
37
  ```js
36
38
  const mapper = view.createGeoMapper({
37
- mode: 'fit', // 'lngLat' 数据本身就是经纬度、与底图完全贴合 | 'fit' 等比铺进底图范围 | 'scale'(默认)按比例缩放
39
+ mode: 'fit', // 'lngLat' / 'scale' 以最终底图为基准 | 'fit' 任意坐标等比铺进底图范围
38
40
  scale: 1, // 「节点与 geojson 的大小比例」:1 是各模式自己的基准,大于 1 铺得更开
39
41
  padding: 0.08, // fit 模式四周留白(占底图短边比例,0 ~ 0.45)
40
42
  data // fit 模式用它的范围算比例;也可以显式给 bounds
@@ -44,15 +46,33 @@ const points = view.getData().points.map(mapper.mapPoint)
44
46
  const lines = view.getData().lines.map(mapper.mapLine)
45
47
  const mapped = view.mapDataToGeoJson({ points, lines, channels }, { mode: 'fit' })
46
48
  view.updateData(mapped.points, mapped.lines)
49
+
50
+ // 保存:背景编辑完成后,把网架当前世界坐标整份落成 GeoJSON 经纬度
51
+ const saved = view.mapDataToLngLat()
52
+
53
+ // 回灌:按调用当时最终画出来的底图投影写回世界坐标,再交给 updateData()
54
+ const restored = view.mapLngLatToData(saved)
55
+ view.updateData(restored)
47
56
  ```
48
57
 
49
- `mapPoint` / `mapLine` / `mapChannel` 之外还有 `map([x, y])` / `unmap` 与 `mapData`,`scaleX` / `scaleY` / `offsetX` / `offsetY` 是最终叠出来的比例与偏移。三种模式的语义:
58
+ `mapPoint` / `mapLine` / `mapChannel` 之外还有 `map([x, y])` / `unmap` 与 `mapData`;坐标同时认 `[x, y]` 与 `{ x, y }`。`mapLine` / `mapChannel` 会同时换 `path`、`start` 与 `end`,`mapPoint` / `mapLine` 还会换节点 / 线路装饰里的固定世界坐标(`at`、坐标型 `position`,屏幕像素 `offset` 不动);`mapChannel` 另换 `groups[].path`,并把 `groups[].waypointOffsets` 当向量换算,只转方向和尺度、不叠平移。`fit` 的数据范围从节点、线路几何、通道 / 分组路径与这些固定坐标装饰一起算。`scaleX` / `scaleY` / `offsetX` / `offsetY` 是最终叠出来的比例与偏移。四种模式的语义:
50
59
 
51
- - `'lngLat'`:数据本身就是经纬度 —— 套上与底图完全相同的 `aspectScale`(只压经度、绕底图中心),`scale: 1` 时数据与底图严丝合缝,`scale` 大于 1 时数据比底图铺得更开(节点相对底图显得更大);
60
+ - `'lngLat'`:数据本身就是经纬度 —— 直接复用底图的最终投影(含 `aspectScale`、`bounds`、`rotation`),`scale: 1` 时数据与底图严丝合缝,`scale` 大于 1 时数据比底图铺得更开(节点相对底图显得更大);
52
61
  - `'fit'`:数据是任意坐标系 —— 等比铺进底图范围(contain + `padding`),长宽比不会被拉变形,`scale` 是铺好之后再整体缩放的倍数;
53
- - `'scale'`(默认):围绕 `center`(缺省底图中心)按 `scale` / `scaleX` / `scaleY` 缩放,再叠 `offsetX` / `offsetY` —— 数据本来就是经纬度时 `1` 就是与底图重合的那一档,它就是「节点与 geojson 的大小比例」这个属性;数据是别的坐标系时按自己需要的比例给。
62
+ - `'world'`:输入已经是画出来的世界坐标 —— 直接调用最终投影的 `unproject` 反算真实经纬度,`mapDataToLngLat()` 内部固定用这一档;背景移动、缩放、旋转后按调用当时的最新投影计算;
63
+ - `'scale'`(默认):先套底图的最终投影,再围绕 `center`(缺省底图中心)按 `scale` / `scaleX` / `scaleY` 缩放,最后叠 `offsetX` / `offsetY` —— 数据本来就是经纬度时 `1` 就是与底图重合的那一档,它就是「节点与 geojson 的大小比例」这个属性;数据是别的坐标系时用 `fit` 或按自己需要的比例给。
64
+
65
+ 映射只换世界坐标、不改数据的属性:通道两端锚的还是同一批节点 id,映射之后依旧对得上;屏幕像素偏移、节点尺寸与线宽保持原值(`pointSizeScale` / `pointZoomScale` 照旧生效),所以「数据铺多大」与「节点画多大」可以分开调。
66
+
67
+ `mapDataToLngLat()` 与 `mapLngLatToData()` 都只返回转换后的副本,**不会自动 `updateData()`**。保存后要原样恢复,必须走逆映射:
68
+
69
+ ```js
70
+ const saved = view.mapDataToLngLat()
71
+ view.updateData({ points: [], lines: [], channels: [] })
72
+ view.updateData(view.mapLngLatToData(saved))
73
+ ```
54
74
 
55
- 映射只换坐标、不改数据的属性:通道两端锚的还是同一批节点 id,映射之后依旧对得上;节点尺寸与线宽仍然是 CSS 像素(`pointSizeScale` / `pointZoomScale` 照旧生效),所以「数据铺多大」与「节点画多大」可以分开调。
75
+ 不要把 `mapDataToLngLat()` 的结果直接 `updateData()`:底图仍是同一个 GeoJSON 时,组件会把 `x` / `y` 当成世界坐标再投影一次,画面位置会偏移。`mapLngLatToData()` 按调用当时的最终底图投影写回,因此底图 `bounds` / `rotation` 没变时世界坐标与屏幕落点原样恢复;底图改过时,同一份经纬度会落到新底图上。`docs/demos/BackgroundDemo.vue` 的「经纬度存档回灌」直接演示了「保存 → 清空 → 回灌」。
56
76
 
57
77
  ## 数据字段
58
78
 
@@ -109,15 +129,15 @@ createGisView(container, { points, lines, channels })
109
129
  | `opacity` | `number` | 透明度 0 ~ 1 |
110
130
  | `shadow` | `GisViewShadow` | 阴影:`{ color, opacity, offsetX, offsetY, blur }`(单位都是 CSS 像素) |
111
131
  | `visible` | `boolean` | `false` 时不画;被它锚定的通道与回线也一起消失 |
112
- | `clickable` | `boolean` | 覆盖 `options.clickable` |
113
- | `draggable` | `boolean` | 覆盖 `options.draggable` |
132
+ | `clickable` | `boolean` | 逐点覆盖点击权限;先打开 `features: { hover: true, select: true }` |
133
+ | `draggable` | `boolean` | `features.drag` 打开时,逐点写 `false` 可关掉这个节点的拖拽(不能反过来把总闸打开) |
114
134
  | `cursor` / `ctrlCursor` | `GisViewCursorValue` | 悬停到它身上时的光标(Ctrl 变体单独一份),优先于全局配置 |
115
135
  | `labelVisible` | `boolean` | 这个点的标签是否显示 |
116
136
  | `labelVisibility` | `'auto' \| 'always' \| 'hidden'` | 标签显隐策略,覆盖 `label.visibility` |
117
137
  | `labelPosition` | `GisViewLabelPosition` | 标签贴在节点的哪一侧 |
118
138
  | `labelOffset` | `[number, number]` | 标签的额外像素偏移 |
119
139
  | `labels` | `GisViewItemLabel \| GisViewItemLabel[]` | 这个点上的标签清单(逐条写:`id` / `content` / `position` / `offset` / `style` / `hidden`…),与 `options.label` 里同 id 的那一条合并;导出数据的 `labels` 列就是它,见「标签(labels,导入即回显)」 |
120
- | `decorations` | `GisViewPointDecoration[]` | 节点上挂的图片 / SVG:可写屏幕像素偏移(`offset` 或 `distance` + `direction`)、固定世界坐标(`anchor: 'fixed'` / `at`),并可用 `connector` 从节点中心画连接线,见「节点上的装饰」 |
140
+ | `decorations` | `GisViewPointDecoration[]` | 节点上挂的图片 / SVG:可写屏幕像素偏移(`offset` 或 `distance` + `direction`)、固定世界坐标(`anchor: 'fixed'` / `at`),并可用 `connector` 从节点中心画连接线;逐条 `followSelectColor` 可让装饰跟随节点选中色(SVG path 写描边,其他符号写填充 / 色调),见「节点上的装饰」 |
121
141
  | `tooltip` | `GisViewTooltipContent \| false` | 这个点的提示内容,`false` 表示不显示 |
122
142
  | `hoverStyle` / `selectStyle` / `multiSelectStyle` / `boxSelectStyle` | `GisViewPointStyle` | 这个点自己的状态样式(按字段覆盖自身样式) |
123
143
 
@@ -136,14 +156,14 @@ createGisView(container, { points, lines, channels })
136
156
  | `dashOffset` | `number` | 虚线图案的相位偏移(CSS 像素,可以为负) |
137
157
  | `shadow` | `GisViewShadow` | 线阴影(按偏移逐实例烘焙) |
138
158
  | `visible` | `boolean` | `false` 时不画 |
139
- | `decorations` | `GisViewLineDecoration[]` | 沿线挂的图片 / SVG,见「线路上的装饰」 |
159
+ | `decorations` | `GisViewLineDecoration[]` | 沿线挂的图片 / SVG;逐条 `followSelectColor` 可让装饰跟随线路选中色(SVG path 写描边,其他符号写填充 / 色调),见「线路上的装饰」 |
140
160
  | `direction` | `GisViewLineDirectionInput` | 这条线自己的方向箭头(静态,不跑动):`true` 用 `options.lineDirection`、对象逐字段覆盖、`false` / `null` 关掉;按弧长摆、朝向跟着切向(起点 → 终点),见「线路方向箭头(lineDirection)」 |
141
161
  | `forward` | `boolean` | 业务正向,默认 `true`:从 `from` / `path[0]` 走向 `to` / `path[last]`。写 `false` 时装饰位置与朝向、方向箭头、线路动画都按反方向解释;它与 `effect.reverse` / `direction.reverse` 是异或关系(反两次等于正一次),不改写 `path` 本身 |
142
162
  | `lineLabel` / `lineLabelPosition` / `lineLabelVisibility` / `lineLabelOffset` / `lineLabelParallel` | | 这条线自己的标签配置(`lineLabelParallel` 是这条线自己的「与线路平行」开关) |
143
163
  | `labels` | `GisViewItemLabel \| GisViewItemLabel[]` | 这条线上的标签清单(逐条写,与节点的 `labels` 同一套,额外认 `parallel`),见「标签(labels,导入即回显)」 |
144
164
  | `tooltip` | `GisViewTooltipContent \| false` | 这条线的提示内容 |
145
- | `clickable` | `boolean` | 覆盖 `options.clickable` |
146
- | `waypointEditable` | `boolean` | 覆盖 `options.waypointEditable` |
165
+ | `clickable` | `boolean` | 逐线覆盖点击权限;先打开 `features: { hover: true, select: true }` |
166
+ | `waypointEditable` | `boolean` | 逐线覆盖拐点编辑;先打开 `features: { waypoint: true }` |
147
167
  | `cursor` / `ctrlCursor` | `GisViewCursorValue` | 悬停到这条线上时的光标(通道回线按通道 / 回线自身取) |
148
168
  | `hoverStyle` / `selectStyle` / `multiSelectStyle` | `GisViewLineStyle` | 这条线自己的状态样式 |
149
169
 
@@ -12,14 +12,14 @@
12
12
 
13
13
  | 字段 | 类型 | 默认值 | 说明 |
14
14
  | --- | --- | --- | --- |
15
- | `features` | `GisViewFeaturesInput` | 无(**缺省全关**) | 功能开关:预设名(`'edit'` 全开 / `'view'` 打开查看类 / `'static'` 与缺省一致(全关))、开关表或布尔值。**什么都不写时只剩画布与数据**:没写的开关跟随各自的详细配置(`interactive` / `clickable` / `draggable` / `boxSelect` / `copy.enabled`…),详细配置也没写就是关(提示 / 标签 / 鹰眼图配了才有),写了 `true` / `false` 就是强制打开 / 关闭;想一次打开一批就用 `'view'`(能平移缩放、能悬停选中看提示但改不动数据)或 `'edit'`(等价 `features: true`)。运行时用 `setFeatures()` 换、`getFeatures()` 读,见「功能开关」 |
15
+ | `features` | `GisViewFeaturesInput` | 无(**缺省全关**) | 功能开关:**对象**(`{ 开关名: boolean }`,不是数组)、预设名(`'edit'` 全开 / `'view'` 打开查看类 / `'static'` 与缺省一致(全关))或布尔值。**交互权限只有这一个入口**:`pan` / `zoom` / `hover` / `select` / `multiSelect` / `toggleSelect` / `boxSelect` / `drag` / `waypoint` / `contextMenu` / `channelDraft` / `keyboard` 没写就是关;旧的顶层 `draggable` / `interactive` / `clickable` / `boxSelect` / `multiSelect` / `toggleSelect` / `waypointEditable` 已移除,传入会经 `onWarning` 提示迁移并忽略。`copy` / `merge` / `remove` 仍保留详细配置(写了即开,可用 features 强制开关),标签 / 提示 / 鹰眼图仍是「配了才有」。对象里允许写 `draggable` / `waypointEditable` / `interactive` / `clickable` 作为别名;同一张表同时写别名与规范名时规范名优先,`getFeatures()` 只返回 19 个规范键。想一次打开一批就用 `'view'`(能平移缩放、能悬停选中看提示但改不动数据)或 `'edit'`(等价 `features: true`)。运行时用 `setFeatures()` 换、`getFeatures()` 读,见「功能开关」 |
16
16
  | `history` | `GisViewHistoryOptions` | 关 | 历史(撤销 / 重做):`true` 用缺省上限 50 条,`{ limit }` 自定义(`0` 表示不限),不配就是关。打开之后 Ctrl+Z 撤销、Ctrl+Shift+Z / Ctrl+Y 重做,接口是 `undo()` / `redo()` / `canUndo()` / `canRedo()`,见「历史」 |
17
17
  | `quality` | `GisViewQualityOptions \| GisViewQualityMode \| boolean` | `'high'` | 画质档位(自动档按渲染耗时与整帧间隔两路信号升降):`'high'` 全开 / `'balanced'` 关光晕 / `'lean'` 连装饰一起关 / `'fast'` 再降到 0.75 倍渲染分辨率,`'auto'`(也可以直接写 `true`)按这两路信号自己升降,对象写法 `{ mode, budgetMs, minScale }` 能换预算与分辨率下限。只改「画哪些批次 + 多大分辨率」,不重排场景、不动数据,见「画质」 |
18
18
  | `denseMode` | `GisViewDenseModeInput` | `'auto'` | 大数据展示模式:`'auto'` 按可见节点数、屏幕密度与数据总量自动进入密集档,`'on'` / `'off'`(或 `true` / `false`)强制开关;对象写法可换 `visibleThreshold` / `densityThreshold` / `pointThreshold` 三档阈值。进入后标签自动走避让 / 截断,线路动点与节点涟漪暂停;读数看 `getStats().dense`,见「大数据展示模式(denseMode)」 |
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
+ | `pointCluster` | `boolean \| GisViewPointClusterOptions` | 关 | 点聚合:`true` 使用缺省档(屏幕半径 60px / 至少 2 点 / 不限缩放),对象写法可调 `enabled` / `radius` / `minPoints` / `maxZoom`,并支持填充色 / 描边 / 透明度 / 数量文字 / `circle`、`square`、SVG、图片或自定义图元图标 / `sizeScale`。聚合圆替代密集原始点参与绘制、拾取、快照与导出;原始 `points` / `getData()` 不变,缩放超过 `maxZoom` 后恢复逐点显示,见「点聚合(pointCluster)」 |
23
23
  | `channelWidth` | `number` | `0.02` | 通道宽度的世界单位写法(`tangent` 模式下是最外侧两条回线中心的间距,`inset` 模式下先按它算再收),**只在 `nodeWidth` 为 0 或两端没绑节点时才真正决定宽度**;单个通道可用 `width` 覆盖 |
24
24
  | `channelInset` | `number` | `0` | `offsetMode: 'inset'` 时从切点收 / 放的比例(占通道宽度):正数内收(最多 0.42),**负数外收**(最多 -0.5,可用区间撑到 2 倍宽);单个通道可用 `inset` 覆盖,见「通道与多回线」 |
25
25
  | `channelNodeWidth` | `number` | `1` | 节点宽度倍数:通道宽度按两端节点尺寸取的倍数,`1` 表示回线正好铺在节点两边、节点放大回线跟着变宽,`0` 表示所有通道退回 `width` / `channelWidth` 的世界单位写法,单个通道可用 `nodeWidth` 覆盖 |
@@ -57,18 +57,14 @@
57
57
  | `defaultHighlight` | `boolean` | `false` | 没有配置状态样式时是否仍用尺寸 / 宽度增幅与强调环画出内置强调;默认 `false`(不配样式就不改变外观),设为 `true` 可退回旧行为 |
58
58
  | `pointSizeScale` / `lineWidthScale`、`pointZoomScale` / `lineZoomScale`、`zoomBaseScale` | `number` | `1` / `0` | 两组系数各管一件事,不重复:`pointSizeScale` / `lineWidthScale` 是与地图无关的整体放大(常数倍数,屏幕上的大小因此恒定),`pointZoomScale` / `lineZoomScale` 是「跟随地图缩放」的指数(0 表示不跟随,1 表示与地图同倍率放大,0.5 是地图放大 4 倍、要素放大 2 倍),绘制尺寸是两者相乘;`zoomBaseScale` 是联动基准,缺省取首次适应视图时的比例 |
59
59
  | `fitSpacing` | `boolean \| GisViewFitSpacingOptions` | 关 | 适应视图的节点间距兜底:`true` 把最近节点中心距精确设为 `24` CSS 像素;对象在 `minDistance`(精确屏距,可放大也可缩小)/ `maxDistance`(最远距离上限,只压低比例)中二选一,同时给出时只启用 `maxDistance`;`maxZoom` 仅为旧配置兼容保留,不再限制距离;只在首次默认适应与 `fitView()` 生效,远处孤立点允许落到视口外,见「适应视图的节点间距(fitSpacing)」 |
60
- | `boxSelect` | `boolean` | `false` | 是否允许按住框选快捷键(`shortcuts.boxSelect`,默认 Shift)拖动鼠标框选要素;**缺省关**,写 `boxSelect: true` 或 `features: { boxSelect: true }` 才开。框选默认只收节点,`selectionKinds` 可把线路 / 通道也纳入 |
61
- | `multiSelect` | `boolean` | `false` | 多选开关:打开后不用按修饰键,直接单击节点 / 线路就取反累加选中(与按住 `shortcuts.multiSelect` 点击同一套行为、同一个 `multiSelectStyle`;取反那一下不经过 `onClick`,`onSelectChange.action.type` 是 `toggleSelect`);**缺省关**,关着时多选走框选(`boxSelect`)与按住多选快捷键(默认 Ctrl / ⌘)。运行时 `updateOptions({ multiSelect })` 切换只重画一帧、不重排场景 |
62
- | `toggleSelect` | `boolean` | `false` | 再次选中即取消:打开后普通单击已选中的节点 / 线路 / 通道会取消,`selectTrigger: 'dblClick'` 下再次双击取消,Ctrl / ⌘ 先选中后再普通单击也取消;取反时 `onClick.action.type` 与 `onSelectChange.action.type` 都是 `toggleSelect`。缺省关,运行时 `updateOptions({ toggleSelect })` 只重画一帧、不重排场景,见「选中与拐点」 |
63
60
  | `selectTrigger` | `'click' \| 'dblClick'` | `'click'` | 选中触发方式:`click` 第一次单击就改选中集;`dblClick` 第一下只派发 `onClick`,等双击判定命中的第二下才改选中集,并同时派发 `onClick` 与 `onDblClick`。Ctrl / ⌘ 多选与 `multiSelect` 在双击模式下同样等第二下才取反。运行时走 `updateOptions({ selectTrigger })`,只重画一帧、不重排场景 |
64
61
  | `selectionKinds` | `GisViewSelectionKindsInput` | 无(框选只收节点;Ctrl 多选收节点 / 线路 / 通道) | 框选与 Ctrl / 多选点击的类型过滤:`'point'` 节点、`'line'` 通道外的独立线路、`'channel'` 通道回线;另有 `'lineEndpoints'` / `'channelEndpoints'`,只在框选时要求线路 / 通道两端节点都在框内。可写数组任意组合,`'all'` / `true` 全开,`false` / `null` 全关。通道回线只归 `channel` 管,通道外的线路只归 `line` 管;通道回线仍按 `channelSelectMode` 摊开。不写时保留旧口径;普通单选与 `select` / `selectFeatures` 不受它限制。运行时走 `updateOptions({ selectionKinds })`,见「框选」与「选择类型过滤」 |
65
62
  | `selectionBoxStyle` | `GisViewSelectionBoxStyle` | 蓝底 0.16 透明 + 1px 蓝框 | 框选矩形的样式:`fill` / `fillOpacity` / `stroke` / `strokeWidth`,以及 DOM 渲染用的 `render: 'div'` / `className` / `borderStyle` / `radius`;canvas 模式不画线型与圆角,见「框选」 |
66
- | `selectionBoxPersistent` | `boolean` | `false` | 框选结束后是否保留选框:打开后拖动保留框会整组平移框内选中节点,线路与通道跟随;选中集变化、清空、关闭开关或调用 `clearSelectionBox()` 时收起。要用它同时打开 `boxSelect` 和 `draggable`,见「框选」的「保留框与整组拖动」 |
67
- | `selectionOnTop` | `boolean` | `true` | 选中的节点 / 拖动中的整组 / 待确认合并的目标节点,以及选中线路所在的整条通道,是否画在所有要素之上(与 `zIndex` 无关),见「选中 / 拖动的层级」 |
63
+ | `selectionBoxPersistent` | `boolean` | `false` | 框选结束后是否保留选框:打开后拖动保留框会整组平移框内选中节点,线路与通道跟随;选中集变化、清空、关闭开关或调用 `clearSelectionBox()` 时收起。要用它同时打开 `features: { boxSelect: true, drag: true }`,见「框选」的「保留框与整组拖动」 |
64
+ | `selectionOnTop` | `boolean` | `true` | 选中的节点 / 拖动中的整组 / 待确认合并的目标节点,以及选中线路所在的整条通道,是否连同各自的 `decorations` 一起画在所有要素之上(与 `zIndex` 无关),见「选中 / 拖动的层级」 |
68
65
  | `channelSelectMode` | `'line' \| 'group' \| 'channel'` | `'line'` | 通道选中模式:点中通道里的一条回线时选中什么 —— `'line'` 只选这一条、`'group'` 连它所在的分组一起、`'channel'` 整条通道(所有分组的回线);通道以外的线路永远只选自己。普通点选与 Ctrl / ⌘ + 点击加点共用这一档高亮范围;Ctrl / ⌘ + 点击在 `group` / `channel` 下按整批取反;切换走 `updateOptions({ channelSelectMode })`,只重画一帧、不重排场景,见「通道选中模式」 |
69
66
  | `channelWaypointDragMode` | `'parallel' \| 'vertex'` | `'parallel'` | 分组拐点拖动模式:`'parallel'` 沿用共用骨架 + 平行偏移(和原有逻辑一致),`'vertex'` 只平移当前分组同一 `pathIndex` 的拐点,其他拐点与分组不动;普通线路始终只动自己。`vertex` 的结果写进 `groups[].waypointOffsets`,切换走 `updateOptions({ channelWaypointDragMode })`,下一次拖动生效,见「分组拐点拖动模式」 |
70
67
  | `channelHiddenLineLayout` | `'preserve' \| 'center'` | `'preserve'` | 隐藏部分回线 / 分组后车道怎么排:`'preserve'` 保留原车道槽位,剩余回线不挪位;`'center'` 只把当前可见回线按原顺序压成连续槽位,再在通道宽度内重新居中。运行时走 `updateOptions({ channelHiddenLineLayout })`,见「隐藏回线后的车道」 |
71
- | `waypointEditable` | `boolean` | `false` | 是否允许编辑线路拐点(按住加点快捷键 `shortcuts.addWaypoint`(默认 Ctrl / ⌘)+ 点击添加、拖动移动、双击或删除快捷键删除),单条线可用 `waypointEditable` 覆盖;**缺省关**,写 `waypointEditable: true` 或 `features: { waypoint: true }` 才开。`false`(以及 `features: { waypoint: false }`)会同时关闭新增、移动与删除,没有只关其中一项的细分开关;`shortcuts.addWaypoint` 仍只决定加点的组合键 |
72
68
  | `waypointVisibility` | `'selected' \| 'always'` | `'selected'` | 拐点显示时机:`selected` 只显示选中线路(以及刚加过拐点的线路)的拐点,`always` 全部显示 |
73
69
  | `waypointStyle` / `waypointSelectStyle` | `GisViewPointStyle` | 无 | 拐点(线路 `path` 的中间顶点)的默认 / 选中样式,**支持配置**:字段与节点样式同款;缺省是白底蓝圈的 9px 圆点,`waypointSelectStyle` 没配时选中拐点用内置强调环,见「线路拐点」的「拐点样式」小节 |
74
70
  | `cursorStyle` | `GisViewCursorStyle` | 见「光标」 | 鼠标光标按分类配置:`idle` / `pan` / `box` / `drag` / `draft` / `point` / `line` / `channel` / `waypoint` 九类,每类可以给系统关键字、CSS 变量或图片(`{ url, hotspot, fallback }`),`ctrl` 里再配按住 Ctrl / ⌘ 时的替代值;节点 / 线路 / 通道还能用 `cursor` / `ctrlCursor` 逐要素覆盖,详见「光标」 |
@@ -78,10 +74,7 @@
78
74
  | `networkVisible` | `boolean` | `true` | 网架整体显隐:`false` 时节点、线路、通道、装饰、动画、标签与框选框一起不画,只保留背景、网格与自定义 WebGL 层;数据、选中与历史都不动,拾取 / 框选 / `queryRect` 也不再命中网架,适合先铺网架再添加背景 |
79
75
  | `networkOpacity` | `number` | `1` | 网架整体不透明度(夹在 0 ~ 1):在每类要素自身 `opacity` 之后再乘一层,节点、线路、通道、装饰、动画与标签一起变淡,背景不受影响。`0` 只是画面上看不见,拾取仍按网架存在;要完全收起请用 `networkVisible: false` |
80
76
  | `backgroundEditing` | `boolean` | `false` | 背景编辑模式:`true` 时节点、线路、通道的悬停、点击、选中、拖拽、拐点编辑、框选、右键与数据快捷键全部停用,仍保留滚轮缩放与 `fitView`,方便让网架和背景一起缩放后只移动 / 旋转 / 缩放背景;缺省 `false`,可用 `updateOptions()` 随时开关 |
81
- | `interactive` | `boolean` | (缺省全关) | 查看类交互的「详细配置手动打开」写法:写 `true` 打开拖拽平移 / 滚轮缩放 / 右键回调(等价 `pan` / `zoom` / `contextMenu` 三个开关),不写就是全关 |
82
- | `preventContextMenu` | `boolean` | `true` | 组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):`contextMenu` 开着(或 `interactive: true` / `features: 'view'`)时组件才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` 照常发)。右键整个不接时(`contextMenu` 缺省关)系统菜单自然照弹 |
83
- | `clickable` | `boolean` | (缺省全关) | 查看类交互的「详细配置手动打开」写法:写 `true` 打开悬停高亮与点击选中(等价 `hover` / `select` 两个开关),要素自身可用 `clickable: false` 逐个关掉 |
84
- | `draggable` | `boolean` | `false` | 点是否可拖拽,单个点可用 `draggable` 覆盖;**缺省关**,写 `draggable: true` 或 `features: { drag: true }` 才开 |
77
+ | `preventContextMenu` | `boolean` | `true` | 组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):只有 `features: { contextMenu: true }`(或 `features: 'view'`)时才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` 照常发)。右键整个不接时系统菜单自然照弹 |
85
78
  | `liveDrag` | `boolean` | `true` | 拖动节点时线路是否实时跟随:`false` 时拖动期间线路与通道原地不动,松手(`pointerup`)后一次性更新到新位置 |
86
79
  | `lineFollowsNode` | `boolean` | `true` | 线路 / 通道是否跟随节点的生命周期:`true` 时隐藏、删除、筛选收起节点会连同绑定线路与通道一起处理;`false` 时节点退场只影响节点,线路 / 通道停在最后一份坐标与解析尺寸。筛选没有显式写 `cascade` 时,关闭它会把默认联动从 `'binding'` 改为 `false`;显式 `cascade` 始终优先。运行时用 `updateOptions({ lineFollowsNode })` 切换,见「筛选」 |
87
80
  | `shortcuts` | `GisViewShortcuts` | 见「快捷键」 | 所有快捷键都可自定义:框选 / 多选 / 加点 / 复制 / 粘贴 / 取消 / 删除 / 适应视图,没写的动作沿用默认值,写 `false` 等于关掉这个动作的快捷键 |
@@ -122,7 +115,7 @@
122
115
  | 通道几何 | `channelWidth` · `channelInset` · `channelNodeWidth` · `channelTangentInset` · `channelMaxGroups` · `channelGroupMaxLines` · `channelSimplify` · `channelDraftStyle` · `channelSelectMode` · `channelWaypointDragMode` · `channelHiddenLineLayout` |
123
116
  | 样式与规则 | `pointStyle` · `lineStyle` · `pointRules` / `lineRules` · `heatmap` · `effects` · `lineDirection` · `prebake` · `label` · `labelOptimize` · `labelRender` · `lineLabel` · `labelStates` · `tooltip` · `pointSizeScale` / `lineWidthScale`、`pointZoomScale` / `lineZoomScale`、`zoomBaseScale` · `cursorStyle` · `gridColor` · `gridSize` · `showGrid` · `networkVisible` · `networkOpacity` |
124
117
  | 状态样式与选中 | `pointHoverStyle` / `pointSelectStyle` · `lineHoverStyle` / `lineSelectStyle` · `pointMultiSelectStyle` / `pointBoxSelectStyle` · `lineMultiSelectStyle` · `defaultHighlight` · `selectionBoxStyle` · `selectionBoxPersistent` · `selectionOnTop` · `waypointStyle` / `waypointSelectStyle` |
125
- | 交互开关 | `boxSelect` · `multiSelect` · `toggleSelect` · `selectTrigger` · `waypointEditable` · `waypointVisibility` · `interactive` · `preventContextMenu` · `clickable` · `draggable` · `liveDrag` · `lineFollowsNode` · `shortcuts` · `copy` · `remove` · `merge` |
118
+ | 交互行为 | `selectTrigger` · `selectionKinds` · `waypointVisibility` · `preventContextMenu` · `liveDrag` · `lineFollowsNode` · `shortcuts` · `copy` · `remove` · `merge`(权限开关统一写 `features`) |
126
119
  | 视图与相机 | `minScale` / `maxScale` · `fitPadding` · `fitOnInit` · `center` / `scale` · `initialView` · `zoomStep` |
127
120
  | 画质与渲染 | `quality` · `width` / `height` · `pixelRatio` · `maxCanvasPixels` · `antialias` · `interactionThinLines` · `interactionNoMsaa` · `renderer` · `webglLayers` |
128
121
  | 大数据(筛选 / 搜索 / 读数) | `denseMode` · `filter` · `filterWorker` · `searchWorker` · `progressiveInit` · `statsInterval` · `idleTimeout` · `renderEmitInterval` |
@@ -134,13 +127,14 @@
134
127
 
135
128
  - **类型**:`GisViewFeaturesInput`
136
129
  - **默认值**:无(**缺省全关**)
137
- - **说明**:功能开关:预设名(`'edit'` 全开 / `'view'` 打开查看类 / `'static'` 与缺省一致(全关))、开关表或布尔值。**什么都不写时只剩画布与数据**:没写的开关跟随各自的详细配置(`interactive` / `clickable` / `draggable` / `boxSelect` / `copy.enabled`…),详细配置也没写就是关(提示 / 标签 / 鹰眼图配了才有),写了 `true` / `false` 就是强制打开 / 关闭;想一次打开一批就用 `'view'`(能平移缩放、能悬停选中看提示但改不动数据)或 `'edit'`(等价 `features: true`)。运行时用 `setFeatures()` 换、`getFeatures()` 读,见「功能开关」
130
+ - **说明**:功能开关:**对象**(`{ 开关名: boolean }`,不是数组)、预设名(`'edit'` 全开 / `'view'` 打开查看类 / `'static'` 与缺省一致(全关))或布尔值。**交互权限只有这一个入口**:平移 / 缩放 / 悬停 / 选中 / 多选 / 取反 / 框选 / 拖拽 / 拐点 / 右键回调 / 通道草稿 / 键盘都没有顶层开关;旧的顶层 `draggable` / `interactive` / `clickable` / `boxSelect` / `multiSelect` / `toggleSelect` / `waypointEditable` 传入只会收到迁移提示并被忽略,也不会留在 `getOptions()`。`copy` / `merge` / `remove` 仍以详细配置是否写了为缺省,`features` 可强制开关;标签 / 提示 / 鹰眼图仍是「配了才有」。对象里允许用详细配置名作别名:`draggable` -> `drag`、`waypointEditable` -> `waypoint`、`interactive` -> `pan` / `zoom` / `contextMenu`、`clickable` -> `hover` / `select`;同一张表同时写别名与规范名时规范名优先,`getFeatures()` 只返回 `FEATURE_NAMES` 里的 19 个规范键,不返回别名。数组写法会发 `warning` 并忽略。想一次打开一批就用 `'view'`(能平移缩放、能悬停选中看提示但改不动数据)或 `'edit'`(等价 `features: true`)。运行时用 `setFeatures()` 换、`getFeatures()` 读,见「功能开关」
138
131
  - **参考**:见「功能开关」一节,子字段在那里的表里逐个展开
139
132
  - **示例**:
140
133
 
141
134
  ```js
142
135
  const view = createGisView(container, { features: 'edit' })
143
136
  view.setFeatures({ hover: true, select: true }) // 运行时再换一批
137
+ view.setFeatures({ draggable: true, multiSelect: true }) // 别名同样可用
144
138
  view.getFeatures() // 读当前那一份
145
139
  ```
146
140
 
@@ -211,18 +205,36 @@ const channels = [{
211
205
 
212
206
  - **类型**:`boolean | GisViewPointClusterOptions`
213
207
  - **默认值**:关
214
- - **说明**:点聚合。`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`
208
+ - **说明**:点聚合。`true` 等价于使用全部缺省值:`radius: 60`、`minPoints: 2`、`maxZoom: 0`、`color: '#2970db'`、`strokeColor: '#ffffff'`、`strokeWidth: 2`、`opacity: 1`、`textColor: '#ffffff'`、`fontSize: 0`(按半径自动取 12 / 13px)、`symbol: 'circle'`、`showCount: true`、`sizeScale: 1`。对象写法里的 `enabled` 缺省 `true`;`radius` 是屏幕 CSS 像素半径(夹在 8 ~ 500),`minPoints` 最少 2 点,`maxZoom` 是相机比例相对 `zoomBaseScale` / 首次适应视图比例的倍数,超过后展开原始点(`0` 表示不限)。`color` / `strokeColor` / `textColor` 使用 CSS 颜色;`strokeWidth` 夹在 0 ~ 20,`opacity` 夹在 0 ~ 1,`fontSize` 夹在 0 ~ 64,`sizeScale` 夹在 0.25 ~ 4 且同时缩放展示半径与拾取半径。`symbol` 接受 `circle` / `square`、SVG、图片地址或注册的自定义图元;SVG 的 `currentColor` 取 `color`,图标自己写了 `strokeColor` 时以图标为准。`showCount: false` 不生成数量文字 DOM,SVG 导出也不输出数量文字。聚合用固定网格建组,单次按数据量 O(n) 扫描;平移复用当前成员,只在缩放、配置、数据、选中、拖动、合并或复制预览变化时重算。聚合期间发散初始化和追加式补点暂停;选中、拖动、待确认合并与复制预览中的点强制保持单成员。它只改展示与命中结果:`getData()` 仍是原始数据,`listPoints({ visibleOnly: false })` 仍能列全,默认 `visibleOnly: true` 会跳过被聚合圆收起的成员;`getClusters()` 含单成员展示组与多成员圆的样式读数,`getPointCluster()` 返回包含上述样式的归一化配置与当前 `active`,统计在 `getStats().clusterCount` / `clusterPointCount` / `clusterMs`。点击聚合圆只发 `clusterClick` / `onClusterClick`,不选中内部节点。推荐与 `denseMode` 配合使用;需要看真实符号时用 `maxZoom`,或直接把 `pointCluster` 设为 `false`。演示见 `?demo=cluster`
215
209
  - **示例**:
216
210
 
217
211
  ```js
218
212
  const view = createGisView(container, {
219
213
  points,
220
- pointCluster: { radius: 64, minPoints: 3, maxZoom: 2.5 }
214
+ pointCluster: {
215
+ radius: 64,
216
+ minPoints: 3,
217
+ maxZoom: 2.5,
218
+ color: '#f05a3c',
219
+ strokeColor: '#1b3358',
220
+ strokeWidth: 3,
221
+ opacity: 0.85,
222
+ textColor: '#ffe08a',
223
+ fontSize: 16,
224
+ symbol: {
225
+ type: 'svg',
226
+ path: 'M0 -11 L3.2 -3.4 L11.5 -3.4 L4.9 1.8 L7.3 9.6 L0 5 L-7.3 9.6 L-4.9 1.8 L-11.5 -3.4 L-3.2 -3.4 Z',
227
+ viewBox: [-12, -12, 24, 24],
228
+ fill: 'currentColor'
229
+ },
230
+ showCount: true,
231
+ sizeScale: 1.2
232
+ }
221
233
  })
222
234
 
223
235
  view.updateOptions({ pointCluster: false }) // 关掉,原始点全部恢复
224
236
  const groups = view.getClusters() // 含 count: 1 的单成员
225
- const { active, radius } = view.getPointCluster() // 当前缩放档是否仍在聚合
237
+ const { active, radius, color, symbol } = view.getPointCluster() // 配置与当前是否仍在聚合
226
238
  ```
227
239
 
228
240
  #### `background`
@@ -682,7 +694,7 @@ createGisView(container, {
682
694
  | `enabled` | `boolean` | `true` | 开关;写对象且 `enabled: false` 等于关闭 |
683
695
  | `scope` | `'points' \| 'both'` | `'both'` | `points` 只亮节点;`both` 同时亮直连线路(通道回线也按线路处理) |
684
696
  | `triggerOnClick` | `boolean` | `true` | 普通单击是否触发 / 更新级联。设 `false` 时单击只走选中,并收掉旧级联 |
685
- | `triggerOnMultiSelect` | `boolean` | `true` | Ctrl / ⌘ 累加点击、以及 `options.multiSelect` 的累加单击是否触发级联;设 `false` 时多选只改选中集 |
697
+ | `triggerOnMultiSelect` | `boolean` | `true` | Ctrl / ⌘ 累加点击、以及 `features: { multiSelect: true }` 的累加单击是否触发级联;设 `false` 时多选只改选中集 |
686
698
  | `triggerOnBoxSelect` | `boolean` | `false` | 框选 / `selectInBox()` 是否触发级联。打开后框内所有命中项一起算邻接并集,直接命中的项仍走框选样式 |
687
699
  | `pointStyle` | `GisViewPointStyle` | 琥珀色描边、`sizeScale: 1.2` | 级联节点样式,整份替换;字段同 `pointStyle` |
688
700
  | `lineStyle` | `GisViewLineStyle` | 琥珀色、`width: 2.6`、`opacity: 0.95` | 级联线路样式,整份替换;字段同 `lineStyle` |
@@ -771,15 +783,14 @@ view.updateOptions({
771
783
 
772
784
  - **类型**:`boolean`
773
785
  - **默认值**:`false`
774
- - **说明**:框选结束后是否保留选框。打开后,非空框选会留下矩形;在框内按下并拖动一个可拖动的已选节点,会把框内选中的全部节点按同一世界坐标增量平移,绑定线路、通道骨架与回线一起跟随,框本身按屏幕增量同步移动。拖动整框不参与节点合并;在框内按下但不拖动也不会把选中集清掉。需要同时打开 `boxSelect` 与 `draggable`
786
+ - **说明**:框选结束后是否保留选框。打开后,非空框选会留下矩形;在框内按下并拖动一个可拖动的已选节点,会把框内选中的全部节点按同一世界坐标增量平移,绑定线路、通道骨架与回线一起跟随,框本身按屏幕增量同步移动。拖动整框不参与节点合并;在框内按下但不拖动也不会把选中集清掉。需要同时打开 `features: { boxSelect: true, drag: true }`
775
787
  - **收起时机**:选中集发生变化(重新框选、点击别的要素、`selectFeatures` / `deselect` 改变集合)、点击空白 / Esc 清空、把本项改回 `false`、实例销毁,或调用 `clearSelectionBox()`。`clearSelectionBox()` 只收矩形、不改选中集;`getSelectionBox()` 读当前矩形(画布 CSS 像素,`{ x, y, width, height }`,没有时为 `null`)
776
788
  - **参考**:见「08 选中与拐点」的「保留框与整组拖动」
777
789
  - **示例**:
778
790
 
779
791
  ```js
780
792
  const view = createGisView(container, {
781
- boxSelect: true,
782
- draggable: true,
793
+ features: { boxSelect: true, drag: true },
783
794
  selectionBoxPersistent: true
784
795
  })
785
796
 
@@ -792,7 +803,7 @@ view.clearSelectionBox() // 只收框;框里的节点仍保持选
792
803
 
793
804
  - **类型**:`boolean`
794
805
  - **默认值**:`true`
795
- - **说明**:选中的节点 / 拖动中的整组 / 待确认合并的目标节点,以及选中线路所在的整条通道,是否抬层绘制(与 `zIndex` 无关)。选中的线路 / 通道压在普通节点之上,但两端节点会再抬一层压回线路之上,保证端点与线路接头可见;见「选中 / 拖动的层级」
806
+ - **说明**:选中的节点 / 拖动中的整组 / 待确认合并的目标节点,以及选中线路所在的整条通道,是否连同各自的 `decorations` 一起抬层绘制(与 `zIndex` 无关)。选中的线路 / 通道压在普通节点之上,但两端节点会再抬一层压回线路之上,保证端点与线路接头可见;抬层装饰排在抬层线路之后、抬层节点之前,见「选中 / 拖动的层级」
796
807
  - **参考**:见「选中 / 拖动的层级(selectionOnTop)」一节,子字段在那里的表里逐个展开
797
808
 
798
809
  #### 拐点样式(`waypointStyle` / `waypointSelectStyle`)
@@ -805,7 +816,7 @@ view.clearSelectionBox() // 只收框;框里的节点仍保持选
805
816
 
806
817
  ```js
807
818
  createGisView(container, {
808
- waypointEditable: true, // 拐点交互缺省关,先打开
819
+ features: { waypoint: true }, // 拐点交互缺省关,先打开
809
820
  waypointVisibility: 'always', // 想让全部线路的拐点都露出来
810
821
  waypointStyle: { size: 9, color: '#ffffff', strokeColor: '#2f6fd0', strokeWidth: 2 },
811
822
  waypointSelectStyle: { size: 15, color: '#ffd166', strokeColor: '#e0a92e' }
@@ -814,39 +825,7 @@ createGisView(container, {
814
825
  view.updateOptions({ waypointSelectStyle: { color: '#ffd166' } }) // 选中那一个换成琥珀色
815
826
  ```
816
827
 
817
- ### 交互开关
818
-
819
- #### `boxSelect`
820
-
821
- - **类型**:`boolean`
822
- - **默认值**:`false`
823
- - **说明**:是否允许按住框选快捷键(`shortcuts.boxSelect`,默认 Shift)拖动鼠标框选要素;**缺省关**,写 `boxSelect: true` 或 `features: { boxSelect: true }` 才开。缺省只收节点,类型范围由 `selectionKinds` 决定
824
-
825
- #### `multiSelect`
826
-
827
- - **类型**:`boolean`
828
- - **默认值**:`false`
829
- - **说明**:多选开关:打开后不用按修饰键,直接单击节点 / 线路就取反累加选中(与按住 `shortcuts.multiSelect` 点击同一套行为、同一个 `multiSelectStyle`;取反那一下不经过 `onClick`,`onSelectChange.action.type` 是 `toggleSelect`);**缺省关**,关着时多选走框选(`boxSelect`)与按住多选快捷键(默认 Ctrl / ⌘)。运行时 `updateOptions({ multiSelect })` 切换只重画一帧、不重排场景
830
- - **参考**:见「点击与样式」一节,子字段在那里的表里逐个展开
831
- - **示例**:
832
-
833
- ```js
834
- createGisView(container, { multiSelect: true }) // 单击即取反累加,不用按 Ctrl
835
- // 按住快捷键的老写法照旧:Ctrl + 点击
836
- ```
837
-
838
- #### `toggleSelect`
839
-
840
- - **类型**:`boolean`
841
- - **默认值**:`false`
842
- - **说明**:再次选中即取消。打开后,普通单击已选中的节点 / 线路 / 通道回线会把它从选中集移除;按住 Ctrl / ⌘ 先选中,再普通单击同一要素也同样取消。通道回线按 `channelSelectMode` 取消单条 / 整组 / 整条;命中的分组或通道已经完整选中时,即使处于 `line` 模式也整组 / 整条取消,避免只删一条后仍像通道未取消。`selectionKinds` 只约束框选与 Ctrl / 多选,不拦截这里的取消。`selectTrigger: 'click'` 下双击仍按一次操作处理,第一下取反、第二下保持;`selectTrigger: 'dblClick'` 下由第二下取反,再次双击取消。取反生效时 `onClick.action.type` 与 `onSelectChange.action.type` 都是 `'toggleSelect'`,同一次点击产生的级联高亮也一起收掉。与 `multiSelect` 同时打开时沿用多选的累加取反口径;缺省关,运行时可用 `updateOptions({ toggleSelect })` 切换
843
- - **参考**:见「[08 选中与拐点](08-选中与拐点.md#点击选中与-dblclick)」
844
- - **示例**:
845
-
846
- ```js
847
- createGisView(container, { toggleSelect: true })
848
- view.updateOptions({ toggleSelect: false }) // 关掉后再次单击保持选中
849
- ```
828
+ ### 交互行为
850
829
 
851
830
  #### `selectTrigger`
852
831
 
@@ -865,13 +844,13 @@ view.updateOptions({ selectTrigger: 'click' })
865
844
 
866
845
  - **类型**:`GisViewSelectionKindsInput`
867
846
  - **默认值**:无(框选只收节点;Ctrl 多选仍收节点 / 线路 / 通道)
868
- - **说明**:限定框选与 Ctrl(⌘)/ `multiSelect` 点击能选中哪几类要素。`'point'` 是节点、`'line'` 是通道外的独立线路、`'channel'` 是通道回线;`'lineEndpoints'` / `'channelEndpoints'` 是框选专用的另一种口径,要求线路 / 通道自己的两端节点都落在框内,纯 `path`、缺失端点与自环跳过。数组可任意组合,`'all'` / `true` 五档全开,`false` / `null` 全关;新旧线路 / 通道口径并存时取并集。通道回线只受 `channel` 管、通道外的线路只受 `line` 管;选中通道回线时仍按 `channelSelectMode` 决定只选这一条 / 一组 / 整条通道。端点口径不会顺带选中两端节点,也不依赖 `point` 是否开启。**不写时保持旧口径**:框选只收节点,Ctrl / 多选点击收节点与线路;显式写 `selectionKinds` 后框选按完整过滤、Ctrl / 多选点击只认互斥的 `point` / `line` / `channel`。普通单击、`toggleSelect` 的再次点击取消与 `select` / `selectFeatures` 接口不受限制;`selectInBox(box, { selectionKinds })` 可一次性覆盖。
847
+ - **说明**:限定框选与 Ctrl(⌘)/ `multiSelect` 点击能选中哪几类要素。`'point'` 是节点、`'line'` 是通道外的独立线路、`'channel'` 是通道回线;`'lineEndpoints'` / `'channelEndpoints'` 是框选专用的另一种口径,要求线路 / 通道自己的两端节点都落在框内,纯 `path`、缺失端点与自环跳过。数组可任意组合,`'all'` / `true` 五档全开,`false` / `null` 全关;新旧线路 / 通道口径并存时取并集。通道回线只受 `channel` 管、通道外的线路只受 `line` 管;选中通道回线时仍按 `channelSelectMode` 决定只选这一条 / 一组 / 整条通道。`line` / `channel` 的几何命中按实际折线路径与选框逐线段相交判定,包围盒只用于候选粗筛,U 形 / 折返线路不会因包围盒覆盖选框而误选。端点口径不会顺带选中两端节点,也不依赖 `point` 是否开启。**不写时保持旧口径**:框选只收节点,Ctrl / 多选点击收节点与线路;显式写 `selectionKinds` 后框选按完整过滤、Ctrl / 多选点击只认互斥的 `point` / `line` / `channel`。普通单击、`toggleSelect` 的再次点击取消与 `select` / `selectFeatures` 接口不受限制;`selectInBox(box, { selectionKinds })` 可一次性覆盖。
869
848
  - **参考**:见「08 选中与拐点」的「框选」与「选择类型过滤(selectionKinds)」
870
849
  - **示例**:
871
850
 
872
851
  ```js
873
852
  createGisView(container, {
874
- boxSelect: true,
853
+ features: { boxSelect: true },
875
854
  selectionKinds: ['point', 'line'], // 框选只收节点与通道外线路,不选通道
876
855
  channelSelectMode: 'channel' // 真要选通道回线时整条通道一起选
877
856
  })
@@ -882,13 +861,6 @@ view.selectInBox(rect, { selectionKinds: 'channel' }) // 仅这一次只框通
882
861
  view.selectInBox(rect, { selectionKinds: ['lineEndpoints', 'channelEndpoints'] }) // 只框两端都在框里的线路 / 通道
883
862
  ```
884
863
 
885
- #### `waypointEditable`
886
-
887
- - **类型**:`boolean`
888
- - **默认值**:`false`
889
- - **说明**:是否允许编辑线路拐点(按住加点快捷键 `shortcuts.addWaypoint`(默认 Ctrl / ⌘)+ 点击添加、拖动移动、双击或删除快捷键删除),单条线可用 `waypointEditable` 覆盖;**缺省关**,写 `waypointEditable: true` 或 `features: { waypoint: true }` 才开。`false`(以及 `features: { waypoint: false }`)会同时关闭新增、移动与删除,没有只关其中一项的细分开关;`shortcuts.addWaypoint` 仍只决定加点的组合键
890
- - **参考**:见「线路拐点」一节,子字段在那里的表里逐个展开
891
-
892
864
  #### `waypointVisibility`
893
865
 
894
866
  - **类型**:`'selected' | 'always'`
@@ -896,29 +868,11 @@ view.selectInBox(rect, { selectionKinds: ['lineEndpoints', 'channelEndpoints'] }
896
868
  - **说明**:拐点显示时机:`selected` 只显示选中线路(以及刚加过拐点的线路)的拐点,`always` 全部显示
897
869
  - **参考**:见「线路拐点」一节
898
870
 
899
- #### `interactive`
900
-
901
- - **类型**:`boolean`
902
- - **默认值**:(缺省全关)
903
- - **说明**:查看类交互的「详细配置手动打开」写法:写 `true` 打开拖拽平移 / 滚轮缩放 / 右键回调(等价 `pan` / `zoom` / `contextMenu` 三个开关),不写就是全关
904
-
905
871
  #### `preventContextMenu`
906
872
 
907
873
  - **类型**:`boolean`
908
874
  - **默认值**:`true`
909
- - **说明**:组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):`contextMenu` 开着(或 `interactive: true` / `features: 'view'`)时组件才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` 照常发)。右键整个不接时(`contextMenu` 缺省关)系统菜单自然照弹
910
-
911
- #### `clickable`
912
-
913
- - **类型**:`boolean`
914
- - **默认值**:(缺省全关)
915
- - **说明**:查看类交互的「详细配置手动打开」写法:写 `true` 打开悬停高亮与点击选中(等价 `hover` / `select` 两个开关),要素自身可用 `clickable: false` 逐个关掉
916
-
917
- #### `draggable`
918
-
919
- - **类型**:`boolean`
920
- - **默认值**:`false`
921
- - **说明**:点是否可拖拽,单个点可用 `draggable` 覆盖;**缺省关**,写 `draggable: true` 或 `features: { drag: true }` 才开
875
+ - **说明**:组件接住右键时是否收掉浏览器的系统菜单(`contextmenu` 的默认行为):只有 `features: { contextMenu: true }`(或 `features: 'view'`)时才接右键,接住时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样;想让系统菜单自己弹就写 `false`(`onContextMenu` 照常发)。右键整个不接时系统菜单自然照弹
922
876
 
923
877
  #### `liveDrag`
924
878
 
@@ -13,7 +13,7 @@
13
13
  | `onDblClick` | `GisViewClickPayload \| null` | 双击要素或空白处(320ms / 6px 内连续两次单击):`action.type` 是 `dblClick`、`action.double` 为 `true`。普通双击在两种 `selectTrigger` 下都会于第二下派发;`dblClick` 模式下选中 / 清空 / 多选取反也在这一下应用,`click` 模式下第一下已经应用过。`click` 模式的 Ctrl / `multiSelect` 取反快捷路保持原口径,不额外发 `onClick` / `onDblClick` |
14
14
  | `onPointerDown` | `GisViewPointerPayload` | 左键 / 中键 / 右键按下:命中的要素与光标位置在载荷顶层;`action.type` 是这一次按下的**意图**(`dragPoint` / `dragWaypoint` / `boxSelect` / `pan` / `copy` / `channelDraft` / `contextMenu`…),`action.target` 说明点的是什么。点 / 线读 `item`,通道回线读 `channel` / `channelContext`,鼠标键、修饰键与 clientX / clientY 读 `originalEvent`,见「指针事件」 |
15
15
  | `onPointerUp` | `GisViewPointerPayload` | 左键 / 中键 / 右键抬起,与 `onPointerDown` 成对:`action.type` 是**刚做完的那件事**(拖过就是 `dragPoint` / `dragWaypoint` / `boxSelect` / `pan`,没拖就是 `click`),`action.moved` 说明这一次是拖还是点,另带 `action.press`(按下时的意图)/ `duration`(按了多久)/ `longPress`(长按) |
16
- | `onContextMenu` | `GisViewPointerPayload` | 每一次右键:点、线、通道、空白都只发这一条(要素信息在载荷里);`action.type` 固定是 `contextMenu`、`action.target` 说明点的是点 / 线 / 通道 / 空白。命中通道回线时,同一载荷另带 `channelContext`(几组 / 当前哪组 / 可用交互),不再有单独的通道右键回调。要自己弹菜单就在回调里渲染。**右键回调缺省关**(`contextMenu` / `interactive` 都没写时组件不接右键),写 `interactive: true` / `contextMenu: true` 或 `features: 'view'` 才发;接住右键时系统菜单默认已被收掉(`preventContextMenu` 缺省 `true`),想看系统菜单写 `preventContextMenu: false` |
16
+ | `onContextMenu` | `GisViewPointerPayload` | 每一次右键:点、线、通道、空白都只发这一条(要素信息在载荷里);`action.type` 固定是 `contextMenu`、`action.target` 说明点的是点 / 线 / 通道 / 空白。命中通道回线时,同一载荷另带 `channelContext`(几组 / 当前哪组 / 可用交互),不再有单独的通道右键回调。要自己弹菜单就在回调里渲染。**右键回调缺省关**,权限只从 `features` 读取:`features: { contextMenu: true }` 或 `features: 'view'` 才发;接住右键时系统菜单默认已被收掉(`preventContextMenu` 缺省 `true`),想看系统菜单写 `preventContextMenu: false` |
17
17
  | `onCascadeHighlight` | `GisViewCascadeHighlightInfo \| null` | 点击级联高亮变了:载荷带来源 `origin`(节点 / 线路 / 通道回线)、范围 `scope` 与亮着的 `points` / `lines` / `pointIndexes` / `lineIndexes`,与 `getCascadeHighlight()` 同一份;点空白或关掉开关时是 `null`。这条高亮不改选中集,事件总线名为 `cascadeHighlight` |
18
18
  | `onHover` | `GisViewHoverPayload \| null` | 悬停要素变化(空白处 / 移出画布是 `null`):与拾取结果同一份读数(`type` / `index` / `item` + 通道字段,另有光标位置 `x` / `y` / `world` 与 `lngLat`)+ `action`(`type` 恒为 `hover`、`target` 说明停在什么上面);指针正好停在最近一次指针事件上时 `originalEvent` 是那一次事件,按键、修饰键与 clientX / clientY 从它读。选中集总读数用 `getSelectionInfo()`;程序化悬停 `hoverAt()` 也走同一条 |
19
19
  | `onFilter` | `GisViewFilterResult` | 筛选完成(含 Worker 异步返回):隐藏的点 / 线下标、剩余可见数量、是否用了 Worker 与计算耗时。`filter.mode: 'remove'` 时删除完成后只发最终一次,`hiddenPoints` / `hiddenLines` 为空 |
@@ -199,12 +199,14 @@ view.off('*') // 不传 handler = 摘掉这个事件的全部订阅,返回摘
199
199
 
200
200
  ## 功能开关
201
201
 
202
- `features` 把散在 options 各处的交互口径收成一张表:不用一行行去对 `draggable` / `boxSelect` / `copy.enabled` / `waypointEditable`,一句话就能把组件切成「全功能编辑」「只读浏览」「静态图」三种口径。**缺省全关** —— 什么都不写时只剩画布与数据:平移 / 缩放 / 悬停 / 选中 / 右键回调 / 快捷键都不响应,改数据的交互(框选多选 / 拖拽 / 拐点 / 复制 / 合并 / 删除 / 新增通道 / 通道右键菜单)当然也关着;**要哪一项就手动打开哪一项**:查看类写详细配置 `interactive: true`(打开平移 / 缩放 / 右键回调)与 `clickable: true`(打开悬停 / 单击选中),编辑类写 `draggable: true` / `boxSelect: true` / `waypointEditable: true`,复制合并删除写 `copy: {…}` / `merge: {…}` / `remove: {…}`(写了就是打开,`enabled: false` 才是关),也可以在同一张开关表里写 `features: { drag: true }`;`features: 'view'` 一次打开查看类、`features: 'edit'` 一次全开。
202
+ `features` 是交互权限的**唯一入口**:平移、缩放、悬停、选中、多选、取反、框选、拖拽、拐点、右键回调、通道草稿与键盘都只写在这张表里;旧的顶层 `draggable` / `interactive` / `clickable` / `boxSelect` / `multiSelect` / `toggleSelect` / `waypointEditable` 已移除,传入会收到迁移提示并忽略。一句话就能把组件切成「全功能编辑」「只读浏览」「静态图」三种口径。**缺省全关** —— 什么都不写时只剩画布与数据;**要哪一项就手动打开哪一项**,例如查看类写 `features: { pan: true, zoom: true, hover: true, select: true, contextMenu: true }`,框选 / 拐点 / 拖动写 `features: { boxSelect: true, waypoint: true, drag: true }`,复制合并删除仍写 `copy: {…}` / `merge: {…}` / `remove: {…}`(写了就是打开,`enabled: false` 才是关);`features: 'view'` 一次打开查看类、`features: 'edit'` 一次全开。
203
+
204
+ **写法是对象,不是数组**:`features` 接受 `{ 开关名: true | false }`、预设名或 `true` / `false`。对象里还可以直接写常用的详细配置名作为别名;同一张表同时写别名和规范名时,规范名优先。数组写法不会生效,组件会发一条 `warning` 并忽略这一份 `features`。
203
205
 
204
206
  ```js
205
207
  createGisView('#map', { points, lines, features: 'view' }) // 只读浏览:能看能选,改不动
206
208
  createGisView('#map', { points, lines, features: 'static' }) // 静态图:全关(与什么都不写一致)
207
- createGisView('#map', { points, lines, interactive: true, clickable: true }) // 不用开关表:详细配置手动打开查看类
209
+ createGisView('#map', { points, lines, features: 'view' }) // 查看类一次打开:能看能选,改不动数据
208
210
  createGisView('#map', {
209
211
  points, lines,
210
212
  features: { drag: true, label: false } // 打开「拖动节点」、关掉「节点标签」
@@ -213,6 +215,10 @@ createGisView('#map', {
213
215
  points, lines,
214
216
  features: { waypoint: false } // 新增 / 移动 / 删除拐点一起关掉
215
217
  })
218
+ createGisView('#map', {
219
+ points, lines,
220
+ features: { draggable: true, multiSelect: true, toggleSelect: true } // 别名 + 选中类开关
221
+ })
216
222
  ```
217
223
 
218
224
  | 预设 | 效果 |
@@ -221,43 +227,54 @@ createGisView('#map', {
221
227
  | `'view'` | 只读浏览:一次打开查看类(平移缩放、悬停、选中、右键回调、键盘),`boxSelect` / `drag` / `waypoint` / `copy` / `merge` / `remove` / `channelDraft` 仍关着 —— 「能看能选、改不动数据」 |
222
228
  | `'static'` | 静态图:显式全关(含标签 / 提示 / 鹰眼图),只剩画布与数据;留着这个名字方便「从全开切回静态」 |
223
229
 
224
- | 开关 | 关掉之后 | 没写时跟随 |
230
+ | 开关 | 关掉之后 | 没写时的有效值 / 来源 |
225
231
  | --- | --- | --- |
226
- | `pan` | 拖拽平移 | `interactive === true`(缺省关) |
227
- | `zoom` | 滚轮缩放 | `interactive === true`(缺省关) |
228
- | `hover` | 悬停拾取(`onHover` 与悬停样式) | `clickable === true`(缺省关) |
229
- | `select` | 点击选中(`selectFeatures()` / `selectInBox()` 这些接口不受限) | `clickable === true`(缺省关) |
230
- | `boxSelect` | Shift + 拖动框选(可用 `shortcuts.boxSelect` 改) | `boxSelect === true`(缺省关) |
231
- | `drag` | 拖动节点(含整组拖动) | `draggable === true`(缺省关) |
232
- | `waypoint` | 线路拐点的新增 / 移动 / 删除(一道总闸,不做细分) | `waypointEditable === true`(缺省关) |
232
+ | `pan` | 拖拽平移 | 关(缺省);只能写 `features.pan` |
233
+ | `zoom` | 滚轮缩放 | 关(缺省);只能写 `features.zoom` |
234
+ | `hover` | 悬停拾取(`onHover` 与悬停样式) | 关(缺省);只能写 `features.hover` |
235
+ | `select` | 点击选中(`selectFeatures()` / `selectInBox()` 这些接口不受限) | 关(缺省);只能写 `features.select` |
236
+ | `multiSelect` | 不按修饰键的普通点击多选 / 取反 | 关(缺省);只能写 `features.multiSelect` |
237
+ | `toggleSelect` | 再次点击已选中的要素时取消选中 | 关(缺省);只能写 `features.toggleSelect` |
238
+ | `boxSelect` | Shift + 拖动框选(可用 `shortcuts.boxSelect` 改) | 关(缺省);只能写 `features.boxSelect` |
239
+ | `drag` | 拖动节点(含整组拖动) | 关(缺省);只能写 `features.drag`(表内别名 `draggable`) |
240
+ | `waypoint` | 线路拐点的新增 / 移动 / 删除(一道总闸,不做细分) | 关(缺省);只能写 `features.waypoint`(表内别名 `waypointEditable`) |
233
241
  | `copy` | Alt 点击复制与复制接口 | 写了 `copy` 且 `copy.enabled !== false`(缺省关) |
234
242
  | `merge` | 拖到另一个节点上合并 | 写了 `merge` 且 `merge.enabled !== false`(缺省关) |
235
243
  | `remove` | Delete 删除选中的拐点 / 线路 / 节点(先出删除载荷,确认之后才动数据) | 写了 `remove` 且 `remove.enabled !== false`(缺省关) |
236
244
  | `channelDraft` | 新增通道草稿(程序化 `addChannel()` 不受限) | 关(缺省) |
237
- | `contextMenu` | 右键回调(关掉之后组件不接右键,浏览器菜单照常弹;通道回线的 `channelContext` 也随这条一起给) | `interactive === true`(缺省关) |
245
+ | `contextMenu` | 右键回调(关掉之后组件不接右键,浏览器菜单照常弹;通道回线的 `channelContext` 也随这条一起给) | 关(缺省);只能写 `features.contextMenu` |
238
246
  | `tooltip` | 悬停提示(悬停样式与 `onHover` 还在) | 配了 `tooltip` 且没写 `enabled: false` |
239
247
  | `label` / `lineLabel` | 节点 / 线路标签图层 | 配了且没写 `enabled: false`(不配不产生 DOM) |
240
248
  | `minimap` | 内置鹰眼图 | 配了 `minimap` 且没写 `enabled: false` |
241
249
  | `keyboard` | 全部**键盘**快捷键(指针修饰键不受它限制,见下) | 关(缺省) |
242
250
 
243
- 三条规则:**没写的开关跟随各自的详细配置**(`interactive` / `clickable` / `draggable` / `boxSelect` / `copy.enabled` 这些),**详细配置也没写就是关**(查看类的详细配置就是 `interactive` / `clickable`;提示 / 标签 / 鹰眼图「配了才有」,配了就等于手动打开);**写了 `true` / `false` 就是强制打开 / 关闭**(`tooltip` / `label` / `lineLabel` / `minimap` 四项例外:开关写 `true` 只是「不按没配处理」,详细配置没配或写了 `enabled: false` 一样没有图层 —— 两边是**与**的关系,见下);**开关只决定「组件做不做某个交互」,不改数据与样式** —— 关掉 `drag` 之后节点还在、还能选中,只是拖不动;关掉 `label` 之后标签图层不再产生 DOM,数据与样式一律不动。
251
+ 详细配置名别名(只用于输入,不进入 `getFeatures()` 的读数):
252
+
253
+ | 别名 | 展开成规范开关 |
254
+ | --- | --- |
255
+ | `draggable` | `drag` |
256
+ | `waypointEditable` | `waypoint` |
257
+ | `interactive` | `pan` / `zoom` / `contextMenu` |
258
+ | `clickable` | `hover` / `select` |
259
+
260
+ 导出的 `FEATURE_NAMES` 仍以 19 个规范开关名为准,`FEATURE_ALIASES` 是这张别名表;`getFeatures()` 不返回任何别名键,也不返回数组下标。别名只写在 `features` 对象里解析。七个旧的顶层交互字段 `draggable` / `interactive` / `clickable` / `boxSelect` / `multiSelect` / `toggleSelect` / `waypointEditable` 已全部移除:传入会收到 warning 并忽略,也不会留在 `getOptions()` 返回对象里;表内别名仍可用。
261
+
262
+ 三条规则:**交互权限没写就是关**(`pan` / `zoom` / `hover` / `select` / `multiSelect` / `toggleSelect` / `boxSelect` / `drag` / `waypoint` / `contextMenu` / `channelDraft` / `keyboard`);**复制 / 合并 / 删除缺省跟随各自的详细配置**(写了 `copy` / `merge` / `remove` 即开,`enabled: false` 关),提示 / 标签 / 鹰眼图仍是「配了才有」;**写了 `true` / `false` 就是强制打开 / 关闭**(`tooltip` / `label` / `lineLabel` / `minimap` 四项例外:开关写 `true` 只是「不按没配处理」,详细配置没配或写了 `enabled: false` 一样没有图层 —— 两边是**与**的关系)。开关只决定「组件做不做某个交互」,不改数据与样式。
244
263
 
245
- ### 两层开关:`features` 与详细配置(重合的那几对)
264
+ ### `features` 与详细配置的分工
246
265
 
247
- 同一件事常能写在两个地方(开关表 / 各自的详细配置),这不是重复,是「**一行说清整体口径**」与「**逐项微调**」两层。逐项对完 17 个开关的关系是三类:
266
+ 交互权限不再有第二份顶层配置;`features` 管「做不做」,详细配置只保留带参数或内容的几类能力:
248
267
 
249
268
  | 能力 | 开关 | 详细配置 | 谁说了算 |
250
269
  | --- | --- | --- | --- |
251
- | 平移 / 缩放 / 右键回调 | `pan` / `zoom` / `contextMenu` | `interactive` | 开关写了按开关;没写看 `interactive === true` |
252
- | 悬停 / 点击选中 | `hover` / `select` | `clickable` | 同上 |
253
- | 框选 / 拖节点 / 拐点 | `boxSelect` / `drag` / `waypoint` | `boxSelect` / `draggable` / `waypointEditable` | 同上 |
270
+ | 查看 / 选中 / 编辑权限 | `pan` / `zoom` / `hover` / `select` / `multiSelect` / `toggleSelect` / `boxSelect` / `drag` / `waypoint` / `contextMenu` / `channelDraft` / `keyboard` | 无(节点数据的 `clickable` / `draggable`、线路数据的 `waypointEditable` 仍是逐要素覆盖,不是顶层选项) | 只认 `features`;没写即关 |
254
271
  | 复制 / 合并 / 删除 | `copy` / `merge` / `remove` | `copy` / `merge` / `remove` 的 `enabled` | 开关写布尔值时直接盖到 `enabled` 上(两边同一条路) |
255
272
  | 标签 / 线路标签 / 提示 / 鹰眼图 | `label` / `lineLabel` / `tooltip` / `minimap` | 详细配置本身(`enabled: false` 就是关) | **与**:开关写 `true` 只是「不按没配处理」,详细配置没配或写了 `enabled: false` 就没有这个图层 |
256
273
  | 键盘快捷键 | `keyboard` | `shortcuts` | `keyboard` 是总闸(关掉之后一个键盘快捷键都不响应);`shortcuts` 是逐动作键位(某一条写 `null` 可以单独停掉)。**Ctrl / ⌘ / Shift 这类指针修饰键不算键盘快捷键**:框选(默认 Shift)、加拐点与多选(默认 Ctrl)、复制预览由 `shortcuts.boxSelect` / `addWaypoint` / `multiSelect` / `duplicate` 决定,`keyboard: false` 不影响它们 |
257
274
  | 筛选 Worker | 没有开关 | `filterWorker`(总闸 + 阈值)与 `filter.worker`(这一份筛选) | `filterWorker: false` 强制同步;否则 `filter.worker ?? true` |
258
275
  | 历史 | 没有开关 | `history`(`true` / `{ limit }` 就是开,`enabled: false` 关) | 只有详细配置这一层 |
259
276
 
260
- 不写进 `features` 的还有 `multiSelect` / `preventContextMenu` / `liveDrag` / `showGrid` / `initialView`:它们不改变「组件做不做某类交互」,跟各自的详细配置走。
277
+ 不写进 `features` 的还有 `preventContextMenu` / `liveDrag` / `showGrid` / `initialView`:它们不改变「组件做不做某类交互」,跟各自的详细配置走。
261
278
 
262
279
  `getFeatures()` 给的是**有效值**:把「跟随详细配置」也解析成了 `true` / `false`,界面可以直接拿它当复选框的选中态;上面「与」的那四项在没有可显示的内容时读数是 `false`(配了就是 `true`,写了 `enabled: false` 又回到 `false`)。
263
280
 
@@ -331,7 +331,7 @@ const geometryOnly = view.getChangedLines({ reasons: ['path'] })
331
331
  | 分组(删一个:把一组并进相邻组) | `removeChannelGroup(channelId, groupId, options?)` | `channelId` / `groupId` 目标通道与要删的组;`options.targetGroupId` 指名并到哪一组(缺省前一组,第一组并后一组) | `{ ok, reason, channelId, groupId, targetGroupId, lineIds, groups }`;骨架 / 拐点沿用目标组,并完只剩一组时声明回到「整条通道一组」;单组通道 `ok: false`(删整条用 `removeChannel`) |
332
332
  | 通道下增减回线(把已有线路挂进 / 摘出通道) | `addChannelLines(channelId, lineIds, options?)` / `removeChannelLines(channelId, lineIds)` | 回线本身要在数据里已经存在(`addLine` / `addLines` 加的独立线路);`addChannelLines` 的 `options.groupId` 指定进哪一组(缺省第一组) | `{ ok, reason, channelId, lineIds, skipped, groups }`:加进来的追加在声明末尾、几何改由通道推导;摘出去的回线留在数据里、按自己的路径画;已在别的通道 / 认不出 / 无 id 的进 `skipped`;摘完不剩时 `ok: false` |
333
333
  | 拐点(线路,插一个 / 删一个) | `addWaypoint(lineId, [x, y])` / `removeWaypoint(lineId, pathIndex)` | 坐标写法与数据一致;`pathIndex` 是 `path` 里的下标 | 返回实例(链式);结果读 `getWaypoints()`,事件 `onWaypointChange` 的 `phase` 是 `'add'` / `'remove'`(拖动是 `start` / `move` / `end`) |
334
- | 拐点(线路,一次插多个) | `addWaypoints(lineId, coordinates, options?)` | `coordinates` 是一批坐标(按数组顺序插入);`options.label` 改这一步历史的标签 | `{ ok, reason, count, lineId, lineIndex, coordinates }`:多个拐点合成一步历史,`coordinates` 只含实际插入成功的坐标;线路不存在 / `waypointEditable: false` 时 `ok: false` |
334
+ | 拐点(线路,一次插多个) | `addWaypoints(lineId, coordinates, options?)` | `coordinates` 是一批坐标(按数组顺序插入);`options.label` 改这一步历史的标签 | `{ ok, reason, count, lineId, lineIndex, coordinates }`:多个拐点合成一步历史,`coordinates` 只含实际插入成功的坐标;线路不存在 / `features.waypoint` 关闭 / 线路自身 `waypointEditable: false` 时 `ok: false` |
335
335
  | 拐点(线路,整份换) | `setLineWaypoints(id, [[x, y], …])` | 只写**中间拐点**(不含首尾端点;传单个坐标 = 只留一个拐点);空数组 = 去掉全部拐点回到直连。写回数据时首尾按节点 / `start` / `end` 补齐 | 走 `updateLine` 同一条落地(数据变更 + 历史);通道里的回线写的是它所在分组的骨架(走 `updateChannel`);返回实例 |
336
336
  | 拐点(通道骨架,整份换) | `setChannelPath(channelId, [[x, y], …])` | 只写**中间拐点**(不含首尾两端;单个坐标 = 只留一个拐点、空数组 = 拉直;两端由节点坐标补齐) | 分过组的通道写的是第一组的 `groups[].path`(顶层 `path` 会被盖住,见「线路拐点」);返回实例 |
337
337
  | 拐点(某个分组,插一个 / 删一个) | `addChannelGroupWaypoint(channelId, groupId, coordinate)` / `removeChannelGroupWaypoint(channelId, groupId, pathIndex)` | 分组要先拆出来(`addChannelGroup` / `splitChannelGroups`) | 返回实例;实时读数用 `getChannelGroupWaypoints(channelId, groupId)` |