git:20260601.e67e5c6 to git:20260827.49e0612

133 added, 15 removed. Audit A to A.

---
name: cesiumjs-viewer-setup
description: "CesiumJS viewer setup - Viewer, CesiumWidget, widgets, Ion token, Scene configuration, SceneMode, factory helpers, geocoders, platform services. Use when initializing a CesiumJS application, configuring viewer widgets, setting Ion access tokens, creating default terrain or imagery, or bootstrapping a 3D globe."
---
# CesiumJS Viewer & Scene Setup
Reference for bootstrapping CesiumJS applications: Viewer, CesiumWidget, Ion/GoogleMaps/ITwinPlatform configuration, widgets, factory helpers, geocoder services, viewer mixins, Credits, and related enums.
## Quick Start
```js
- import { Ion, Viewer, Terrain } from "cesium";
+ import { Viewer, ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
import "cesium/Build/Cesium/Widgets/widgets.css";
- // Always set your Ion token before any other Cesium calls
- Ion.defaultAccessToken = "YOUR_CESIUM_ION_ACCESS_TOKEN";
-
const viewer = new Viewer("cesiumContainer", {
- terrain: Terrain.fromWorldTerrain(),
+ baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
+ url: "https://tile.openstreetmap.org/",
+ maximumLevel: 18,
+ })),
+ baseLayerPicker: false,
});
```
Required HTML: `<div id="cesiumContainer" style="width:100%;height:100vh"></div>`
+ Use Cesium ion defaults (`Terrain.fromWorldTerrain`,
+ `ImageryLayer.fromWorldImagery`, `createOsmBuildingsAsync`, Google
+ Photorealistic 3D Tiles) only when the target runtime has the required ion or
+ Google entitlement. For public/no-token examples, choose explicit public
+ providers and URL-backed 3D Tiles.
+
+ ## Framing Loaded Content (Critical)
+
+ **After adding a tileset, model, or data source, you must explicitly frame it.**
+ A `Viewer` constructed with default options starts the camera at a fixed view
+ of Earth. It does NOT auto-zoom to primitives you add. Forgetting this is
+ the most common cause of "I see only gray surface" or "the asset is a speck
+ in the corner" failures.
+
+ ```js
+ const tileset = await Cesium3DTileset.fromUrl(url);
+ viewer.scene.primitives.add(tileset);
+
+ // Pick ONE of:
+ await viewer.zoomTo(tileset); // instant fit to bounding sphere
+ await viewer.flyTo(tileset, { duration: 0 }); // animated; duration 0 = instant
+ ```
+
+ `viewer.zoomTo` / `viewer.flyTo` accept `Entity`, `Entity[]`, `EntityCollection`,
+ `DataSource`, `ImageryLayer`, `Cesium3DTileset`, `VoxelPrimitive`, `Model`, or
+ `TimeDynamicPointCloud`. They use the target's bounding sphere, which works for
+ tilesets with arbitrary local coordinate systems where computing an ECEF
+ camera position by hand will miss the asset entirely.
+
+ When you do need an explicit camera position (e.g., to satisfy a heading/pitch
+ specification), still use `viewer.zoomTo(target, headingPitchRange)` so the
+ range is derived from the bounding sphere rather than guessed:
+
+ ```js
+ import { HeadingPitchRange, Math as CesiumMath } from "cesium";
+
+ await viewer.zoomTo(tileset, new HeadingPitchRange(
+ CesiumMath.toRadians(45), // heading
+ CesiumMath.toRadians(-30), // pitch
+ /* range omitted -> auto-fit */
+ ));
+ ```
+
## Ion & Platform Configuration
### Cesium Ion
```js
import { Ion } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN"; // required for ion assets
Ion.defaultServer = "https://your-ion-server.example.com/"; // optional: self-hosted
```
### IonResource
```js
import { IonResource, Cesium3DTileset } from "cesium";
const resource = await IonResource.fromAssetId(96188);
const tileset = await Cesium3DTileset.fromUrl(resource);
viewer.scene.primitives.add(tileset);
+ await viewer.zoomTo(tileset);
```
### Google Maps Platform
```js
import { GoogleMaps, createGooglePhotorealistic3DTileset, Viewer, IonGeocodeProviderType } from "cesium";
GoogleMaps.defaultApiKey = "YOUR_GOOGLE_MAPS_API_KEY"; // optional: without key, served via ion
const viewer = new Viewer("cesiumContainer", {
geocoder: IonGeocodeProviderType.GOOGLE, // required with Google 3D Tiles
});
const tileset = await createGooglePhotorealistic3DTileset({
onlyUsingWithGoogleGeocoder: true,
});
viewer.scene.primitives.add(tileset);
```
### iTwin Platform (experimental)
```js
import { ITwinPlatform, ITwinData } from "cesium";
ITwinPlatform.defaultAccessToken = "YOUR_ITWIN_TOKEN";
const tileset = await ITwinData.createTilesetForIModel(viewer, "imodel-id");
// 1.140+ (#13208): Reality Data of type GaussianSplat3DTiles is now supported
const splats = await ITwinData.createTilesetForRealityDataId(
iTwinId,
realityDataId,
ITwinPlatform.RealityDataType.GaussianSplat3DTiles,
);
viewer.scene.primitives.add(splats);
```
## Viewer Constructor Options
`new Viewer(container, options?)` -- `container` is a DOM element or its string ID.
### Widget Toggles
| Option | Default | Purpose |
|--------|---------|---------|
| `animation` | `true` | Playback controls |
| `baseLayerPicker` | `true` | Imagery/terrain switcher |
| `fullscreenButton` | `true` | Fullscreen toggle |
| `vrButton` | `false` | WebVR toggle |
| `geocoder` | `IonGeocodeProviderType.DEFAULT` | Search bar (`false` to hide) |
| `homeButton` | `true` | Reset to home view |
| `infoBox` | `true` | Entity info popup |
| `sceneModePicker` | `true` | 2D/3D/Columbus toggle |
| `selectionIndicator` | `true` | Selection reticle |
| `timeline` | `true` | Time scrubber |
| `navigationHelpButton` | `true` | Mouse/touch help |
| `projectionPicker` | `false` | Perspective/ortho toggle |
### Scene & Rendering
| Option | Default | Purpose |
|--------|---------|---------|
| `sceneMode` | `SceneMode.SCENE3D` | Initial scene mode |
- | `scene3DOnly` | `false` | Lock to 3D, saves GPU memory |
+ | `scene3DOnly` | `false` | Lock to 3D, saves GPU memory per geometry instance |
| `shadows` | `false` | Shadow casting |
| `terrainShadows` | `ShadowMode.RECEIVE_ONLY` | Terrain shadow mode |
| `requestRenderMode` | `false` | Render only on changes |
| `maximumRenderTimeChange` | `0.0` | Max sim-time delta for render |
| `msaaSamples` | `4` | MSAA (1 to disable) |
| `orderIndependentTranslucency` | `true` | Translucent ordering |
| `mapMode2D` | `MapMode2D.INFINITE_SCROLL` | 2D scroll behavior |
+ Pair `requestRenderMode: true` with `scene3DOnly: true` for low-power /
+ dashboard apps; both are commonly required together when the scenario calls
+ for GPU savings.
+
### Layers & Terrain
| Option | Default | Purpose |
|--------|---------|---------|
| `baseLayer` | `ImageryLayer.fromWorldImagery()` | Base imagery (`false` for none; needs `baseLayerPicker: false`) |
| `terrain` | none | Async terrain helper (cannot combine with `terrainProvider`) |
| `terrainProvider` | `EllipsoidTerrainProvider` | Sync terrain provider |
| `globe` | `new Globe()` | `false` for no globe (space scenes) |
| `skyBox` | auto (WGS84) | `false` disables sky/sun/moon |
| `skyAtmosphere` | auto (WGS84) | `false` disables limb glow |
### Minimal Viewer (No Widgets)
```js
- import { Viewer, Ion, Terrain } from "cesium";
- Ion.defaultAccessToken = "YOUR_TOKEN";
+ import { Viewer, ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
const viewer = new Viewer("cesiumContainer", {
+ baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
+ url: "https://tile.openstreetmap.org/",
+ maximumLevel: 18,
+ })),
animation: false, baseLayerPicker: false, fullscreenButton: false,
geocoder: false, homeButton: false, infoBox: false,
sceneModePicker: false, selectionIndicator: false,
timeline: false, navigationHelpButton: false,
- terrain: Terrain.fromWorldTerrain(),
});
```
## CesiumWidget (Lightweight Alternative)
No UI widgets, no Knockout dependency. Suitable for custom UIs or embedding.
```js
import { CesiumWidget, Ion } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN";
const widget = new CesiumWidget("cesiumContainer", { shouldAnimate: true });
// Exposes: widget.scene, widget.camera, widget.entities
```
## SceneMode Enum
| Value | Description |
|-------|-------------|
| `SceneMode.SCENE3D` | Standard 3D globe (default) |
| `SceneMode.SCENE2D` | Top-down orthographic map |
| `SceneMode.COLUMBUS_VIEW` | 2.5D flat map with height |
| `SceneMode.MORPHING` | Transitioning between modes |
```js
import { Viewer, SceneMode } from "cesium";
const viewer = new Viewer("cesiumContainer", { sceneMode: SceneMode.SCENE2D });
viewer.scene.morphTo3D(2.0); // animated transition
viewer.scene.morphToColumbusView(2.0);
```
## Scene Configuration
```js
const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true; // entities interact with terrain
scene.globe.enableLighting = true; // sun-based lighting
// Key sub-objects
scene.camera; // Camera
scene.primitives; // PrimitiveCollection
scene.groundPrimitives; // PrimitiveCollection (ground-clamped)
scene.imageryLayers; // ImageryLayerCollection
scene.postProcessStages;
scene.requestRender(); // trigger frame in requestRenderMode
```
+ **Important: never touch `scene.globe.*` or `scene.skyAtmosphere.*` when the
+ matching constructor option was set to `false`.** Disabling these in the
+ `Viewer` options leaves the corresponding property as `undefined` on the
+ scene, and accessing `.enableLighting`, `.depthTestAgainstTerrain`,
+ `.show`, or any other field throws `TypeError: Cannot set properties of
+ undefined`. For space scenes, configure once in the constructor and do not
+ mutate those properties afterward (see "Space Scene" below).
+
## Factory Helpers
### createOsmBuildingsAsync
```js
import { createOsmBuildingsAsync, Cesium3DTileStyle } from "cesium";
// Default styling (colors from OSM tags)
const tileset = await createOsmBuildingsAsync();
viewer.scene.primitives.add(tileset);
// Custom style
const styled = await createOsmBuildingsAsync({
style: new Cesium3DTileStyle({
color: { conditions: [
["${feature['building']} === 'hospital'", "color('#0000FF')"],
[true, "color('#ffffff')"],
]},
}),
});
```
### createGooglePhotorealistic3DTileset
```js
import { createGooglePhotorealistic3DTileset, IonGeocodeProviderType } from "cesium";
// Must use Google geocoder
const viewer = new Viewer("cesiumContainer", { geocoder: IonGeocodeProviderType.GOOGLE });
const tileset = await createGooglePhotorealistic3DTileset({ onlyUsingWithGoogleGeocoder: true });
viewer.scene.primitives.add(tileset);
```
### Terrain.fromWorldTerrain / fromWorldBathymetry
Preferred for the `terrain` constructor option. Non-blocking with error events.
```js
import { Viewer, Terrain } from "cesium";
// World terrain with normals and water
const viewer = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldTerrain({ requestVertexNormals: true, requestWaterMask: true }),
});
// Bathymetry (ocean floor)
const viewer2 = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldBathymetry({ requestVertexNormals: true }),
});
```
### Terrain Event Handling
```js
import { Terrain, CesiumTerrainProvider } from "cesium";
const terrain = new Terrain(CesiumTerrainProvider.fromUrl("https://my-terrain.example.com"));
viewer.scene.setTerrain(terrain);
terrain.readyEvent.addEventListener((provider) => {
viewer.scene.globe.enableLighting = true;
});
terrain.errorEvent.addEventListener((error) => console.error("Terrain failed:", error));
```
### createWorldTerrainAsync / createWorldImageryAsync
Lower-level: return raw providers. Use when you need the provider directly.
```js
import { createWorldTerrainAsync, createWorldImageryAsync, IonWorldImageryStyle } from "cesium";
const terrainProvider = await createWorldTerrainAsync({ requestVertexNormals: true });
viewer.terrainProvider = terrainProvider;
const imageryProvider = await createWorldImageryAsync({ style: IonWorldImageryStyle.AERIAL_WITH_LABELS });
```
**IonWorldImageryStyle**: `AERIAL` (default) | `AERIAL_WITH_LABELS` | `ROAD`
## Geocoder Configuration
The `geocoder` option accepts `false`, an `IonGeocodeProviderType`, or a `GeocoderService[]`.
**IonGeocodeProviderType**: `DEFAULT` | `GOOGLE` (required with Google tiles) | `BING`
```js
import { Viewer, CartographicGeocoderService, IonGeocoderService, OpenCageGeocoderService } from "cesium";
// Multiple services (searched in order)
const viewer = new Viewer("cesiumContainer", {
geocoder: [
new CartographicGeocoderService(), // accepts "lat, lon" input
new IonGeocoderService({ scene: viewer.scene }),
],
});
```
### Custom GeocoderService
```js
const myGeocoder = {
async geocode(input, type) {
// type: GeocodeType.SEARCH or GeocodeType.AUTOCOMPLETE
const resp = await fetch(`https://api.example.com/search?q=${input}`);
const data = await resp.json();
return data.map((item) => ({
displayName: item.name,
destination: Cartesian3.fromDegrees(item.lon, item.lat),
}));
},
};
const viewer = new Viewer("cesiumContainer", { geocoder: [myGeocoder] });
```
## Viewer Mixins
```js
import { Viewer, viewerDragDropMixin, viewerCesium3DTilesInspectorMixin,
viewerCesiumInspectorMixin, viewerPerformanceWatchdogMixin, viewerVoxelInspectorMixin } from "cesium";
const viewer = new Viewer("cesiumContainer");
// Drag-and-drop CZML/GeoJSON/KML loading
viewer.extend(viewerDragDropMixin, { dropTarget: "cesiumContainer", clearOnDrop: true });
viewer.dropError.addEventListener((handler, name, error) => console.error(error));
viewer.extend(viewerCesium3DTilesInspectorMixin); // 3D Tiles debug panel
viewer.extend(viewerCesiumInspectorMixin); // general scene inspector
viewer.extend(viewerPerformanceWatchdogMixin); // low-FPS warning
viewer.extend(viewerVoxelInspectorMixin); // voxel debug panel
```
## Key Viewer Properties & Methods
| Property | Type |
|----------|------|
| `viewer.scene` | `Scene` |
| `viewer.camera` | `Camera` |
| `viewer.entities` | `EntityCollection` |
| `viewer.dataSources` | `DataSourceCollection` |
| `viewer.imageryLayers` | `ImageryLayerCollection` |
| `viewer.terrainProvider` | `TerrainProvider` |
| `viewer.clock` / `clockViewModel` | `Clock` / `ClockViewModel` |
| `viewer.canvas` | `HTMLCanvasElement` |
| `viewer.screenSpaceEventHandler` | `ScreenSpaceEventHandler` |
| `viewer.selectedEntity` / `trackedEntity` | `Entity` |
| `viewer.shadows` | `boolean` |
| `viewer.resolutionScale` | `number` (default 1.0) |
```js
await viewer.flyTo(entity, { duration: 3.0, offset: headingPitchRange }); // animated
- await viewer.zoomTo(tileset); // instant
+ await viewer.zoomTo(tileset); // instant; uses bounding sphere
viewer.destroy(); // free all resources
```
## Credit & FrameRateMonitor
```js
import { Credit, FrameRateMonitor } from "cesium";
// Custom credit (showOnScreen = true)
viewer.creditDisplay.addStaticCredit(new Credit("Data by Example Corp", true));
// Monitor frame rate
const monitor = FrameRateMonitor.fromScene(viewer.scene);
monitor.lowFrameRate.addEventListener(() => console.warn("Low FPS"));
monitor.nominalFrameRate.addEventListener(() => console.log("FPS recovered"));
```
## Common Patterns
### Production Viewer with Terrain and OSM Buildings
```js
import { Ion, Viewer, Terrain, createOsmBuildingsAsync, Cartesian3, Math as CesiumMath } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN";
const viewer = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldTerrain(), animation: false, timeline: false,
});
viewer.scene.primitives.add(await createOsmBuildingsAsync());
viewer.scene.camera.flyTo({
destination: Cartesian3.fromDegrees(-74.019, 40.6912, 750),
orientation: { heading: CesiumMath.toRadians(20), pitch: CesiumMath.toRadians(-20) },
});
```
+ ### Loading and Framing a Sample 3D Tileset
+
+ When the tileset's local coordinate frame is unknown (sample assets,
+ discrete-LOD demos), do NOT hand-compute an ECEF camera position; you will
+ miss the asset and render a blank gray surface. Use `viewer.zoomTo` with a
+ `HeadingPitchRange` to derive a fitted view from the tileset's bounding sphere.
+
+ ```js
+ import { Cesium3DTileset, HeadingPitchRange, Math as CesiumMath } from "cesium";
+
+ const tileset = await Cesium3DTileset.fromUrl(url);
+ viewer.scene.primitives.add(tileset);
+
+ await viewer.zoomTo(
+ tileset,
+ new HeadingPitchRange(
+ CesiumMath.toRadians(45),
+ CesiumMath.toRadians(-30),
+ // omit range -> Cesium auto-fits the bounding sphere
+ ),
+ );
+ ```
+
### Space Scene (No Globe)
+ Disable the globe and atmosphere via constructor options only. The scene's
+ `globe` and `skyAtmosphere` properties become `undefined`, so any later
+ `viewer.scene.globe.<anything> = ...` or `viewer.scene.skyAtmosphere.show =
+ false` will throw `Cannot set properties of undefined`. Configure lighting,
+ depth-test-against-terrain, atmosphere visibility, and similar options in the
+ constructor or skip them entirely for a space scene.
+
```js
const viewer = new Viewer("cesiumContainer", {
- globe: false, skyAtmosphere: false, baseLayerPicker: false,
+ globe: false, // viewer.scene.globe will be undefined
+ skyAtmosphere: false, // viewer.scene.skyAtmosphere will be undefined
+ baseLayerPicker: false,
+ baseLayer: false, // no imagery layer is needed without a globe
});
+
+ // Safe post-construction tweaks (skyBox/sun/moon remain defined):
+ viewer.scene.skyBox.show = true; // default star field
+ viewer.scene.sun.show = false;
+ viewer.scene.moon.show = false;
+
+ // DO NOT do this, throws because globe/skyAtmosphere are undefined:
+ // viewer.scene.globe.enableLighting = true;
+ // viewer.scene.skyAtmosphere.show = false;
```
- ### Explicit Render Mode (Low Power)
+ ### Explicit Render Mode (Low Power Dashboard)
+ Pair with `scene3DOnly: true` when 2D/Columbus View is not needed; this is
+ the canonical low-GPU configuration.
+
```js
const viewer = new Viewer("cesiumContainer", {
- requestRenderMode: true, maximumRenderTimeChange: Infinity,
+ requestRenderMode: true,
+ maximumRenderTimeChange: Infinity,
+ scene3DOnly: true,
+ animation: false,
+ timeline: false,
});
// Call viewer.scene.requestRender() after programmatic changes
```
### Custom Base Layer
```js
import { Viewer, ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
const viewer = new Viewer("cesiumContainer", {
baseLayerPicker: false,
baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
})),
});
```
### Columbus View with Web Mercator
```js
import { Viewer, SceneMode, WebMercatorProjection } from "cesium";
const viewer = new Viewer("cesiumContainer", {
sceneMode: SceneMode.COLUMBUS_VIEW, mapProjection: new WebMercatorProjection(),
});
```
## Performance Tips
1. **Set `requestRenderMode: true`** for mostly-static apps. Reduces CPU/GPU and battery drain. Call `scene.requestRender()` after changes.
- 2. **Use `scene3DOnly: true`** when 2D/Columbus View is not needed. Saves GPU memory per geometry instance.
+ 2. **Use `scene3DOnly: true`** when 2D/Columbus View is not needed. Saves GPU memory per geometry instance. Commonly required alongside `requestRenderMode` for dashboard/low-power scenarios.
3. **Disable unused widgets** (`animation: false`, `timeline: false`) to reduce DOM overhead.
4. **Set `msaaSamples: 1`** on low-power devices. Default `4` balances quality.
5. **Lower `resolutionScale`** (e.g., `0.75`) on HiDPI displays for better frame rates.
- 6. **Prefer `Terrain.fromWorldTerrain()`** over `await createWorldTerrainAsync()` -- non-blocking with error events.
+ 6. **Prefer `Terrain.fromWorldTerrain()`** over `await createWorldTerrainAsync()`; it is non-blocking and exposes error events.
7. **Enable `requestVertexNormals: true`** on terrain for proper lighting at negligible cost.
8. **Call `viewer.destroy()`** when removing from DOM to free WebGL contexts.
9. **Limit imagery layers** to 2-3. Each adds a texture lookup per fragment.
+
+ ## Framing Checklist (Avoid Blank / Off-Frame Renders)
+
+ Before declaring a viewer-setup task complete, verify:
+
+ 1. After adding any tileset, model, GeoJSON/KML/CZML data source, or entity collection: `await viewer.zoomTo(target)` or `await viewer.flyTo(target)` was called.
+ 2. For tilesets with unknown local frames, the camera is derived from the bounding sphere (`HeadingPitchRange`), not a hand-picked `Cartesian3.fromDegrees(...)`.
+ 3. If the scenario specifies a heading/pitch, those are passed via `HeadingPitchRange` to `zoomTo`/`flyTo` rather than set on `camera.flyTo` with a guessed destination.
+ 4. A required base imagery layer is actually present (count == 1) when the scenario expects a visible map under the asset.
+ 5. When `globe: false` or `skyAtmosphere: false` is set in constructor options, no later code accesses `viewer.scene.globe.*` or `viewer.scene.skyAtmosphere.*`; both are `undefined` and will throw.
## See Also
- **cesiumjs-camera** -- Camera positioning, flyTo, lookAt, navigation constraints
- **cesiumjs-entities** -- Entity API, data sources, GeoJSON/KML/CZML loading
- **cesiumjs-imagery** -- Imagery providers, layer management, split-screen
- **cesiumjs-terrain-environment** -- Terrain providers, Globe, atmosphere, sky, lighting