agentsclimarketplace

Cesiumjs imagery

Skill CesiumGS/cesiumjs-skills/skills/cesiumjs-imagery

Curated agent skills for CesiumJS development.

Install
npx -y skills add CesiumGS/cesiumjs-skills --skill cesiumjs-imagery

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

CesiumJS imagery layers - ImageryProvider, ImageryLayer, ImageryLayerCollection, WMS, WMTS, Bing, OpenStreetMap, ArcGIS, Mapbox, tile discard policies. Use when adding or swapping base map layers, configuring imagery providers, layering multiple map sources, or creating split-screen imagery comparisons.

SKILL.md

15.4 KB, as published. Nobody here has run it

CesiumJS Imagery Layers

CesiumJS v1.143 -- Imagery providers supply raster tile data rendered on the Globe or draped over a Cesium3DTileset. The three core abstractions are ImageryProvider (fetches tiles), ImageryLayer (display settings), and ImageryLayerCollection (ordered stack on the globe).

ImageryProvider        (abstract -- fetches tile images)
  -> ImageryLayer      (wraps one provider; alpha, brightness, split, etc.)
    -> ImageryLayerCollection  (ordered stack; index 0 = base layer)
      -> Globe / Cesium3DTileset

Layers render bottom-to-top. Index 0 is the base layer, stretched to fill the globe even if its rectangle does not cover the entire world.

Quick Start and ImageryLayer Factories

When creating a viewer for imagery work, disable unneeded widgets so the imagery is the visual focus. Use camera.setView (not flyTo) when you need the camera in position immediately — flyTo animates and may not finish before your code continues.

import { Viewer, ImageryLayer, IonImageryProvider, IonWorldImageryStyle, Math as CesiumMath } from "cesium";

// Clean viewer -- disable widgets that distract from imagery
const viewer = new Viewer("cesiumContainer", {
  animation: false,
  timeline: false,
  navigationHelpButton: false,
  navigationInstructionsInitiallyVisible: false,
});

// Position camera immediately (no animation)
viewer.camera.setView({
  destination: Cesium.Cartesian3.fromDegrees(-73.0, 41.0, 1500000),
  orientation: {
    heading: 0.0,
    pitch: CesiumMath.toRadians(-90), // look straight down
    roll: 0.0,
  },
});

// Explicit base layer choice
const viewer2 = new Viewer("cesiumContainer", {
  baseLayer: ImageryLayer.fromWorldImagery(),
});

// fromProviderAsync -- wraps any async provider; returns ImageryLayer immediately
const nightLayer = ImageryLayer.fromProviderAsync(
  IonImageryProvider.fromAssetId(3812), // Earth at Night
);
nightLayer.alpha = 0.5;
nightLayer.brightness = 2.0;
viewer.imageryLayers.add(nightLayer);

// fromWorldImagery with style override
const roadLayer = ImageryLayer.fromWorldImagery({
  style: IonWorldImageryStyle.ROAD,
});
viewer.imageryLayers.add(roadLayer);

Camera Height Reference for Imagery Scenes

Use camera.setView with these approximate heights:

ScaleHeight (m)Example
Street / block500–2,000Downtown intersection
City5,000–25,000Washington DC, Paris
Metro area50,000–200,000Greater London
Region / state300,000–1,500,000Florida, Japan
Continent3,000,000–8,000,000Europe, North America

For top-down (map-style) views set pitch: CesiumMath.toRadians(-90). For oblique 3D views set pitch: CesiumMath.toRadians(-35) to CesiumMath.toRadians(-60).

ImageryLayerCollection API

Access via viewer.imageryLayers (same as viewer.scene.imageryLayers).

const layers = viewer.imageryLayers;

layers.add(myLayer);              // add on top
layers.add(myLayer, 0);           // add at index
layers.addImageryProvider(provider); // create layer + add

layers.raise(myLayer);            // move up one
layers.lower(myLayer);            // move down one
layers.raiseToTop(myLayer);       // move to top
layers.lowerToBottom(myLayer);    // move to bottom

layers.remove(myLayer);           // remove and destroy
layers.remove(myLayer, false);    // remove without destroying
layers.removeAll();

const count = layers.length;
const base  = layers.get(0);
const idx   = layers.indexOf(myLayer);
const has   = layers.contains(myLayer);

Events: layerAdded(layer, index), layerRemoved(layer, index), layerMoved(layer, newIndex, oldIndex), layerShownOrHidden(layer, index, show).

ImageryLayer Display Properties

Properties accept a number or a per-tile callback (frameState, layer, x, y, level) => value.

PropertyDefaultNotes
alpha1.00 = transparent, 1 = opaque
brightness1.0< 1 darker, > 1 brighter
contrast1.0< 1 lower, > 1 higher
hue0.0Shift in radians
saturation1.0< 1 desaturated, > 1 oversaturated
gamma1.0Gamma correction
showtrueVisibility toggle
splitDirectionSplitDirection.NONELEFT, RIGHT, or NONE
nightAlpha / dayAlpha1.0Requires Globe.enableLighting

Additional options: rectangle, minimumTerrainLevel / maximumTerrainLevel, cutoutRectangle, colorToAlpha / colorToAlphaThreshold, minificationFilter / magnificationFilter (LINEAR default, or NEAREST).

// Adjust appearance at runtime
layer.alpha = 0.7;
layer.brightness = 1.3;
layer.contrast = 1.5;
layer.saturation = 0.5;
layer.gamma = 1.2;

Swapping the Base Layer

Remove the default base layer and replace it at index 0. The replacement becomes the new base layer, stretched to fill the globe.

import { ImageryLayer, OpenStreetMapImageryProvider } from "cesium";

// Remove default Bing aerial
viewer.imageryLayers.remove(viewer.imageryLayers.get(0));

// Add OSM as new base layer at index 0
const osmLayer = new ImageryLayer(
  new OpenStreetMapImageryProvider({
    url: "https://tile.openstreetmap.org/",
    maximumLevel: 19,
    credit: "OpenStreetMap contributors",
  }),
);
viewer.imageryLayers.add(osmLayer, 0);

Imagery Providers

IonImageryProvider

// Always use fromAssetId (async factory); never call constructor directly
const layer = ImageryLayer.fromProviderAsync(
  IonImageryProvider.fromAssetId(3812),
);
viewer.imageryLayers.add(layer);

OpenStreetMapImageryProvider

Extends UrlTemplateImageryProvider for Slippy tile servers.

const osm = new OpenStreetMapImageryProvider({
  url: "https://tile.openstreetmap.org/",
  maximumLevel: 19,
  credit: "OpenStreetMap contributors",
  // retinaTiles: true,  // request @2x tiles
});
viewer.imageryLayers.addImageryProvider(osm);

UrlTemplateImageryProvider

The most flexible provider. Placeholders: {x}, {y}, {z}, {s}, {reverseX/Y/Z}, {west/south/east/northDegrees}, {west/south/east/northProjected}, {width}, {height}.

import { UrlTemplateImageryProvider, GeographicTilingScheme, buildModuleUrl } from "cesium";

// TMS-style with Geographic tiling
const tms = new UrlTemplateImageryProvider({
  url: buildModuleUrl("Assets/Textures/NaturalEarthII") + "/{z}/{x}/{reverseY}.jpg",
  tilingScheme: new GeographicTilingScheme(),
  maximumLevel: 5,
});
viewer.imageryLayers.addImageryProvider(tms);

// Carto Positron with subdomains
const positron = new UrlTemplateImageryProvider({
  url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
  subdomains: "abcd",
  credit: "Map tiles by CartoDB, under CC BY 3.0. Data by OpenStreetMap, under ODbL.",
});

// Custom tags for time-varying data
const custom = new UrlTemplateImageryProvider({
  url: "https://yourserver/{Time}/{z}/{y}/{x}.png",
  customTags: {
    Time: (imageryProvider, x, y, level) => "20240101",
  },
});

WebMapServiceImageryProvider (WMS)

import { WebMapServiceImageryProvider, ImageryLayer, Rectangle } from "cesium";

const wms = new WebMapServiceImageryProvider({
  url: "https://basemap.nationalmap.gov:443/arcgis/services/USGSHydroCached/MapServer/WMSServer",
  layers: "0",
  rectangle: Rectangle.fromDegrees(-180, -90, 180, 90),
  // parameters: { transparent: true, format: "image/png" },
  // crs: "EPSG:4326",  // WMS >= 1.3.0
  // srs: "EPSG:4326",  // WMS 1.1.x
});
viewer.imageryLayers.add(new ImageryLayer(wms));

WebMapTileServiceImageryProvider (WMTS)

Required options: url, layer, style, tileMatrixSetID.

import { WebMapTileServiceImageryProvider, Credit } from "cesium";

const wmts = new WebMapTileServiceImageryProvider({
  url: "https://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS",
  layer: "USGSShadedReliefOnly",
  style: "default",
  format: "image/jpeg",
  tileMatrixSetID: "default028mm",
  maximumLevel: 19,
  credit: new Credit("U. S. Geological Survey"),
});
viewer.imageryLayers.addImageryProvider(wmts);

GetFeatureInfo (1.140+, #13196): WebMapTileServiceImageryProvider now supports pickFeatures for both KVP and RESTful WMTS services. Enable it with the new constructor options enablePickFeatures, getFeatureInfoFormats, getFeatureInfoUrl, and getFeatureInfoParameters; then call provider.pickFeatures(x, y, level, longitude, latitude) (the same signature WMS uses) to query attributes at a location.

ArcGisMapServerImageryProvider

import { ArcGisMapServerImageryProvider, ArcGisMapService, ArcGisBaseMapType, ImageryLayer } from "cesium";

ArcGisMapService.defaultAccessToken = "<YOUR_ARCGIS_TOKEN>";

// From basemap type enum: SATELLITE, OCEANS, HILLSHADE
const arcgis = ImageryLayer.fromProviderAsync(
  ArcGisMapServerImageryProvider.fromBasemapType(ArcGisBaseMapType.SATELLITE),
);
viewer.imageryLayers.add(arcgis);

// From a specific MapServer URL
const streets = ImageryLayer.fromProviderAsync(
  ArcGisMapServerImageryProvider.fromUrl(
    "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
  ),
);

BingMapsImageryProvider

import { BingMapsImageryProvider, BingMapsStyle, ImageryLayer } from "cesium";

const bing = ImageryLayer.fromProviderAsync(
  BingMapsImageryProvider.fromUrl("https://dev.virtualearth.net", {
    key: "<YOUR_BING_KEY>",
    mapStyle: BingMapsStyle.AERIAL_WITH_LABELS_ON_DEMAND,
  }),
);
viewer.imageryLayers.add(bing);

Styles: AERIAL, AERIAL_WITH_LABELS_ON_DEMAND, ROAD_ON_DEMAND, CANVAS_DARK, CANVAS_LIGHT, CANVAS_GRAY.

MapboxStyleImageryProvider

import { MapboxStyleImageryProvider, ImageryLayer } from "cesium";

const mapbox = new MapboxStyleImageryProvider({
  styleId: "streets-v11",
  accessToken: "<YOUR_MAPBOX_TOKEN>",
  // tilesize: 512, scaleFactor: true  // retina
});
viewer.imageryLayers.add(new ImageryLayer(mapbox));

SingleTileImageryProvider

import { SingleTileImageryProvider, ImageryLayer, Rectangle } from "cesium";

const logo = ImageryLayer.fromProviderAsync(
  SingleTileImageryProvider.fromUrl("/images/overlay.png", {
    rectangle: Rectangle.fromDegrees(-75.0, 28.0, -67.0, 29.75),
  }),
);
viewer.imageryLayers.add(logo);

1.140+ (#13297): OffscreenCanvas is now an accepted ImageryTypes value, so you can feed a worker-rendered or dynamically-drawn OffscreenCanvas wherever an image source is expected -- useful for procedurally generated or live-updating overlays without round-tripping through a data URL.

Split-Screen Comparison

import { ImageryLayer, IonImageryProvider, SplitDirection } from "cesium";

// Add an overlay that only appears on the left side of the split
const nightLayer = ImageryLayer.fromProviderAsync(IonImageryProvider.fromAssetId(3812));
nightLayer.splitDirection = SplitDirection.LEFT;
viewer.imageryLayers.add(nightLayer);

viewer.scene.splitPosition = 0.5; // 0-1 fraction of viewport width

SplitDirection: LEFT (-1), NONE (0), RIGHT (1).

Cutout Rectangle

import { Rectangle } from "cesium";

const cutout = Rectangle.fromDegrees(-90, 20, -70, 40);

// Cut a hole in the base layer to reveal imagery beneath
const base = viewer.imageryLayers.get(0);
base.cutoutRectangle = cutout;

Color-to-Alpha

import { Color } from "cesium";

const baseLayer = viewer.imageryLayers.get(0);
baseLayer.colorToAlpha = new Color(0.0, 0.016, 0.059); // dark ocean blue
baseLayer.colorToAlphaThreshold = 0.2; // tolerance (0-1)

Draping Imagery on 3D Tiles

import { Cesium3DTileset, ImageryLayer, IonImageryProvider } from "cesium";

const tileset = await Cesium3DTileset.fromUrl("/path/to/tileset.json");
viewer.scene.primitives.add(tileset);

const labelLayer = ImageryLayer.fromProviderAsync(
  IonImageryProvider.fromAssetId(2411391),
);
tileset.imageryLayers.add(labelLayer); // drape on tileset, not globe
labelLayer.show = false; // toggle off

Debugging Providers

import { TileCoordinatesImageryProvider, GridImageryProvider, ImageryLayer, Color } from "cesium";

// Show x/y/level labels on every tile
viewer.imageryLayers.add(new ImageryLayer(
  new TileCoordinatesImageryProvider({ color: Color.YELLOW }),
));
// Wireframe grid overlay
viewer.imageryLayers.add(new ImageryLayer(new GridImageryProvider()));

Tile Discard Policies

PolicyBehavior
DiscardEmptyTileImagePolicyDiscards zero-byte images (Bing Maps default)
DiscardMissingTileImagePolicyCompares pixels against a known "missing" tile
NeverTileDiscardPolicyNever discards (use when server always returns valid tiles)
import { NeverTileDiscardPolicy, UrlTemplateImageryProvider } from "cesium";

const provider = new UrlTemplateImageryProvider({
  url: "https://my-server/{z}/{x}/{y}.png",
  tileDiscardPolicy: new NeverTileDiscardPolicy(),
});

Error Handling

const layer = ImageryLayer.fromProviderAsync(IonImageryProvider.fromAssetId(3812));
viewer.imageryLayers.add(layer);

// Provider creation failure
layer.errorEvent.addEventListener((error) => {
  console.error("Layer creation failed:", error);
});

// Provider resolved -- listen for per-tile errors
layer.readyEvent.addEventListener((provider) => {
  provider.errorEvent.addEventListener((tileError) => {
    console.warn("Tile error:", tileError.message);
  });
});

Time-Dynamic WMTS

Pass clock and times (a TimeIntervalCollection) for time-varying layers.

import { WebMapTileServiceImageryProvider, TimeIntervalCollection, JulianDate, Credit } from "cesium";

const times = TimeIntervalCollection.fromIso8601({
  iso8601: "2015-07-30/2017-06-16/P1D",
  dataCallback: (interval) => ({ Time: JulianDate.toIso8601(interval.start) }),
});
const weather = new WebMapTileServiceImageryProvider({
  url: "https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/AMSR2_Snow_Water_Equivalent/default/{Time}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png",
  layer: "AMSR2_Snow_Water_Equivalent",  style: "default",
  tileMatrixSetID: "2km",  maximumLevel: 5,  format: "image/png",
  clock: viewer.clock,  times: times,
  credit: new Credit("NASA Global Imagery Browse Services for EOSDIS"),
});
viewer.imageryLayers.addImageryProvider(weather);

Performance Tips

  1. Limit simultaneous layers -- 2-3 is typical; each layer multiplies tile requests and GPU texture memory.
  2. Set hasAlphaChannel: false on opaque providers to reduce memory and upload time.
  3. Use minimumTerrainLevel / maximumTerrainLevel to skip tile fetches at irrelevant zoom levels.
  4. Prefer ImageryLayer.fromProviderAsync over manual await -- avoids blank globe during provider load.
  5. Set tight rectangle bounds on regional providers to prevent out-of-extent tile requests.
  6. Reuse provider instances -- remove with destroy: false and re-add instead of recreating.
  7. Use NeverTileDiscardPolicy when tiles are always valid; pixel comparison adds overhead.
  8. Choose NEAREST filtering only for classified raster data; LINEAR (default) is faster.

See Also

  • cesiumjs-viewer-setup -- Viewer constructor, Ion token, createWorldImageryAsync
  • cesiumjs-terrain-environment -- Globe, terrain providers, atmosphere, lighting

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.