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

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 +618 -0
  2. package/INSTALL.md +478 -0
  3. package/LICENSE +15 -0
  4. package/README.md +148 -2
  5. package/RELEASING.md +160 -0
  6. package/bin/anov-gis-skills.mjs +408 -0
  7. package/lib/mcp-config.mjs +273 -0
  8. package/package.json +42 -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,389 @@
1
+ ---
2
+ name: cesiumjs-entities
3
+ description: "CesiumJS entities and data sources - Entity, EntityCollection, DataSource, GeoJsonDataSource, KmlDataSource, CzmlDataSource, Graphics types, Visualizers. Use when adding points, labels, models, polygons, or polylines to the map, loading GeoJSON/KML/CZML/GPX data, or working with the high-level Entity API."
4
+ ---
5
+ # CesiumJS Entities & DataSources
6
+
7
+ > **Version baseline:** CesiumJS 1.142 -- ES module imports: `import { ... } from "cesium";`
8
+ > **Ownership rule:** `*Graphics` classes belong here; `*Geometry` classes belong in cesiumjs-primitives. Properties (SampledProperty, CallbackProperty, MaterialProperty subtypes) belong in cesiumjs-time-properties.
9
+
10
+ ## Architecture
11
+
12
+ The Entity API is the high-level, data-driven layer of CesiumJS. Entities combine position, orientation, and one or more Graphics types into a single object managed by an EntityCollection. DataSources load external formats (GeoJSON, KML, CZML, GPX) and populate an EntityCollection automatically. Visualizers and GeometryUpdaters translate Entity descriptions into Primitives each frame.
13
+
14
+ ```
15
+ DataSource --> EntityCollection --> Entity
16
+ |-- position / orientation
17
+ |-- billboard / point / label / model / polygon / polyline / ...
18
+ |-- properties (PropertyBag)
19
+ ```
20
+
21
+ - `viewer.entities` is a shortcut to the default DataSource's EntityCollection
22
+ - `viewer.dataSources` holds all loaded DataSources; each owns an `EntityCollection`
23
+
24
+ ## Entity Basics
25
+
26
+ ### Point
27
+
28
+ ```javascript
29
+ import { Viewer, Cartesian3, Color, HeightReference } from "cesium";
30
+
31
+ const viewer = new Viewer("cesiumContainer");
32
+ const entity = viewer.entities.add({
33
+ id: "my-point", // optional; auto-generated GUID if omitted
34
+ name: "Sample Point",
35
+ position: Cartesian3.fromDegrees(-75.59777, 40.03883),
36
+ point: {
37
+ pixelSize: 10,
38
+ color: Color.YELLOW,
39
+ outlineColor: Color.BLACK,
40
+ outlineWidth: 2,
41
+ heightReference: HeightReference.CLAMP_TO_GROUND,
42
+ },
43
+ });
44
+ viewer.zoomTo(entity);
45
+ ```
46
+
47
+ ### Billboard with Label
48
+
49
+ ```javascript
50
+ import { Viewer, Cartesian3, Cartesian2, Color, VerticalOrigin, HeightReference, LabelStyle } from "cesium";
51
+
52
+ const viewer = new Viewer("cesiumContainer");
53
+ viewer.entities.add({
54
+ position: Cartesian3.fromDegrees(-122.4175, 37.7749),
55
+ billboard: {
56
+ image: "/assets/marker.png",
57
+ scale: 0.5,
58
+ verticalOrigin: VerticalOrigin.BOTTOM,
59
+ heightReference: HeightReference.CLAMP_TO_GROUND,
60
+ },
61
+ label: {
62
+ text: "San Francisco",
63
+ font: "14px sans-serif",
64
+ fillColor: Color.WHITE,
65
+ outlineColor: Color.BLACK,
66
+ outlineWidth: 2,
67
+ style: LabelStyle.FILL_AND_OUTLINE,
68
+ pixelOffset: new Cartesian2(0, -36),
69
+ heightReference: HeightReference.CLAMP_TO_GROUND,
70
+ },
71
+ });
72
+ ```
73
+
74
+ ### Polygon (flat, extruded, with holes)
75
+
76
+ ```javascript
77
+ import { Viewer, Cartesian3, Color, PolygonHierarchy } from "cesium";
78
+
79
+ const viewer = new Viewer("cesiumContainer");
80
+ // Flat polygon
81
+ viewer.entities.add({
82
+ polygon: {
83
+ hierarchy: Cartesian3.fromDegreesArray([-115, 37, -115, 32, -107, 33, -102, 31, -102, 35]),
84
+ material: Color.RED.withAlpha(0.5),
85
+ outline: true,
86
+ outlineColor: Color.BLACK,
87
+ },
88
+ });
89
+ // Extruded polygon with hole
90
+ viewer.entities.add({
91
+ polygon: {
92
+ hierarchy: new PolygonHierarchy(
93
+ Cartesian3.fromDegreesArray([-99, 30, -85, 30, -85, 40, -99, 40]),
94
+ [new PolygonHierarchy(Cartesian3.fromDegreesArray([-97, 32, -87, 32, -87, 38, -97, 38]))]
95
+ ),
96
+ extrudedHeight: 300000,
97
+ material: Color.BLUE.withAlpha(0.6),
98
+ closeTop: true,
99
+ closeBottom: true,
100
+ },
101
+ });
102
+ ```
103
+
104
+ ### Polyline
105
+
106
+ ```javascript
107
+ import { Viewer, Cartesian3, Color, ArcType } from "cesium";
108
+
109
+ const viewer = new Viewer("cesiumContainer");
110
+ viewer.entities.add({
111
+ polyline: {
112
+ positions: Cartesian3.fromDegreesArray([-75, 35, -125, 35]),
113
+ width: 5,
114
+ material: Color.RED,
115
+ arcType: ArcType.GEODESIC,
116
+ clampToGround: true,
117
+ },
118
+ });
119
+ ```
120
+
121
+ ### 3D Model
122
+
123
+ ```javascript
124
+ import { Viewer, Cartesian3, HeadingPitchRoll, Transforms, Math as CesiumMath, Color } from "cesium";
125
+
126
+ const viewer = new Viewer("cesiumContainer");
127
+ const position = Cartesian3.fromDegrees(-123.075, 44.045, 5000);
128
+ const hpr = new HeadingPitchRoll(CesiumMath.toRadians(135), 0, 0);
129
+
130
+ viewer.entities.add({
131
+ position,
132
+ orientation: Transforms.headingPitchRollQuaternion(position, hpr),
133
+ model: {
134
+ uri: "/assets/CesiumAir/Cesium_Air.glb",
135
+ minimumPixelSize: 64,
136
+ maximumScale: 20000,
137
+ silhouetteColor: Color.RED,
138
+ silhouetteSize: 2,
139
+ },
140
+ });
141
+ ```
142
+
143
+ ### Box, Cylinder, Ellipsoid, Ellipse
144
+
145
+ ```javascript
146
+ import { Viewer, Cartesian3, Color } from "cesium";
147
+ const viewer = new Viewer("cesiumContainer");
148
+
149
+ viewer.entities.add({ // Box
150
+ position: Cartesian3.fromDegrees(-107, 40, 300000),
151
+ box: { dimensions: new Cartesian3(400000, 300000, 500000), material: Color.BLUE.withAlpha(0.5) },
152
+ });
153
+ viewer.entities.add({ // Cylinder (topRadius: 0 for a cone)
154
+ position: Cartesian3.fromDegrees(-100, 40, 200000),
155
+ cylinder: { length: 400000, topRadius: 200000, bottomRadius: 200000, material: Color.GREEN.withAlpha(0.5) },
156
+ });
157
+ viewer.entities.add({ // Ellipsoid (sphere when all radii equal)
158
+ position: Cartesian3.fromDegrees(-93, 40, 300000),
159
+ ellipsoid: { radii: new Cartesian3(200000, 200000, 300000), material: Color.RED.withAlpha(0.5) },
160
+ });
161
+ viewer.entities.add({ // Ellipse (circle when axes equal)
162
+ position: Cartesian3.fromDegrees(-86, 40),
163
+ ellipse: { semiMajorAxis: 300000, semiMinorAxis: 300000, material: Color.PURPLE.withAlpha(0.5) },
164
+ });
165
+ ```
166
+
167
+ ### Corridor, Rectangle, Wall
168
+
169
+ ```javascript
170
+ import { Viewer, Cartesian3, Color, Rectangle, CornerType } from "cesium";
171
+ const viewer = new Viewer("cesiumContainer");
172
+
173
+ viewer.entities.add({ // Corridor: path with width
174
+ corridor: { positions: Cartesian3.fromDegreesArray([-80, 40, -90, 40, -90, 35]), width: 200000, material: Color.ORANGE.withAlpha(0.6), cornerType: CornerType.ROUNDED },
175
+ });
176
+ viewer.entities.add({ // Rectangle by geographic bounds
177
+ rectangle: { coordinates: Rectangle.fromDegrees(-110, 20, -80, 25), material: Color.GREEN.withAlpha(0.5), extrudedHeight: 50000 },
178
+ });
179
+ viewer.entities.add({ // Wall: vertical curtain
180
+ wall: { positions: Cartesian3.fromDegreesArrayHeights([-115, 44, 200000, -90, 44, 200000]), minimumHeights: [100000, 100000], material: Color.CYAN.withAlpha(0.7) },
181
+ });
182
+ ```
183
+
184
+ ## EntityCollection Operations
185
+
186
+ ```javascript
187
+ viewer.entities.getById("my-point"); // retrieve by ID
188
+ viewer.entities.getOrCreateEntity("some-id"); // get or create
189
+ viewer.entities.values; // Entity[] (read-only)
190
+ viewer.entities.remove(entity); // remove by reference
191
+ viewer.entities.removeById("my-point"); // remove by ID
192
+ viewer.entities.removeAll(); // clear all
193
+
194
+ // Batch updates -- suspend events for bulk add/remove
195
+ viewer.entities.suspendEvents();
196
+ for (let i = 0; i < 1000; i++) viewer.entities.add({ /* ... */ });
197
+ viewer.entities.resumeEvents(); // fires one collectionChanged event
198
+
199
+ viewer.entities.collectionChanged.addEventListener((collection, added, removed, changed) => {
200
+ console.log(`Added: ${added.length}, Removed: ${removed.length}`);
201
+ });
202
+ ```
203
+
204
+ ## DataSources
205
+
206
+ ### GeoJSON / TopoJSON
207
+
208
+ ```javascript
209
+ import { Viewer, GeoJsonDataSource, Color } from "cesium";
210
+ const viewer = new Viewer("cesiumContainer");
211
+
212
+ // Load from URL with styling options
213
+ const ds = await GeoJsonDataSource.load("/data/counties.geojson", {
214
+ stroke: Color.HOTPINK, fill: Color.PINK.withAlpha(0.5), strokeWidth: 3, clampToGround: true,
215
+ });
216
+ viewer.dataSources.add(ds);
217
+ viewer.zoomTo(ds);
218
+
219
+ // Post-load styling: iterate and customize
220
+ for (const entity of ds.entities.values) {
221
+ if (entity.polygon) entity.polygon.material = Color.fromRandom({ alpha: 0.8 });
222
+ }
223
+ ```
224
+
225
+ `GeoJsonDataSource.load()` also accepts an inline GeoJSON object instead of a URL.
226
+
227
+ Use `GeoJsonDataSource` when you want Entities, DataSource lifecycle, clustering,
228
+ time-dynamic properties, or easy post-load per-entity styling. For very large
229
+ static GeoJSON where Entity overhead is the bottleneck, use `GeoJsonPrimitive`
230
+ from the `cesiumjs-primitives` skill instead (added in 1.142).
231
+
232
+ ### KML / KMZ
233
+
234
+ ```javascript
235
+ import { KmlDataSource } from "cesium";
236
+ const ds = await KmlDataSource.load("/data/sample.kml", {
237
+ camera: viewer.scene.camera, canvas: viewer.scene.canvas, clampToGround: true,
238
+ });
239
+ viewer.dataSources.add(ds);
240
+ viewer.flyTo(ds);
241
+ ```
242
+
243
+ ### CZML
244
+
245
+ ```javascript
246
+ import { CzmlDataSource } from "cesium";
247
+ const ds = await CzmlDataSource.load("/data/vehicle.czml");
248
+ viewer.dataSources.add(ds);
249
+ await ds.process("/data/vehicle-update.czml"); // append without clearing
250
+ ```
251
+
252
+ ### GPX
253
+
254
+ ```javascript
255
+ import { GpxDataSource } from "cesium";
256
+ const ds = await GpxDataSource.load("/data/trail.gpx");
257
+ viewer.dataSources.add(ds);
258
+ viewer.zoomTo(ds);
259
+ ```
260
+
261
+ ### CustomDataSource
262
+
263
+ ```javascript
264
+ import { CustomDataSource, Cartesian3, Color } from "cesium";
265
+
266
+ const customDs = new CustomDataSource("sensors");
267
+ customDs.entities.add({
268
+ position: Cartesian3.fromDegrees(-95, 40),
269
+ point: { pixelSize: 8, color: Color.LIME },
270
+ });
271
+ viewer.dataSources.add(customDs);
272
+ customDs.show = false; // toggle entire group visibility
273
+ ```
274
+
275
+ ## Entity Clustering
276
+
277
+ ```javascript
278
+ import { GeoJsonDataSource, Color, VerticalOrigin, PinBuilder } from "cesium";
279
+ const ds = await GeoJsonDataSource.load("/data/facilities.geojson");
280
+ viewer.dataSources.add(ds);
281
+
282
+ ds.clustering.enabled = true;
283
+ ds.clustering.pixelRange = 40;
284
+ ds.clustering.minimumClusterSize = 3;
285
+
286
+ const pinBuilder = new PinBuilder();
287
+ ds.clustering.clusterEvent.addEventListener((entities, cluster) => {
288
+ cluster.label.show = false;
289
+ cluster.billboard.show = true;
290
+ cluster.billboard.verticalOrigin = VerticalOrigin.BOTTOM;
291
+ cluster.billboard.image = pinBuilder.fromText(`${entities.length}`, Color.VIOLET, 48).toDataURL();
292
+ });
293
+ ```
294
+
295
+ ## Parent-Child Visibility
296
+
297
+ Setting `parent.show = false` hides all descendants without changing their individual `show` flags.
298
+
299
+ ```javascript
300
+ const parent = viewer.entities.add({ name: "Group" });
301
+ viewer.entities.add({ parent, position: Cartesian3.fromDegrees(-90, 35), point: { pixelSize: 6, color: Color.RED } });
302
+ viewer.entities.add({ parent, position: Cartesian3.fromDegrees(-91, 36), point: { pixelSize: 6, color: Color.BLUE } });
303
+
304
+ parent.show = false; // both children disappear
305
+ parent.show = true; // both reappear
306
+ ```
307
+
308
+ ## Entity Description and Custom Properties
309
+
310
+ ```javascript
311
+ // InfoBox HTML: displayed when user clicks the entity
312
+ viewer.entities.add({
313
+ name: "Station Alpha",
314
+ position: Cartesian3.fromDegrees(-75.17, 39.95),
315
+ point: { pixelSize: 12, color: Color.CYAN },
316
+ description: `<table class="cesium-infoBox-defaultTable"><tr><th>Status</th><td>Active</td></tr></table>`,
317
+ properties: { population: 500000, category: "metro" },
318
+ });
319
+ // Read custom properties
320
+ // entity.properties.population.getValue() --> 500000
321
+ ```
322
+
323
+ ## Export to KML
324
+
325
+ ```javascript
326
+ import { exportKml } from "cesium";
327
+
328
+ const result = await exportKml({ entities: viewer.entities, kmz: true });
329
+ // result.kmz is a Blob; result.kml is a string when kmz: false
330
+ const url = URL.createObjectURL(result.kmz);
331
+ ```
332
+
333
+ ## All 17 Graphics Types
334
+
335
+ | Graphics | Key Properties |
336
+ |----------|---------------|
337
+ | `PointGraphics` | pixelSize, color, outlineColor, outlineWidth |
338
+ | `BillboardGraphics` | image, scale, rotation, sizeInMeters, heightReference |
339
+ | `LabelGraphics` | text, font, fillColor, style, showBackground |
340
+ | `ModelGraphics` | uri, scale, silhouetteColor, runAnimations, colorBlendMode |
341
+ | `PolygonGraphics` | hierarchy, height, extrudedHeight, material, perPositionHeight |
342
+ | `PolylineGraphics` | positions, width, material, clampToGround, arcType |
343
+ | `EllipseGraphics` | semiMajorAxis, semiMinorAxis, rotation, extrudedHeight |
344
+ | `RectangleGraphics` | coordinates (Rectangle), height, extrudedHeight |
345
+ | `BoxGraphics` | dimensions (Cartesian3), material, outline |
346
+ | `CylinderGraphics` | length, topRadius, bottomRadius |
347
+ | `EllipsoidGraphics` | radii (Cartesian3) |
348
+ | `CorridorGraphics` | positions, width, cornerType |
349
+ | `WallGraphics` | positions, minimumHeights, maximumHeights |
350
+ | `PolylineVolumeGraphics` | positions, shape (Cartesian2[]) |
351
+ | `PlaneGraphics` | plane (Plane), dimensions (Cartesian2) |
352
+ | `PathGraphics` | resolution, leadTime, trailTime, width, `relativeTo` (experimental, 1.140+) |
353
+ | `Cesium3DTilesetGraphics` | uri |
354
+
355
+ > **`PathGraphics.relativeTo` (experimental, 1.140+, #13223):** display a path in a
356
+ > reference frame relative to *another* entity, or in a different reference frame
357
+ > than the entity's `position` `ReferenceFrame` -- e.g. draw a drone's track
358
+ > relative to a moving vehicle rather than ECEF. Marked experimental; the signature
359
+ > may change.
360
+
361
+ ## Key Enums
362
+
363
+ | Enum | Values |
364
+ |------|--------|
365
+ | `HeightReference` | NONE, CLAMP_TO_GROUND, RELATIVE_TO_GROUND |
366
+ | `HorizontalOrigin` | LEFT, CENTER, RIGHT |
367
+ | `VerticalOrigin` | TOP, CENTER, BOTTOM, BASELINE |
368
+ | `LabelStyle` | FILL, OUTLINE, FILL_AND_OUTLINE |
369
+ | `ColorBlendMode` | HIGHLIGHT, REPLACE, MIX |
370
+ | `ShadowMode` | DISABLED, ENABLED, CAST_ONLY, RECEIVE_ONLY |
371
+
372
+ ## Performance Tips
373
+
374
+ 1. **Use `suspendEvents()`/`resumeEvents()`** when bulk-adding entities to batch change notifications.
375
+ 2. **Prefer constant properties** for static data -- CesiumJS optimizes non-changing entities.
376
+ 3. **Use `DistanceDisplayCondition`** to hide entities beyond a useful range.
377
+ 4. **Switch to Primitives** for 10k+ static shapes; Primitives avoid per-entity overhead.
378
+ 5. **Enable clustering** on DataSources with many point-like entities.
379
+ 6. **Set `disableDepthTestDistance`** on billboards/labels to prevent terrain occlusion.
380
+ 7. **Minimize outlines** on ground-clamped polygons -- extra rendering passes required.
381
+ 8. **Use `clampToGround` only when needed** -- requires terrain-conforming subdivision.
382
+ 9. **Batch CZML updates** with `process()` to append, not `load()` which replaces all.
383
+ 10. **Cache PinBuilder canvases** -- reuse results to avoid regenerating identical images.
384
+
385
+ ## See Also
386
+
387
+ - **cesiumjs-time-properties** -- SampledProperty, CallbackProperty, MaterialProperty types for time-dynamic entity attributes
388
+ - **cesiumjs-primitives** -- Low-level Primitive API for performance-critical static geometry (`*Geometry` classes)
389
+ - **cesiumjs-interaction** -- ScreenSpaceEventHandler, Scene.pick, entity selection and hover patterns