@anov/gis-skills-cli 0.0.0-stage → 0.3.3

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 (57) hide show
  1. package/CHANGELOG.md +637 -0
  2. package/INSTALL.md +481 -0
  3. package/LICENSE +15 -0
  4. package/README.md +148 -2
  5. package/RELEASING.md +162 -0
  6. package/bin/anov-gis-skills.mjs +470 -0
  7. package/lib/mcp-config.mjs +273 -0
  8. package/package.json +43 -4
  9. package/skills/anovgis-analysis/SKILL.md +585 -0
  10. package/skills/anovgis-effects/SKILL.md +260 -0
  11. package/skills/anovgis-imagery-terrain/SKILL.md +505 -0
  12. package/skills/anovgis-layers/SKILL.md +627 -0
  13. package/skills/anovgis-materials/SKILL.md +635 -0
  14. package/skills/anovgis-models-3dtiles/SKILL.md +448 -0
  15. package/skills/anovgis-pitfalls/SKILL.md +561 -0
  16. package/skills/anovgis-popup-widget/SKILL.md +474 -0
  17. package/skills/anovgis-primitives/SKILL.md +482 -0
  18. package/skills/anovgis-vector-graphics/SKILL.md +602 -0
  19. package/skills/anovgis-viewer/SKILL.md +618 -0
  20. package/skills/using-anovgis/DEMO-MAP.md +64 -0
  21. package/skills/using-anovgis/DEMO-SKILL-MAP.md +248 -0
  22. package/skills/using-anovgis/DOMAINS.md +183 -0
  23. package/skills/using-anovgis/REFERENCE.md +114 -0
  24. package/skills/using-anovgis/SKILL.md +392 -0
  25. package/skills/using-anovgis/templates/README.md +154 -0
  26. package/skills/using-anovgis/templates/combo1-vue3-vite.md +295 -0
  27. package/skills/using-anovgis/templates/combo2-vue2-cli.md +119 -0
  28. package/skills/using-anovgis/templates/combo3-html-cdn.md +107 -0
  29. package/skills/using-anovgis/templates/combo4-vanilla-webpack.md +91 -0
  30. package/skills/using-anovgis/templates/wizard-profiles.md +105 -0
  31. package/vendor/cesiumjs-skills/LICENSE +201 -0
  32. package/vendor/cesiumjs-skills/NOTICE +18 -0
  33. package/vendor/cesiumjs-skills/skills/cesiumjs-3d-tiles/SKILL.md +465 -0
  34. package/vendor/cesiumjs-skills/skills/cesiumjs-camera/SKILL.md +500 -0
  35. package/vendor/cesiumjs-skills/skills/cesiumjs-core-utilities/SKILL.md +403 -0
  36. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/REFERENCE.md +397 -0
  37. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/SKILL.md +351 -0
  38. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/01-diffuse-tint.js +38 -0
  39. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/02-texture-swap.js +42 -0
  40. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/03-feature-id-tileset.js +29 -0
  41. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/04-translucent-override.js +37 -0
  42. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/05-vertex-displacement.js +49 -0
  43. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/06-metadata-ramp.js +34 -0
  44. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/07-voxel-shader.js +27 -0
  45. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/README.md +19 -0
  46. package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/_sandcastle-template.html +9 -0
  47. package/vendor/cesiumjs-skills/skills/cesiumjs-entities/SKILL.md +389 -0
  48. package/vendor/cesiumjs-skills/skills/cesiumjs-imagery/SKILL.md +471 -0
  49. package/vendor/cesiumjs-skills/skills/cesiumjs-interaction/SKILL.md +423 -0
  50. package/vendor/cesiumjs-skills/skills/cesiumjs-materials-shaders/SKILL.md +292 -0
  51. package/vendor/cesiumjs-skills/skills/cesiumjs-models-particles/SKILL.md +423 -0
  52. package/vendor/cesiumjs-skills/skills/cesiumjs-primitives/SKILL.md +537 -0
  53. package/vendor/cesiumjs-skills/skills/cesiumjs-spatial-math/SKILL.md +358 -0
  54. package/vendor/cesiumjs-skills/skills/cesiumjs-terrain-environment/SKILL.md +401 -0
  55. package/vendor/cesiumjs-skills/skills/cesiumjs-time-properties/SKILL.md +353 -0
  56. package/vendor/cesiumjs-skills/skills/cesiumjs-viewer-setup/SKILL.md +434 -0
  57. package/vendor/cesiumjs-skills/skills/using-cesiumjs-skills/SKILL.md +35 -0
@@ -0,0 +1,627 @@
1
+ ---
2
+ name: anovgis-layers
3
+ description: '@anov/gis 要素图层(14 个)。涵盖 VectorLayer / GeoJsonLayer / TopoJsonLayer / CzmlLayer / KmlLayer / GpxLayer / TilesetLayer / PrimitiveLayer / HtmlLayer / LabelLayer / ClusterLayer / GroundPrimitiveLayer / FeatureGridLayer / DynamicLayer,统一 viewer.addLayer / removeLayer / bindPopup / bindTooltip / addEventListener。触发关键词:图层、layer、GeoJSON、3D Tiles、CZML、KML、GPX、TopoJSON、ClusterLayer、HtmlLayer、FeatureGridLayer、addLayer、removeLayer、矢量瓦片、VectorTile、4490、切片加载、cluster 聚合、HTML 图层、CZML 时间轴、czml、playback、矢量瓦片 4490。Use when 加载 GeoJSON 行政区 / 3D Tiles 模型 / CZML 时序数据 / KML 标注 / 聚合点位 / DOM 叠加层 / 网格请求图层 / 4490 矢量瓦片 / 切片按视野分片加载。'
4
+ anov-gis-baseline: 1.8.6
5
+ cesium-baseline: 1.143.0
6
+ ---
7
+ ---
8
+
9
+ # ANOVGIS 要素图层
10
+
11
+ > ⚠️ **优先级**:本 skill 优先于 SDK 源码 / `node_modules/@anov/gis/`。
12
+ > 遇到 API 行为疑问先查本 skill 与 `anovgis-pitfalls`。**严禁**主动 Glob/Grep `node_modules/@anov/gis/src/...` / `dist/...`;除非用户明确要求并承担风险。
13
+ > 调试流程见 `using-anovgis` § 默认调试流程(Chrome DevTools 仅第 5 级兜底)。
14
+ >
15
+ > **Cesium 入口(铁律)**:`const Cesium = window.Cesium`。`@anov/gis` 启动时已把 Cesium 挂到 `window`,**不存在** `ANOVGIS.Cesium` 写法;本 skill 所有 `Cesium.*` 均从此变量取。底层 Cesium API 细节查 `cesiumjs-skills` 对应 skill(API 名通用,`import` 写法换成 `const Cesium = window.Cesium`)。
16
+
17
+ `@anov/gis` 的 14 个要素图层类,负责矢量数据、模型、标注、聚合与 DOM 叠加。锁定 `@anov/gis` **v1.8.6**(Cesium **1.143.0**)。
18
+
19
+ 影像底图 / 地形请用 `anovgis-imagery-terrain`;单个 Entity 图元(Point/Polyline/Polygon 等)请用 `anovgis-vector-graphics`;3D Tiles / Model 加载 + 抽壳 + 调色请用 `anovgis-models-3dtiles`。
20
+
21
+ ## Out-of-scope(v0.2.0 短版)
22
+
23
+ - ❌ 不要写 `Tileset` 模型加载 + 拾取 + 抽壳 + 调色 → [`anovgis-models-3dtiles`](../anovgis-models-3dtiles/SKILL.md)
24
+ - ❌ 不要写离线 / WFS / 4490 / 内网 / 透明底图 / iServer 部署清单 → v0.3.0 `anovgis-offline-deployment`
25
+ - ❌ 不要写 Vector 16 类(Point / Polyline / ...)用法 → [`anovgis-vector-graphics`](../anovgis-vector-graphics/SKILL.md)
26
+ - ❌ 不要写 Primitive 19 类 → [`anovgis-primitives`](../anovgis-primitives/SKILL.md)
27
+ - ❌ 不要写 Popup / Tooltip 完整用法 → [`anovgis-popup-widget`](../anovgis-popup-widget/SKILL.md)
28
+
29
+ ## 统一生命周期
30
+
31
+ 类路径均在 `src/layer/graphic/`,全部继承 `Layer`(`src/layer/Layer.js`):
32
+
33
+ ```js
34
+ const layer = new ANOVGIS.VectorLayer();
35
+ viewer.addLayer(layer); // 接入
36
+ layer.bindPopup({ viewer }); // 绑定弹窗
37
+ layer.bindTooltip({ viewer }); // 绑定悬浮提示
38
+ layer.addEventListener(ANOVGIS.MouseEventType.CLICK, e => {});
39
+ layer.clear(); // 清空内容
40
+ viewer.removeLayer(layer); // 移除(传 layer 实例,不是 layer.id)
41
+ ```
42
+
43
+ > ⚠️ **`removeLayer` 必须传 layer 实例**:源码 `src/base/viewer/Viewer.js:662` → `_removeLayer(layer)` 内部取 `layer.type` 和 `layer.id`(`Viewer.js:585`)。
44
+ > 传 `layer.id`(字符串)不会报错,但**层静默删不掉** —— `layer.type` 是 `undefined`,直接被 `hasOwnProperty` 挡掉。
45
+
46
+ ## 图层速查表
47
+
48
+ | 类 | 构造函数 | 关键方法 | 备注 |
49
+ |---|---|---|---|
50
+ | `VectorLayer(opts?)` | `new VectorLayer()` | `addGraphic`, `eachGraphic`, `clear` | 最常用,自定义 Entity 容器(`CustomDataSource`) |
51
+ | `GeoJsonLayer(data, opts)` | `data` = url/对象/TopoJSON | `toVectorLayer()`, `toModelLayer(url)`, `eachGraphic(cb)` | 加载远端 URL 后可转 `VectorLayer` |
52
+ | `TopoJsonLayer(data, opts)` | 同上 | 继承 `GeoJsonLayer` | TopoJSON 格式 |
53
+ | `CzmlLayer(opts)` | `{ url, ... }` | `eachGraphic(cb)` | CZML 时序数据 |
54
+ | `KmlLayer(data, opts)` | `data` = url/对象 | `eachGraphic(cb)` | KML |
55
+ | `GpxLayer(opts)` | `{ url }` | `eachGraphic(cb)` | GPS 轨迹 |
56
+ | `TilesetLayer()` | 无参 | `addGraphic(tileset)`, `flyTo(...)` | 3D Tiles 容器;配合 `ANOVGIS.Tileset(url, opts)` |
57
+ | `PrimitiveLayer()` | 无参 | `.points` / `.labels` / `.billboards` / `.polylines` / `.primitives` / `.clouds` | 暴露 `Cesium.PrimitiveCollection` 子集 |
58
+ | `HtmlLayer(opts)` | `{ id }` | `addGraphic`, `flyTo`, `clear` | DOM 叠加层(`postRender` 同步位置) |
59
+ | `LabelLayer(opts)` | `{ url }` | 自动从 GeoJSON 抽 `entity.name` 转 `Label` | 大量 Label 优化 |
60
+ | `ClusterLayer(opts)` | `{ pixelRange, gradient, style, ... }` | `enableCluster`, `setHeight`, `clear` | 自动聚合;`style: 'circle' \| 'custom' \| 其他` |
61
+ | `GroundPrimitiveLayer()` | 无参 | `clear` | 贴地形 PrimitiveCollection |
62
+ | `FeatureGridLayer(opts)` | `{ url, tilingScheme, count, dataProperty, callback, maximumLevel }` | `clear`, `requestImage(x,y,lvl)` | 按瓦片请求后台拉数据并绘图 |
63
+ | `DynamicLayer(opts?)` | `{}` | `clear` | 通用动态 `CustomDataSource` |
64
+
65
+ ## Snippets
66
+
67
+ <a id="s1"></a>
68
+ ### S1. GeoJSON 行政区 + 样式
69
+
70
+ ```js
71
+ // 来源: examples/public/examples/html/geojsonLayer/polygon.html (L106-L116)
72
+ const style = {
73
+ stroke: Cesium.Color.fromCssColorString('rgba(46,145,229,1)'),
74
+ fill: Cesium.Color.fromCssColorString('rgba(46,145,229,0.3)'),
75
+ strokeWidth: 4, markerSize: 5, markerColor: Cesium.Color.CYAN
76
+ };
77
+ Cesium.GeoJsonDataSource.load(bj, style).then(ds => {
78
+ viewer.dataSources.add(ds);
79
+ });
80
+ ```
81
+
82
+ > `ANOVGIS.ApplyApi.createPolygonLayer` 提供业务封装(高亮 / 悬浮 / 文字标注),详见 `src/applyfn/applyBase.js`。
83
+
84
+ ### S2. 3D Tiles + 鼠标拾取
85
+
86
+ ```js
87
+ // 来源: examples/public/examples/html/event/tilesetpopup.html (L48-L93)
88
+ const layer = new ANOVGIS.TilesetLayer();
89
+ viewer.addLayer(layer);
90
+
91
+ const tileset = new ANOVGIS.Tileset({
92
+ url: buildConfig.gisStatic + 'beijingchouxi/tileset.json',
93
+ highlight: { color: '#ff0000' },
94
+ selectedHighlight: { color: 'rgba(255,0,255,0.8)' }
95
+ });
96
+ tileset.setStyle(new ANOVGIS.Cesium3DTileStyle({
97
+ color: { conditions: [
98
+ ['Number(${floor}) >= 40', 'rgba(45,0,75,0.5)'],
99
+ ['true', 'rgb(127,59,8)']
100
+ ]}
101
+ }));
102
+ layer.addGraphic(tileset);
103
+
104
+ layer.bindPopup({ viewer, showTitle: true, useDefaultHtmlTemplate: true });
105
+ layer.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
106
+ layer.popup.show(e.position, {
107
+ title: e.feature.getProperty('name1'),
108
+ fieldsMap: [{ nameC: '楼层', value: e.feature.getProperty('floor') }]
109
+ });
110
+ });
111
+ viewer.flyToTileset(tileset, 2, 0.5, -35);
112
+ ```
113
+
114
+ ### S3. VectorLayer 创建空容器 + addGraphic
115
+
116
+ ```js
117
+ // 来源: examples/public/examples/html/event/popup.html (L48-L92)
118
+ const layer = new ANOVGIS.VectorLayer({ id: 'pt-layer' });
119
+ viewer.addLayer(layer);
120
+
121
+ // 单个 Point + Popup
122
+ const v = new ANOVGIS.Point(position, {
123
+ color: ANOVGIS.Color.WHITE,
124
+ pixelSize: 12,
125
+ outlineColor: ANOVGIS.Color.BLUE,
126
+ outlineWidth: 2,
127
+ label: { text: '文字标签', font: '25px sans-serif', color: '#ffff00',
128
+ clampToGround: true, verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
129
+ pixelOffset: new Cesium.Cartesian2(0, -12) }
130
+ });
131
+ v.bindPopup({ viewer, showTitle: true, useDefaultHtmlTemplate: true });
132
+ v.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
133
+ v.popup.show(e.position, e.graphic.infoData);
134
+ });
135
+ layer.addGraphic(v);
136
+
137
+ viewer.flyTo(layer);
138
+ ```
139
+
140
+ > `VectorLayer` 本质是 `CustomDataSource`,可与 Cesium 原生 Entity 混用;可加 `Point` / `Polyline` / `Polygon` / `Model` / `Billboard` / `Label`。
141
+
142
+ ### S4. GeoJsonLayer.toVectorLayer() 完整链
143
+
144
+ ```js
145
+ // 来源: examples/public/examples/html/geojsonLayer/cluster.html (L36-L60)
146
+ // + graphics/divInfo2.html (L269-L294)
147
+ let layer = new ANOVGIS.GeoJsonLayer('../modelData/json/shanxi2.json');
148
+ layer = await layer.toVectorLayer(); // 拿到统一 VectorLayer
149
+ viewer.addLayer(layer);
150
+ viewer.flyTo(layer);
151
+
152
+ layer.eachGraphic(v => { // v 是 Polygon / Polyline / Billboard
153
+ let poss = [].concat(...v.positions, v.positions[0]); // 闭合环
154
+ v.setOptions({
155
+ material: Cesium.Color.fromCssColorString('#1b3377'),
156
+ fill: true,
157
+ polyline: { positions: poss, width: 1,
158
+ material: Cesium.Color.fromCssColorString('#52b3f0') }
159
+ });
160
+ });
161
+ ```
162
+
163
+ > `toVectorLayer()` 内部按 entity 类型分派 `Billboard / Point / Polyline / Polygon / Label`;模型点位用 `toModelLayer(modelUrl)` 替身。
164
+
165
+ ### S5. VectorLayer bindPopup / highlight / tooltip 三件套
166
+
167
+ ```js
168
+ // 来源: examples/public/examples/html/event/highlight.html (L60-L132)
169
+ const layer = new ANOVGIS.VectorLayer({ id: 'layer' });
170
+ viewer.addLayer(layer);
171
+
172
+ const pointOption = {
173
+ color: ANOVGIS.Color.WHITE, pixelSize: 12,
174
+ outlineColor: ANOVGIS.Color.BLUE, outlineWidth: 2,
175
+ label: { text: '文字标签', font: '25px sans-serif', color: '#ffff00',
176
+ clampToGround: true, verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
177
+ pixelOffset: new Cesium.Cartesian2(0, -12) },
178
+ infoData: { title: '标题',
179
+ fieldsMap: [{ nameC: '编号', value: 123 },
180
+ { nameC: '姓名', value: '单位名称' }] },
181
+ highlight: { pixelSize: 17, outlineColor: '#ff0000', outlineWidth: 3 } // 滑过样式
182
+ };
183
+
184
+ positions.forEach((p, i) => {
185
+ const opt = Cesium.clone(pointOption, true);
186
+ opt.infoData.title = `第${i}个标题`;
187
+ const point = new ANOVGIS.Point(p, opt);
188
+ point.bindTooltip({ viewer, showTitle: true, offsetY: '-10', useDefaultHtmlTemplate: true });
189
+ point.addEventListener(ANOVGIS.MouseEventType.MOUSE_MOVE, (e) => {
190
+ point.tooltip.show(e.position, e.graphic.infoData);
191
+ });
192
+ layer.addGraphic(point);
193
+ });
194
+
195
+ layer.bindPopup({ viewer, showTitle: true });
196
+ layer.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
197
+ layer.popup.show(e.position, e.graphic.infoData);
198
+ });
199
+ viewer.flyTo(layer);
200
+ ```
201
+
202
+ > `infoData` 字段是默认 Popup / Tooltip 模板的输入;非默认模板时 `popup.show(pos, '<html-string>')`。
203
+
204
+ ### S6. CzmlLayer 时序数据 + clock 配置
205
+
206
+ ```js
207
+ // 来源: SDK src/layer/graphic/CzmlLayer.js (L1-L43) + Cesium CzmlDataSource 标准用法
208
+ const layer = new ANOVGIS.CzmlLayer({ url: '/data/path.czml' });
209
+ viewer.addLayer(layer);
210
+
211
+ // 时序播放:把数据自带 clock 设到 viewer
212
+ layer.eachGraphic(() => {}); // 先触发 load 解析(可选)
213
+ viewer.clock.shouldAnimate = true;
214
+ viewer.clock.multiplier = 2; // 2× 速
215
+ viewer.timeline.zoomTo(layer._nativeObjCesium // Cesium Promise CzmlDataSource
216
+ ? layer._nativeObjCesium.clock.startTime
217
+ : Cesium.JulianDate.now(),
218
+ layer._nativeObjCesium.clock.stopTime);
219
+ ```
220
+
221
+ > CZML 自带 `availability` / `epoch` 字段,会自动生成 `Clock`;加载完成后 `viewer.dataSources` 内已注册,无需手动播放控制;若想停表动画 `viewer.clock.shouldAnimate = false`。
222
+
223
+ ### S7. TilesetLayer 3D Tiles + ready 回调 + click 拾取
224
+
225
+ ```js
226
+ // 来源: examples/public/examples/html/event/tilesetpopup.html (L48-L108) + src/graphic/model/Tileset.js
227
+ const layer = new ANOVGIS.TilesetLayer();
228
+ viewer.addLayer(layer);
229
+
230
+ const tileset = new ANOVGIS.Tileset({
231
+ url: buildConfig.gisStatic + 'model/tileset.json',
232
+ highlight: { color: '#ff0000' }, // 滑过
233
+ selectedHighlight: { color: 'rgba(255,0,255,0.8)' } // 选中
234
+ });
235
+ tileset.ready(ts => { // 加载完成回调
236
+ ts.pointShadowing.enabled = true;
237
+ console.log('tileset ready', ts);
238
+ });
239
+ layer.addGraphic(tileset);
240
+
241
+ layer.bindPopup({ viewer, showTitle: true, useDefaultHtmlTemplate: true });
242
+ layer.bindTooltip({ viewer, showTitle: false, offsetY: '-10', useDefaultHtmlTemplate: true });
243
+ layer.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
244
+ layer.popup.show(e.position, {
245
+ title: e.feature.getProperty('name1'),
246
+ fieldsMap: [{ nameC: '楼层', value: e.feature.getProperty('floor') }]
247
+ });
248
+ });
249
+ layer.addEventListener(ANOVGIS.MouseEventType.MOUSE_MOVE, (e) => {
250
+ layer.tooltip.show(e.position, e.feature.getProperty('name1'));
251
+ });
252
+ viewer.flyToTileset(tileset, 2, 0.5, -35);
253
+ ```
254
+
255
+ > Cesium 1.143 已删除 `readyEvent`,统一 `Cesium3DTileset.fromUrl()` 返回 Promise;SDK 用 `tileset.ready(cb)` 兜底。
256
+
257
+ <a id="s8"></a>
258
+ ### S8. ClusterLayer 聚合 + 自定义图标
259
+
260
+ ```js
261
+ // 来源: examples/public/examples/html/geojsonLayer/cluster.html (L87-L153)
262
+ // + SDK src/layer/graphic/ClusterLayer.js
263
+ const layer = new ANOVGIS.ClusterLayer({
264
+ style: 'circle', // 'circle' | 'custom' | 其他(蛛网)
265
+ pixelRange: 40,
266
+ gradient: { 0.0001: Cesium.Color.fromCssColorString('#928875'),
267
+ 0.001: Cesium.Color.fromCssColorString('#928875'),
268
+ 0.01: Cesium.Color.fromCssColorString('#928875'),
269
+ 0.1: Cesium.Color.fromCssColorString('#928875') },
270
+ fontColor: Cesium.Color.fromCssColorString('#f0f0f0'),
271
+ boderColor: Cesium.Color.fromCssColorString('#ffd673'),
272
+ outColor: Cesium.Color.fromCssColorString('#ffd673').withAlpha(0.2),
273
+ });
274
+ viewer.addLayer(layer);
275
+
276
+ data.forEach(v => {
277
+ const pos = `${v.longitude},${v.latitude},0`;
278
+ layer.addGraphic(new ANOVGIS.Billboard(pos, { image: './assets/camera.png' }));
279
+ });
280
+
281
+ layer.bindPopup({ viewer, showTitle: false, useDefaultHtmlTemplate: true });
282
+ layer.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
283
+ layer.popup.show(e.position, e.graphic.name);
284
+ });
285
+ // 聚合点点击:缩放到子项
286
+ viewer.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
287
+ const ce = e.clusterEntity;
288
+ if (ce) {
289
+ const target = ce.id || ce.label?.id;
290
+ if (target) viewer.flyTo(target, 2, 0, -90, 0);
291
+ }
292
+ });
293
+ ```
294
+
295
+ > `style: 'custom'` 时 `gradient` 的 value 直接当 `image` 字符串(base64 dataURL 或 URL);`enableCluster = false` 可关聚合。
296
+
297
+ <a id="s9"></a>
298
+ ### S9. HtmlLayer DOM 叠加 + 绝对定位 div
299
+
300
+ ```js
301
+ // 来源: examples/public/examples/html/graphics/divInfo.html (L111-L135)
302
+ // + graphics/divInfo2.html (L165-L268)
303
+ const layer = new ANOVGIS.HtmlLayer({ layerName: 'info' });
304
+ viewer.addLayer(layer);
305
+
306
+ const div = new ANOVGIS.DivIcon(
307
+ new ANOVGIS.Position(116.38105869, 39.10115627, 1),
308
+ `<div class="boxbg">
309
+ <img class="div-img" src="../modelData/images/gyqy.png" />
310
+ <div class="div-right">天然纤维是自然界原有的...</div>
311
+ <div class="div-bottom">
312
+ <div class="div-info">河北棉花</div>
313
+ <div class="div-info">新疆棉花</div>
314
+ </div>
315
+ </div>`
316
+ );
317
+ div.setOptions({
318
+ horizontalOrigin: 'CENTER', verticalOrigin: 'TOP', // 对齐锚点
319
+ offsetX: 0, offsetY: -60,
320
+ pointerEvents: true, // 开启后 DOM 可交互
321
+ scaleByDistance: { near: 0, nearValue: 1.0, far: 10000, farValue: 0.0 }
322
+ });
323
+ div.addEventListener(ANOVGIS.MouseEventType.CLICK, () => alert('点击了'));
324
+ layer.addGraphic(div);
325
+
326
+ viewer.flyTo(layer, 3, 0, -60, 100); // 自动取所有 graphic 位置 flyTo
327
+ ```
328
+
329
+ > `HtmlLayer` 通过 `scene.postRender` 每帧把 `cartesianToCanvasCoordinates` 写到 DOM `transform`;DOM 节点建议 < 1000,否则掉帧。`pointerEvents:false` 时 DOM 不接收事件,鼠标穿透到 Cesium 拾取。
330
+
331
+ ### S10. Primitive 版海量点 marker — `geojsonLayer/primitiveMarker.html` L23-L227
332
+
333
+ ```js
334
+ // 来源: primitiveMarker.html L171-217
335
+ // 用 Cesium 原生 PointPrimitiveCollection 做海量点(不走 SDK Entity,性能更好)
336
+ const collection = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection());
337
+ function addPoint([lng, lat, height], style) {
338
+ collection.add({
339
+ position: Cesium.Cartesian3.fromDegrees(lng, lat, height || 0),
340
+ color: Cesium.Color.fromCssColorString(style.color),
341
+ pixelSize: style.pixelSize,
342
+ outlineColor: Cesium.Color.fromCssColorString(style.outlineColor),
343
+ outlineWidth: style.outlineWidth,
344
+ });
345
+ }
346
+ // 备选(被注释): ANOVGIS.ApplyApi.createClusterLayer / createPointLayer
347
+ // ANOVGIS.ApplyApi.createClusterLayer(viewer).createCluster({
348
+ // pointList, gradient: ['rgba(186,53,0,0.8)', ...], infoHtml,
349
+ // isHover, isClick, pointClick
350
+ // });
351
+ ```
352
+
353
+ ### S11. 矢量墙体(Wall) — `geojsonLayer/wall.html` L23-L84
354
+
355
+ ```js
356
+ // 来源: wall.html L35-66
357
+ // 用 viewer.entities 原生 + WallFlowMaterialProperty 流光
358
+ const wallEntity = viewer.entities.add({
359
+ name: '矢量墙体',
360
+ wall: {
361
+ positions: Cesium.Cartesian3.fromDegreesArray(position),
362
+ maximumHeights: new Array(positions.length).fill(1000),
363
+ minimumHeights: new Array(positions.length).fill(0),
364
+ material: Cesium.Color.fromCssColorString('rgba(255,255,0,0.5)'),
365
+ outline: true,
366
+ outlineColor: Cesium.Color.fromCssColorString('rgba(255,255,0,1)'),
367
+ },
368
+ });
369
+ viewer.flyTo(wallEntity);
370
+ ```
371
+
372
+ ### S12. 墙体楼块(双层渲染) — `geojsonLayer/wallBuilds.html` L23-L180
373
+
374
+ ```js
375
+ // 来源: wallBuilds.html L72-L177
376
+ // 演示墙体(Entity + WallFlowMaterialProperty) + 楼顶(GeoJsonDataSource) 双层
377
+ function addWall(positions, entity) {
378
+ for (let cor of positions) {
379
+ const wallObj = {
380
+ name: entity.name,
381
+ wall: {
382
+ positions: Cesium.Cartesian3.fromDegreesArray(handleCoordinates(cor.coordinates)),
383
+ maximumHeights: new Array(cor.coordinates.length).fill(cor.height),
384
+ minimumHeights: new Array(cor.coordinates.length).fill(0),
385
+ }
386
+ };
387
+ if (entity.isCustomMaterial && entity.material) {
388
+ wallObj.wall.material = entity.material;
389
+ } else if (!entity.isGradient) {
390
+ wallObj.wall.material = Cesium.Color.fromCssColorString(entity.color);
391
+ } else {
392
+ wallObj.wall.material = new ANOVGIS.WallFlowMaterialProperty({
393
+ color: Cesium.Color.fromCssColorString(entity.color),
394
+ duration: 2500
395
+ });
396
+ }
397
+ viewer.entities.add(wallObj);
398
+ }
399
+ }
400
+ function addRoof(geojson, buildObj) {
401
+ Cesium.GeoJsonDataSource.load(geojson).then((datasource) => {
402
+ viewer.dataSources.add(datasource);
403
+ for (let entity of datasource.entities.values) {
404
+ const height = entity.properties['height'].getValue();
405
+ entity.polygon.height = height * floorHeight;
406
+ entity.polygon.material = Cesium.Color.fromCssColorString(buildObj.color);
407
+ entity.polygon.outline = buildObj.isOutline;
408
+ entity.polygon.outlineColor = Cesium.Color.fromCssColorString(buildObj.outlineColor);
409
+ }
410
+ });
411
+ }
412
+ ```
413
+
414
+ <a id="s13"></a>
415
+ ### S13. 天地图 WFS GeoJSON — `offlinemap/wfs.html` L29-L82
416
+
417
+ ```js
418
+ // 来源: wfs.html L42-82
419
+ // 直接 Cesium.GeoJsonDataSource.load(不走 SDK helper),适合"想上色时仍可用原生 API"
420
+ window.viewer = new ANOVGIS.Viewer('cesiumContainer');
421
+ viewer.imageryLayers.addImageryProvider(
422
+ ANOVGIS.ImageryLayerFactory.createImageryLayer(ANOVGIS.ImageryType.TDT, {
423
+ style: 'img', key: globalConfig.tdtKey
424
+ })
425
+ );
426
+
427
+ function buildWfsUrl(config) {
428
+ const params = new URLSearchParams({
429
+ service: 'WFS', version: config.version, request: 'GetFeature',
430
+ typeName: config.layers, outputFormat: config.outputFormat, srsName: 'EPSG:4326',
431
+ });
432
+ return `${config.url}?${params.toString()}`;
433
+ }
434
+ const wfsConfig = {
435
+ url: 'http://gisserver.tianditu.gov.cn/TDTService/wfs',
436
+ layers: 'LRRL', // LRRL=铁路, LRDL=公路
437
+ version: '1.1.0', outputFormat: 'application/json',
438
+ };
439
+ const geoJsonData = await (await fetch(buildWfsUrl(wfsConfig))).json();
440
+ const dataSource = await Cesium.GeoJsonDataSource.load(geoJsonData, {
441
+ stroke: Cesium.Color.BLUE, strokeWidth: 2, strokeOpacity: 0.8
442
+ });
443
+ viewer.dataSources.add(dataSource);
444
+ ```
445
+
446
+ ### S14. 河南高铁(虚实线 + layer 级 popup) — `offlinemap/railway.html` L116-L299
447
+
448
+ ```js
449
+ // 来源: railway.html L127-L285
450
+ // 关键: dashPattern 区分隧道(虚线) / 市区 / 山区 / 平原 + layer 级 bindPopup
451
+ const railLayer = new ANOVGIS.VectorLayer();
452
+ viewer.addLayer(railLayer);
453
+ railLayer.bindPopup({ viewer, showTitle: true, horizontalOrigin: 'CENTER', verticalOrigin: 'TOP', useDefaultHtmlTemplate: true });
454
+ railLayer.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
455
+ if (e.graphic) railLayer.popup.show(e.position, e.graphic.infoData);
456
+ });
457
+
458
+ geoJsonData.features.forEach(feature => {
459
+ const props = feature.properties;
460
+ const isTunnel = props.tunnel === 'yes';
461
+ const lineDash = isTunnel ? [20, 10] : undefined; // 虚线 = 隧道
462
+ const infoData = {
463
+ title: '高铁线路信息',
464
+ fieldsMap: [
465
+ { nameC: '线路名称:', value: props.name || '高铁线' },
466
+ { nameC: '距市区:', value: props.distance + '米' },
467
+ ],
468
+ };
469
+ coords.forEach(seg => {
470
+ const polyline = new ANOVGIS.Polyline(seg, {
471
+ material: lineColor, width: lineWidth,
472
+ dashPattern: lineDash, // 虚实线
473
+ infoData,
474
+ });
475
+ railLayer.addGraphic(polyline);
476
+ });
477
+ });
478
+ viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(113.5, 34.0, 900000) });
479
+ ```
480
+
481
+ ### S15. SuperMap iServer WMS + bindPopup — `offlinemap/iServerWmsCGI.html` L24-L90
482
+
483
+ ```js
484
+ // 来源: iServerWmsCGI.html L27-89
485
+ // 关键: getFeatureInfoParameters 触发 GetFeatureInfo,selectedHighlight 点击高亮 + 弹字段表
486
+ const wms = ANOVGIS.ImageryLayerFactory.createImageryLayer(ANOVGIS.ImageryType.WMS, {
487
+ url: 'http://110.6.113.2:8090/iserver/services/map-nongda2608/wms111/CGI',
488
+ layers: '0',
489
+ parameters: { transparent: true, format: 'image/png' },
490
+ rectangle: Cesium.Rectangle.fromDegrees(119.799, 49.270, 120.110, 49.503),
491
+ getFeatureInfoParameters: { feature_count: 10 },
492
+ selectedHighlight: { material: 'rgba(255,255,0,0.6)', height: 0.01 },
493
+ });
494
+ wms.bindPopup({ viewer, showTitle: true, useDefaultHtmlTemplate: true });
495
+ wms.addEventListener(ANOVGIS.MouseEventType.CLICK, (e) => {
496
+ wms.popup.show(e.position, {
497
+ title: 'CGI 点击查询',
498
+ fieldsMap: Object.keys(e.feature.data.properties || {}).map(key => ({
499
+ nameC: key + ':', value: e.feature.data.properties[key] || '未知',
500
+ })),
501
+ });
502
+ });
503
+ viewer.imageryLayers.addImageryProvider(wms);
504
+ viewer.flyTo(wms);
505
+ ```
506
+
507
+ ### S16. GeoJSON 点位 billboard(交替图片) — `geojsonLayer/billboard.html` L65-L101
508
+
509
+ ```js
510
+ // 来源: billboard.html L65-L101
511
+ // 注:本 demo 未用 GeoJsonLayer,直接遍历 points 数组 + viewer.entities.add
512
+ function showPoints(points) {
513
+ const billboard1 = { image: '../modelData/images/upRule.png' };
514
+ const billboard2 = { image: '../modelData/images/underRule.png' };
515
+ let i = 1;
516
+ for (let position of points) {
517
+ if (i > 1000) break;
518
+ let lng = +position.lnglat[0], lat = +position.lnglat[1];
519
+ const entity = viewer.entities.add({
520
+ position: Cesium.Cartesian3.fromDegrees(lng, lat),
521
+ billboard: i % 2 === 0 ? billboard2 : billboard1, // 交替图片
522
+ });
523
+ i++;
524
+ }
525
+ }
526
+ ```
527
+
528
+ ### S17. GeoJSON 折线(原生命码) — `geojsonLayer/marker.html` L63-L74
529
+
530
+ ```js
531
+ // 来源: marker.html L63-L74
532
+ // 注:文件名 marker.html 但内容是 polyline 演示
533
+ Cesium.GeoJsonDataSource.load(geojson).then((datasource) => {
534
+ viewer.dataSources.add(datasource);
535
+ for (let entity of datasource.entities.values) {
536
+ entity.polyline.width = 1;
537
+ entity.polyline.material = Cesium.Color.fromCssColorString('rgba(255,255,0,0.5)');
538
+ }
539
+ });
540
+ ```
541
+
542
+ ### S18. GeoJSON 多边形楼块(基础版,绝对高度) — `geojsonLayer/polygonBuilds.html` L233-L264
543
+
544
+ ```js
545
+ // 来源: polygonBuilds.html L233-L264
546
+ // 关键: 绝对高度(不贴地),适合无地形场景避免建筑漂移
547
+ Cesium.GeoJsonDataSource.load(geojson).then((datasource) => {
548
+ viewer.dataSources.add(datasource);
549
+ for (let entity of datasource.entities.values) {
550
+ let height = 0;
551
+ entity.polygon.height = 0; // 距椭球面 0 米
552
+ if (entity.properties.hasOwnProperty('height')) {
553
+ const val = entity.properties['height'].getValue();
554
+ if (!isNaN(val)) height = val - 0;
555
+ }
556
+ entity.polygon.extrudedHeight = height; // 从属性读拉伸高度
557
+ entity.polygon.outline = true;
558
+ entity.polygon.material = Cesium.Color.fromCssColorString('rgba(62,171,107,0.8)');
559
+ entity.polygon.outlineColor = Cesium.Color.fromCssColorString('rgba(62,171,107,0.4)');
560
+ }
561
+ });
562
+ ```
563
+
564
+ ### S19. GeoJSON 多边形楼块(贴地 + floorHeight 倍率) — `geojsonLayer/polygonBuildsMaterial.html` L64-L89
565
+
566
+ ```js
567
+ // 来源: polygonBuildsMaterial.html L64-89
568
+ // 关键: HeightReference.RELATIVE_TO_GROUND + extrudedHeight = height * 3(floorHeight)
569
+ const floorHeight = 3;
570
+ Cesium.GeoJsonDataSource.load(geojson).then((datasource) => {
571
+ viewer.dataSources.add(datasource);
572
+ for (let entity of datasource.entities.values) {
573
+ let height = 0;
574
+ entity.polygon.heightReference = Cesium.HeightReference.RELATIVE_TO_GROUND;
575
+ entity.polygon.height = 0;
576
+ entity.polygon.extrudedHeightReference = Cesium.HeightReference.RELATIVE_TO_GROUND;
577
+ if (entity.properties.hasOwnProperty('height')) {
578
+ const val = entity.properties['height'].getValue();
579
+ if (!isNaN(val)) height = val - 0;
580
+ }
581
+ entity.polygon.extrudedHeight = height * floorHeight; // 按层高 3 米拉伸
582
+ entity.polygon.outline = true;
583
+ entity.polygon.material = Cesium.Color.fromCssColorString('rgba(62,171,107,0.8)');
584
+ entity.polygon.outlineColor = Cesium.Color.fromCssColorString('rgba(62,171,107,0.4)');
585
+ }
586
+ });
587
+ ```
588
+
589
+ ### S20. GeoJSON 多边形楼块(自定义 GLSL 材质) — `geojsonLayer/polygonBuildsMaterial.html` L83-L122
590
+
591
+ ```js
592
+ // 来源: polygonBuildsMaterial.html L83-L122
593
+ // 关键: 给每个 polygon 应用自定义 Cesium.Material,fabric 内嵌 GLSL 按 UV 输出颜色
594
+ entity.polygon.outline = false; // 关闭 outline
595
+ entity.polygon.material = new Cesium.Material({
596
+ translucent: false,
597
+ fabric: {
598
+ uniforms: {
599
+ // image / gradient / decoration 三个图片 uniform 按需填
600
+ },
601
+ source: `
602
+ czm_material czm_getMaterial(czm_materialInput materialInput) {
603
+ czm_material material = czm_getDefaultMaterial(materialInput);
604
+ // 按立面 UV 输出颜色(st ∈ [0,1])
605
+ material.diffuse = vec3(materialInput.st, 1.0);
606
+ material.alpha = 1.0;
607
+ return material;
608
+ }
609
+ `,
610
+ },
611
+ });
612
+ ```
613
+
614
+ ## 实战最佳实践
615
+
616
+ 1. **`TilesetLayer` 容器 + `Tileset` 实例**:必须 `layer.addGraphic(tileset)` 之后才有 popup / 事件;定位用 `viewer.flyToTileset(tileset, duration, offsetX, pitch)`。
617
+ 2. **GeoJSON 转 `VectorLayer`**:`geoJsonLayer.toVectorLayer()` 拿到统一容器;模型点位用 `toModelLayer(modelUrl)`。
618
+ 3. **`ClusterLayer` 配色**:`gradient: { 阈值: 颜色, ... }` 按"占比"分档;`style: 'custom'` 时 value 直接当图片。
619
+ 4. **`HtmlLayer` 性能**:DOM 节点 < 1000 较稳;大量点位改用 `LabelLayer` 或 `PrimitiveLayer.labels`。
620
+ 5. **`FeatureGridLayer` 适用场景**:海量点位按视野瓦片分片拉取,`callback` 里自行绘制,避免一次性加载全量。
621
+
622
+ ## 参考
623
+
624
+ - 图层基类:`src/layer/Layer.js`
625
+ - 要素图层:`src/layer/graphic/`
626
+ - 3D Tiles 封装:`ANOVGIS.Tileset` (`src/graphic/model/Tileset.js`)
627
+ - 业务封装:`src/applyfn/applyBase.js`