@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,500 @@
1
+ ---
2
+ name: cesiumjs-camera
3
+ description: "CesiumJS camera control - Camera, flyTo, lookAt, setView, ScreenSpaceCameraController, CameraEventAggregator, flight animation. Use when positioning the camera, creating flyTo animations, constraining user navigation, tracking entities, or converting between screen and world coordinates."
4
+ ---
5
+ # CesiumJS Camera & Navigation
6
+
7
+ > **Baseline:** CesiumJS v1.142 -- ES module imports (`import { ... } from "cesium";`)
8
+
9
+ ## Camera Fundamentals
10
+
11
+ Access via `viewer.camera`. The camera has a `position` (Cartesian3 in world
12
+ coords), orientation vectors (`direction`, `up`, `right`), and a frustum.
13
+ All angles are **radians**.
14
+
15
+ Read-only computed properties: `positionWC`, `positionCartographic`,
16
+ `directionWC`, `upWC`, `rightWC`, `heading` (0 = north, clockwise), `pitch`
17
+ (negative = down), `roll`, `transform`, `viewMatrix`, `inverseViewMatrix`.
18
+
19
+ Events: `moveStart` / `moveEnd` fire when movement begins/ends. `changed`
20
+ fires when the camera moves by more than `percentageChanged` (default 0.5).
21
+
22
+ > **City views need 3D buildings.** For skyline, street-level, or urban panorama
23
+ > views, add `Cesium.createOsmBuildingsAsync()` (or Google Photorealistic 3D
24
+ > Tiles). Without 3D Tiles, cities render as flat satellite imagery -- no
25
+ > buildings, no skyline silhouette. Include a code comment to load buildings.
26
+
27
+ ### Altitude & Orientation Guidelines
28
+
29
+ Choose altitude and pitch to match the **scale of the feature** you want to show:
30
+
31
+ | View type | Altitude (m) | Pitch (deg) | Notes |
32
+ |---|---|---|---|
33
+ | **Landmark close-up** | 500 -- 1,500 | -25 to -35 | Individual buildings/structures fill the frame. Use `lookAt` with appropriate range. |
34
+ | **City panoramic / skyline** | 800 -- 1,500 | -10 to -20 | For viewing a skyline from across a river or bay. Position camera to the side, face the city. **Requires OSM Buildings or 3D Tiles** for 3D silhouette. |
35
+ | **City overview** | 2,000 -- 5,000 | -35 to -50 | Urban grid, rivers, and parks clearly visible |
36
+ | **Metro / regional** | 8,000 -- 20,000 | -60 to -90 | Entire metro area or geographic feature |
37
+ | **Canyon / cliff rim** | 50 -- 300 above rim | -15 to -25 | Use steeper pitch to reveal depth below. Near-horizontal (-5) looks flat across terrain. |
38
+ | **Country / continent** | 500,000 -- 5,000,000 | -90 | Political boundaries, coastlines |
39
+
40
+ **When the prompt says "looking at [city]" or "start at [city]"**, default to **city overview** range (2,000-5,000 m) with pitch around **-45** to **-60** degrees and heading **0** (north). This produces a clear, recognizable view where the urban layout, rivers, and landmarks are identifiable.
41
+
42
+ **Top-down views** (`pitch: -90`) are best for geographic features (canyons, coastlines, rivers) where overhead perspective reveals the distinctive shape. For cities, prefer an angled view that shows the 3D skyline.
43
+
44
+ > **Gimbal lock:** Never use `pitch: -Math.PI/2` exactly. Use
45
+ > `-(Math.PI / 2 - 0.0001)` for straight-down views to avoid singularity.
46
+
47
+ > **Ground-level views (altitude < 200 m)** require 3D Tiles. Without them,
48
+ > CesiumJS shows only sky and flat ground. Suggest a higher-altitude fallback.
49
+
50
+ > **Skyline panoramics** (across a river/bay): 800-1,500 m, pitch -10 to -20.
51
+ > **Add `Cesium.createOsmBuildingsAsync()` for 3D silhouette.** Pitch too
52
+ > horizontal (-5) at moderate altitude shows a flat grid, not a skyline.
53
+
54
+ > **Canyon / cliff rim views**: pitch -15 to -25. Near-horizontal pitch (-5 to
55
+ > -8) looks flat across terrain and misses the vertical drop.
56
+
57
+ ---
58
+
59
+ ## setView -- Instant Placement
60
+
61
+ Teleports the camera in a single frame -- no animation. Use for initial view,
62
+ mode resets, constraint setup. `destination`: `Cartesian3` or `Rectangle`.
63
+ `orientation`: `{ heading, pitch, roll }` or `{ direction, up }`.
64
+
65
+ ```js
66
+ import { Cartesian3, Math as CesiumMath } from "cesium";
67
+
68
+ // City overview: 3000 m altitude, angled view facing north
69
+ viewer.camera.setView({
70
+ destination: Cartesian3.fromDegrees(-0.1276, 51.5074, 3000.0),
71
+ orientation: {
72
+ heading: CesiumMath.toRadians(0.0), // north
73
+ pitch: CesiumMath.toRadians(-50.0), // angled down -- shows city layout clearly
74
+ roll: 0.0,
75
+ },
76
+ });
77
+ ```
78
+
79
+ ```js
80
+ import { Cartesian3, Math as CesiumMath } from "cesium";
81
+
82
+ // Canyon rim perspective: slightly above rim, looking down into the canyon
83
+ // Pitch of -20 reveals depth; near-horizontal (-5) would look flat across
84
+ viewer.camera.setView({
85
+ destination: Cartesian3.fromDegrees(-112.14, 36.06, 2400.0),
86
+ orientation: {
87
+ heading: CesiumMath.toRadians(0.0),
88
+ pitch: CesiumMath.toRadians(-20.0), // steeper pitch to show canyon depth
89
+ roll: 0.0,
90
+ },
91
+ });
92
+ ```
93
+
94
+ ```js
95
+ // Top-down geographic view -- use safe pitch to avoid gimbal lock
96
+ viewer.camera.setView({
97
+ destination: Cesium.Cartesian3.fromDegrees(-112.14, 36.06, 50000.0),
98
+ orientation: { heading: 0.0, pitch: -(Math.PI / 2 - 0.0001), roll: 0.0 },
99
+ });
100
+
101
+ // Rectangle form (top-down, orientation defaults to north/down)
102
+ viewer.camera.setView({
103
+ destination: Cesium.Rectangle.fromDegrees(-77.0, 38.0, -72.0, 42.0),
104
+ });
105
+ ```
106
+
107
+ ---
108
+
109
+ ## flyTo -- Animated Flight
110
+
111
+ Smoothly animates the camera. Returns nothing (not a Promise); use `complete`
112
+ callback. Options: `destination`, `orientation`, `duration` (seconds),
113
+ `complete`/`cancel`, `maximumHeight`, `pitchAdjustHeight`, `flyOverLongitude`.
114
+
115
+ ```js
116
+ import { Cartesian3, Math as CesiumMath } from "cesium";
117
+
118
+ // Fly to a landmark: 1500 m gives a clear view of the surrounding area
119
+ viewer.camera.flyTo({
120
+ destination: Cartesian3.fromDegrees(2.2945, 48.8584, 1500.0),
121
+ orientation: {
122
+ heading: CesiumMath.toRadians(0.0),
123
+ pitch: CesiumMath.toRadians(-35.0),
124
+ roll: 0.0,
125
+ },
126
+ duration: 3,
127
+ });
128
+ ```
129
+
130
+ ```js
131
+ import { Cartesian3, Math as CesiumMath } from "cesium";
132
+
133
+ // Chain flights using the complete callback (flyTo does NOT return a Promise)
134
+ viewer.camera.flyTo({
135
+ destination: Cartesian3.fromDegrees(-74.0445, 40.6892, 800.0),
136
+ orientation: {
137
+ heading: CesiumMath.toRadians(0.0),
138
+ pitch: CesiumMath.toRadians(-35.0),
139
+ roll: 0.0,
140
+ },
141
+ duration: 3,
142
+ complete() {
143
+ viewer.camera.flyTo({
144
+ destination: Cartesian3.fromDegrees(-73.9857, 40.758, 600.0),
145
+ orientation: {
146
+ heading: CesiumMath.toRadians(0.0),
147
+ pitch: CesiumMath.toRadians(-40.0),
148
+ roll: 0.0,
149
+ },
150
+ duration: 2,
151
+ });
152
+ },
153
+ });
154
+ ```
155
+
156
+ > **Altitude tip for tours**: keep each stop at **600 m+** so tiles and imagery
157
+ > load. Below 400 m, expect blurry tiles on fast successive flights.
158
+
159
+ ```js
160
+ // Long-distance flight: LA to Tokyo via Europe
161
+ viewer.camera.flyTo({
162
+ destination: Cesium.Cartesian3.fromDegrees(139.815, 35.714, 20000.0),
163
+ duration: 20,
164
+ flyOverLongitude: Cesium.Math.toRadians(60.0), // eastward via Europe
165
+ pitchAdjustHeight: 1000, // look down at high altitude
166
+ });
167
+ ```
168
+
169
+ Control in-progress flights with `completeFlight()` (jumps to end state) and
170
+ `cancelFlight()` (stays at current position).
171
+
172
+ ---
173
+
174
+ ## flyHome
175
+
176
+ Fly to the default view. Override with `Camera.DEFAULT_VIEW_RECTANGLE`.
177
+
178
+ ```js
179
+ import { Camera, Rectangle } from "cesium";
180
+
181
+ Camera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees(-10.0, 35.0, 40.0, 60.0);
182
+ viewer.camera.flyHome(2.0); // duration in seconds; omit for auto
183
+ ```
184
+
185
+ > **Limitation:** `flyHome()` always produces a top-down, north-up view --
186
+ > no orientation control. Workaround: intercept `viewer.homeButton.viewModel
187
+ > .command.beforeExecute`, cancel it, and call `flyTo` with custom orientation.
188
+
189
+ ---
190
+
191
+ ## lookAt -- Lock Camera to Target
192
+
193
+ Positions camera to look at a target from an offset (`HeadingPitchRange` or
194
+ `Cartesian3`). **Locks the camera until `lookAtTransform(Matrix4.IDENTITY)`.**
195
+
196
+ ```js
197
+ import { Cartesian3, Math as CesiumMath, HeadingPitchRange } from "cesium";
198
+
199
+ // View from the south, looking north (heading 0 = facing north = camera is south)
200
+ const target = Cartesian3.fromDegrees(2.2945, 48.8584, 300.0);
201
+ viewer.camera.lookAt(
202
+ target,
203
+ new HeadingPitchRange(
204
+ CesiumMath.toRadians(0.0), // heading 0 = north-facing
205
+ CesiumMath.toRadians(-20.0), // pitch -- 20 deg down
206
+ 1500.0, // range in meters
207
+ ),
208
+ );
209
+ ```
210
+
211
+ ```js
212
+ import { Cartesian3, Math as CesiumMath, HeadingPitchRange } from "cesium";
213
+
214
+ // View from the east, looking west (heading 270 = facing west = camera is east)
215
+ const target = Cartesian3.fromDegrees(-73.9857, 40.7484, 200.0);
216
+ viewer.camera.lookAt(
217
+ target,
218
+ new HeadingPitchRange(
219
+ CesiumMath.toRadians(270.0), // heading -- west
220
+ CesiumMath.toRadians(-25.0), // pitch -- 25 deg down
221
+ 800.0, // range in meters
222
+ ),
223
+ );
224
+ ```
225
+
226
+ **Cardinal direction reference for `lookAt` heading:**
227
+
228
+ | To view from... | Camera faces... | Heading (deg) | Heading (rad) |
229
+ |---|---|---|---|
230
+ | **South** | North | 0 | `0` |
231
+ | **West** | East | 90 | `Math.PI / 2` |
232
+ | **North** | South | 180 | `Math.PI` |
233
+ | **East** | West | 270 | `3 * Math.PI / 2` |
234
+
235
+ Heading = direction camera **faces**. Camera is **opposite** that direction from the target.
236
+
237
+ ```js
238
+ import { Matrix4 } from "cesium";
239
+
240
+ // ALWAYS release the lookAt lock when done to restore free navigation
241
+ viewer.camera.lookAtTransform(Matrix4.IDENTITY);
242
+ ```
243
+
244
+ > **Trap:** Every `lookAt` call MUST have a matching `lookAtTransform(Matrix4.IDENTITY)`.
245
+ > Without the release, mouse/touch/keyboard navigation is permanently disabled.
246
+ > Use `setTimeout`, `complete` callback, or an event to trigger the release.
247
+
248
+ ---
249
+
250
+ ## lookAtTransform -- Custom Reference Frames
251
+
252
+ Set camera position relative to an arbitrary transform matrix.
253
+
254
+ ```js
255
+ import { Cartesian3, Transforms, HeadingPitchRange, Math as CesiumMath } from "cesium";
256
+
257
+ // View in an east-north-up frame centered on a point
258
+ const center = Cartesian3.fromDegrees(-75.598, 40.039);
259
+ const transform = Transforms.eastNorthUpToFixedFrame(center);
260
+ viewer.camera.lookAtTransform(
261
+ transform,
262
+ new HeadingPitchRange(0.0, CesiumMath.toRadians(-45.0), 5000.0),
263
+ );
264
+ ```
265
+
266
+ For ICRF (inertial) frame: use `Transforms.computeIcrfToFixedMatrix(time)` in a
267
+ `postUpdate` listener, apply via `lookAtTransform(Matrix4.fromRotationTranslation(icrfToFixed), offset)`.
268
+
269
+ ---
270
+
271
+ ## flyToBoundingSphere / viewBoundingSphere
272
+
273
+ Frame the camera around a `BoundingSphere`. Range is auto-computed when 0.
274
+
275
+ ```js
276
+ import { BoundingSphere, Cartesian3, HeadingPitchRange, Math as CesiumMath } from "cesium";
277
+
278
+ const sphere = new BoundingSphere(Cartesian3.fromDegrees(-117.16, 32.71), 1000.0);
279
+
280
+ // Animated
281
+ viewer.camera.flyToBoundingSphere(sphere, {
282
+ offset: new HeadingPitchRange(0.0, CesiumMath.toRadians(-45.0), 0.0),
283
+ duration: 2.0,
284
+ });
285
+
286
+ // Instant
287
+ viewer.camera.viewBoundingSphere(sphere);
288
+ ```
289
+
290
+ ---
291
+
292
+ ## Movement, Rotation, Look, and Zoom Methods
293
+
294
+ **Movement** (translate position by meters, default `defaultMoveAmount` = 100 km):
295
+ `moveForward`, `moveBackward`, `moveUp`, `moveDown`, `moveLeft`, `moveRight`,
296
+ `move(direction, amount)`.
297
+
298
+ **Rotation** (orbit around reference frame center, preserves distance, default
299
+ `defaultRotateAmount` = PI/3600 rad): `rotateUp`, `rotateDown`, `rotateLeft`,
300
+ `rotateRight`, `rotate(axis, angle)`.
301
+
302
+ **Look** (first-person rotate-in-place, default `defaultLookAmount` = PI/60 rad):
303
+ `lookUp`, `lookDown`, `lookLeft`, `lookRight`, `look(axis, angle)`,
304
+ `twistLeft`, `twistRight`.
305
+
306
+ **Zoom** (along view direction, default `defaultZoomAmount` = 100 km):
307
+ `zoomIn(amount)`, `zoomOut(amount)`.
308
+
309
+ ```js
310
+ // Scale movement speed to altitude for natural feel
311
+ const height = viewer.scene.globe.ellipsoid
312
+ .cartesianToCartographic(viewer.camera.position).height;
313
+ const speed = height / 100.0;
314
+ viewer.camera.moveForward(speed);
315
+ ```
316
+
317
+ ---
318
+
319
+ ## ScreenSpaceCameraController
320
+
321
+ Handles default mouse/touch input. Access via
322
+ `viewer.scene.screenSpaceCameraController`.
323
+
324
+ ### Constraining Navigation
325
+
326
+ When setting up constraints, **also call `setView`** so the initial view respects them.
327
+
328
+ ```js
329
+ import { Cartesian3, Math as CesiumMath } from "cesium";
330
+
331
+ const ctrl = viewer.scene.screenSpaceCameraController;
332
+
333
+ ctrl.minimumZoomDistance = 500; // meters from surface
334
+ ctrl.maximumZoomDistance = 50000;
335
+ ctrl.maximumTiltAngle = Math.PI / 2; // prevent going below horizon
336
+
337
+ // Disable specific interactions
338
+ ctrl.enableRotate = false;
339
+ ctrl.enableTilt = false;
340
+ ctrl.enableZoom = false;
341
+ ctrl.enableTranslate = false; // 2D / Columbus only
342
+ ctrl.enableLook = false;
343
+ ctrl.enableInputs = false; // disable everything at once
344
+
345
+ // Set initial view at city-overview altitude for a clear starting point
346
+ viewer.camera.setView({
347
+ destination: Cartesian3.fromDegrees(-0.1276, 51.5074, 3000.0),
348
+ orientation: {
349
+ heading: CesiumMath.toRadians(0.0),
350
+ pitch: CesiumMath.toRadians(-50.0),
351
+ roll: 0.0,
352
+ },
353
+ });
354
+ ```
355
+
356
+ > **Gotcha:** `maximumZoomDistance` is silently ignored when
357
+ > `enableCollisionDetection = false`. Re-enable collision after underground
358
+ > views, or enforce zoom limits manually in `clock.onTick`.
359
+
360
+ Other properties: `inertiaSpin`, `inertiaZoom`, `inertiaTranslate` (0 = none,
361
+ 0.9 = default), `enableCollisionDetection` (set `false` to allow camera underground).
362
+
363
+ ### Remapping Input Events
364
+
365
+ ```js
366
+ import { CameraEventType, KeyboardEventModifier } from "cesium";
367
+
368
+ const ctrl = viewer.scene.screenSpaceCameraController;
369
+ ctrl.rotateEventTypes = CameraEventType.RIGHT_DRAG;
370
+ ctrl.tiltEventTypes = {
371
+ eventType: CameraEventType.LEFT_DRAG,
372
+ modifier: KeyboardEventModifier.CTRL,
373
+ };
374
+ ctrl.zoomEventTypes = CameraEventType.WHEEL;
375
+ ```
376
+
377
+ `CameraEventType` values: `LEFT_DRAG`, `RIGHT_DRAG`, `MIDDLE_DRAG`, `WHEEL`,
378
+ `PINCH`. Combine with `KeyboardEventModifier`: `SHIFT`, `CTRL`, `ALT`.
379
+
380
+ ---
381
+
382
+ ## Custom First-Person Controls
383
+
384
+ Disable default controller, use `ScreenSpaceEventHandler` for mouse-look and
385
+ `keydown`/`keyup` for WASD. Apply in `clock.onTick`. Scale speed to altitude.
386
+
387
+ ```js
388
+ import { ScreenSpaceEventHandler, ScreenSpaceEventType, Cartesian3 } from "cesium";
389
+ const ctrl = viewer.scene.screenSpaceCameraController;
390
+ ctrl.enableRotate = ctrl.enableTranslate = ctrl.enableZoom = false;
391
+ ctrl.enableTilt = ctrl.enableLook = false;
392
+
393
+ const canvas = viewer.canvas;
394
+ canvas.setAttribute("tabindex", "0");
395
+ let looking = false, startPos, mousePos;
396
+ const handler = new ScreenSpaceEventHandler(canvas);
397
+ handler.setInputAction((m) => { looking = true; startPos = mousePos = Cartesian3.clone(m.position); }, ScreenSpaceEventType.LEFT_DOWN);
398
+ handler.setInputAction((m) => { mousePos = m.endPosition; }, ScreenSpaceEventType.MOUSE_MOVE);
399
+ handler.setInputAction(() => { looking = false; }, ScreenSpaceEventType.LEFT_UP);
400
+
401
+ const flags = {};
402
+ document.addEventListener("keydown", (e) => { flags[e.code] = true; });
403
+ document.addEventListener("keyup", (e) => { flags[e.code] = false; });
404
+ viewer.clock.onTick.addEventListener(() => {
405
+ const cam = viewer.camera;
406
+ if (looking) {
407
+ cam.lookRight((mousePos.x - startPos.x) / canvas.clientWidth * 0.05);
408
+ cam.lookUp(-(mousePos.y - startPos.y) / canvas.clientHeight * 0.05);
409
+ }
410
+ const spd = viewer.scene.globe.ellipsoid.cartesianToCartographic(cam.position).height / 100;
411
+ if (flags.KeyW) cam.moveForward(spd); if (flags.KeyS) cam.moveBackward(spd);
412
+ if (flags.KeyA) cam.moveLeft(spd); if (flags.KeyD) cam.moveRight(spd);
413
+ if (flags.KeyQ) cam.moveUp(spd); if (flags.KeyE) cam.moveDown(spd);
414
+ });
415
+ ```
416
+
417
+ ---
418
+
419
+ ## Camera Events
420
+
421
+ ```js
422
+ const off = viewer.camera.moveStart.addEventListener(() => console.log("moving"));
423
+ viewer.camera.moveEnd.addEventListener(() => console.log("stopped"));
424
+ // off(); // call return value to unsubscribe
425
+
426
+ viewer.camera.percentageChanged = 0.1; // threshold for change detection
427
+ viewer.camera.changed.addEventListener((pct) => console.log(`Changed ${(pct*100).toFixed(1)}%`));
428
+ ```
429
+
430
+ ---
431
+
432
+ ## pickEllipsoid -- Screen to Globe
433
+
434
+ ```js
435
+ import { Cartesian2 } from "cesium";
436
+
437
+ const center = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2);
438
+ const worldPos = viewer.camera.pickEllipsoid(center);
439
+ if (worldPos) {
440
+ const carto = viewer.scene.globe.ellipsoid.cartesianToCartographic(worldPos);
441
+ console.log(Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude));
442
+ }
443
+ ```
444
+
445
+ ---
446
+
447
+ ## Entity Tracking
448
+
449
+ ```js
450
+ // Track an entity (sets camera to follow automatically)
451
+ viewer.trackedEntity = viewer.entities.getById("vehicle");
452
+
453
+ // Customize default tracking offset
454
+ import { Camera, HeadingPitchRange, Math as CesiumMath } from "cesium";
455
+ Camera.DEFAULT_OFFSET = new HeadingPitchRange(
456
+ CesiumMath.toRadians(90.0), CesiumMath.toRadians(-25.0), 500.0,
457
+ );
458
+
459
+ viewer.trackedEntity = undefined; // stop tracking
460
+ ```
461
+
462
+ Debug: `viewer.scene.primitives.add(new Cesium.DebugCameraPrimitive({ camera: viewer.camera, color: Cesium.Color.YELLOW, updateOnChange: true }));`
463
+
464
+ ---
465
+
466
+ ## Performance Tips
467
+
468
+ 1. **Prefer `setView` over `flyTo` with `duration: 0`** -- avoids tween overhead.
469
+ 2. **Avoid reading `heading`/`pitch`/`roll` every frame** -- each computes an
470
+ ENU transform. Cache or use `direction`/`up` vectors.
471
+ 3. **Throttle `changed` events** -- raise `percentageChanged` (e.g., 0.5).
472
+ 4. **Always release `lookAt` locks** -- `lookAtTransform(Matrix4.IDENTITY)`.
473
+ 5. **Set `maximumHeight` for short flights** -- prevents zooming to space.
474
+ 6. **Scale movement to altitude** -- divide camera height for natural speed.
475
+ 7. **Re-enable collision after underground views** -- `enableCollisionDetection = true`.
476
+ 8. **Use 600 m+ altitude for tour stops** -- avoids blurry tiles on successive flights.
477
+
478
+ ---
479
+
480
+ ## Common Patterns Quick Reference
481
+
482
+ | Task | Method | Key detail |
483
+ |---|---|---|
484
+ | Jump to a city | `setView` | 2,000-5,000 m, pitch -50, heading 0 |
485
+ | Animate to a landmark | `flyTo` | 1,000-2,000 m, pitch -30 to -40, set `duration` |
486
+ | City skyline / panoramic | `setView` or `flyTo` | 800-1,500 m, pitch -10 to -20. Position camera across river/bay, face the city. **Load OSM Buildings.** |
487
+ | Overhead / map view | `setView` or `flyTo` | pitch `-(Math.PI/2 - 0.0001)`, altitude matches feature size |
488
+ | Canyon / cliff rim | `setView` or `flyTo` | 50-300 m above rim, pitch -15 to -25 for depth |
489
+ | Lock on a target | `lookAt` | **Must** release with `lookAtTransform(Matrix4.IDENTITY)` |
490
+ | Camera tour (multi-stop) | `flyTo` chain | Use `complete` callback, keep altitude 600 m+ |
491
+ | Ground-level / street view | `setView` | **Requires 3D Tiles** (OSM Buildings or Google Photorealistic). Without them, only sky and flat ground visible. |
492
+ | Constrain user nav | `screenSpaceCameraController` | Set min/max zoom, tilt angle; also call `setView` for initial position |
493
+
494
+ ---
495
+
496
+ ## See Also
497
+
498
+ - **cesiumjs-spatial-math** -- Cartesian3, Cartographic, Matrix4, Transforms, coordinate conversions
499
+ - **cesiumjs-interaction** -- ScreenSpaceEventHandler, Scene.pick, mouse/touch events
500
+ - **cesiumjs-entities** -- Entity, trackedEntity, EntityCollection, data sources