@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.
- package/CHANGELOG.md +618 -0
- package/INSTALL.md +478 -0
- package/LICENSE +15 -0
- package/README.md +148 -2
- package/RELEASING.md +160 -0
- package/bin/anov-gis-skills.mjs +408 -0
- package/lib/mcp-config.mjs +273 -0
- package/package.json +42 -4
- package/skills/anovgis-analysis/SKILL.md +585 -0
- package/skills/anovgis-effects/SKILL.md +260 -0
- package/skills/anovgis-imagery-terrain/SKILL.md +505 -0
- package/skills/anovgis-layers/SKILL.md +627 -0
- package/skills/anovgis-materials/SKILL.md +635 -0
- package/skills/anovgis-models-3dtiles/SKILL.md +448 -0
- package/skills/anovgis-pitfalls/SKILL.md +561 -0
- package/skills/anovgis-popup-widget/SKILL.md +474 -0
- package/skills/anovgis-primitives/SKILL.md +482 -0
- package/skills/anovgis-vector-graphics/SKILL.md +602 -0
- package/skills/anovgis-viewer/SKILL.md +618 -0
- package/skills/using-anovgis/DEMO-MAP.md +64 -0
- package/skills/using-anovgis/DEMO-SKILL-MAP.md +248 -0
- package/skills/using-anovgis/DOMAINS.md +183 -0
- package/skills/using-anovgis/REFERENCE.md +114 -0
- package/skills/using-anovgis/SKILL.md +392 -0
- package/skills/using-anovgis/templates/README.md +154 -0
- package/skills/using-anovgis/templates/combo1-vue3-vite.md +295 -0
- package/skills/using-anovgis/templates/combo2-vue2-cli.md +119 -0
- package/skills/using-anovgis/templates/combo3-html-cdn.md +107 -0
- package/skills/using-anovgis/templates/combo4-vanilla-webpack.md +91 -0
- package/skills/using-anovgis/templates/wizard-profiles.md +105 -0
- package/vendor/cesiumjs-skills/LICENSE +201 -0
- package/vendor/cesiumjs-skills/NOTICE +18 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-3d-tiles/SKILL.md +465 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-camera/SKILL.md +500 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-core-utilities/SKILL.md +403 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/REFERENCE.md +397 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/SKILL.md +351 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/01-diffuse-tint.js +38 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/02-texture-swap.js +42 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/03-feature-id-tileset.js +29 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/04-translucent-override.js +37 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/05-vertex-displacement.js +49 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/06-metadata-ramp.js +34 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/07-voxel-shader.js +27 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/README.md +19 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-custom-shader/examples/_sandcastle-template.html +9 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-entities/SKILL.md +389 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-imagery/SKILL.md +471 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-interaction/SKILL.md +423 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-materials-shaders/SKILL.md +292 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-models-particles/SKILL.md +423 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-primitives/SKILL.md +537 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-spatial-math/SKILL.md +358 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-terrain-environment/SKILL.md +401 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-time-properties/SKILL.md +353 -0
- package/vendor/cesiumjs-skills/skills/cesiumjs-viewer-setup/SKILL.md +434 -0
- 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
|