3 — Tileset & Imagery Layers
▶ Live demo: run this tutorial in your browser — built from src/03_layers.ts.
A
GeoSceneis a stack of datasets. This tutorial layers terrain, buildings, and photorealistic 3D Tiles into one scene, then drapes multi-layer imagery (an aerial base map with a transparent labels overlay) onto the terrain. Sources: samples/geo_osm.ts, samples/geo_imagery.ts, src/geo/geo_scene.ts.
The four kinds of tileset#
GeoScene has one add* method per data type. Each returns
{ tileset, ready } immediately; ready resolves when the root tile loads.
geo.addTerrain(asset, opts); // Cesium quantized-mesh terrain (ion)
geo.addRasterDemTerrain(source, opts); // Web-Mercator PNG-elevation terrain (e.g. AWS Terrarium)
geo.add3DTiles(asset, opts); // 3D Tiles: buildings, city models, photorealistic
geo.addVectorTiles(source, opts); // vector tiles (MVT) draped on terrain
Stacking order matters#
Add terrain first, then things that sit on it. Buildings and other content clamp their height against the terrain that's already loaded, so terrain has to be in the scene before them:
import { GeoScene, GeoFrame } from 'taos/geo/index.js';
import { AWS_TERRARIUM, ESRI_WORLD_IMAGERY, OPENFREEMAP_VECTOR } from 'taos/geo/index.js';
const geo = new GeoScene(device, GeoFrame.atLonLat(-74.0060, 40.7128, 0));
// 1. Terrain underneath everything (keyless AWS Terrarium, draped with imagery).
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: ESRI_WORLD_IMAGERY });
// 2. OSM building footprints on top (keyless OpenFreeMap vector tiles).
geo.addVectorTiles(OPENFREEMAP_VECTOR);
The keyed equivalents are a drop-in swap (same GeoScene API): addTerrain( resolveIonAsset(ION_ASSETS.worldTerrain, token)) for Cesium quantized-mesh world
terrain and add3DTiles(resolveIonAsset(ION_ASSETS.osmBuildings, token)) for the
Cesium OSM Buildings 3D Tileset — both higher-fidelity, both needing an ion token.
Swapping OSM for full photorealistic 3D Tiles is a one-line change — point
add3DTiles at Google's photorealistic asset instead:
geo.add3DTiles(await resolveIonAsset(ION_ASSETS.googlePhotorealistic, ION_TOKEN));
(Photorealistic tiles already include terrain and buildings, so you'd usually drop the separate terrain/OSM layers when using them.)
Per-tileset options#
add3DTiles takes GeoTilesetOptions:
geo.add3DTiles(asset, {
lit: true, // light otherwise-unlit materials (default true)
albedoBoost: 1.2, // brighten dark content
collision: false, // retain CPU triangle soup for physics (tutorial 9)
useWorker: () => true,// decode tiles off the main thread
// bodyOffset, regionEllipsoid — for non-Earth bodies (tutorial 10)
});
Tune the LOD aggressiveness per tileset after it's added:
const { tileset, ready } = geo.add3DTiles(asset);
await ready;
tileset.maxSSE = 12; // target screen-space error in px (default 16; lower = sharper, heavier)
Imagery: draping a texture on terrain#
Quantized-mesh and raster-DEM terrain are geometry only — they get their color from an imagery provider draped over them. Pass one via the terrain options:
import { ESRI_WORLD_IMAGERY, OSM_RASTER, CARTO_LIGHT } from 'taos/geo/index.js';
import { AWS_TERRARIUM } from 'taos/geo/index.js';
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: ESRI_WORLD_IMAGERY }); // aerial photos
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: CARTO_LIGHT }); // clean map style
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: false }); // no drape (bare geometry)
imagery: true (or omitting it) uses ESRI_WORLD_IMAGERY by default. Built-in
providers (no API key needed for most): ESRI_WORLD_IMAGERY, OSM_RASTER,
CARTO_LIGHT, CARTO_DARK, CARTO_LIGHT_LABELS, CARTO_DARK_LABELS,
CARTO_LIGHT_RETINA, STADIA_ALIDADE_SMOOTH, STADIA_ALIDADE_DARK.
Your own XYZ tile source#
import { xyzImagery } from 'taos/geo/index.js';
const myTiles = xyzImagery({
label: 'My Tiles',
attribution: '© Me',
template: 'https://my-server.com/{z}/{x}/{y}.png', // {s} subdomain optional
maxZoom: 18,
});
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: myTiles });
Multi-layer imagery: base map + labels#
A single provider is one image. To composite several — say an aerial base with a
transparent street-labels overlay — build an ImageryStack. Each layer is a
provider plus optional photographic adjustments
(alpha, brightness, contrast, gamma, saturation, hue). They're
alpha-composited into one texture before draping, so the terrain path is
unchanged.
import { imageryStack, ESRI_WORLD_IMAGERY, CARTO_LIGHT_LABELS } from 'taos/geo/index.js';
geo.addRasterDemTerrain(AWS_TERRARIUM, {
imagery: imageryStack(
{ provider: ESRI_WORLD_IMAGERY, brightness: 1.05, contrast: 1.05 }, // base
{ provider: CARTO_LIGHT_LABELS, alpha: 0.9 }, // labels on top
),
});
Layers composite bottom-to-top — the first argument is the base, each later one
draws over it. CARTO_*_LABELS providers are transparent label-only tiles
designed exactly for this overlay role.
samples/geo_imagery.ts wires these into live sliders (base picker, label opacity, brightness/contrast/saturation) and re-streams the terrain when they change — a good reference for a runtime imagery control panel.
Switching the whole stack at runtime#
To swap datasets (e.g. toggle between ion and AWS terrain), clear() the scene
and rebuild:
async function rebuild(useIon: boolean): Promise<void> {
geo.clear(); // remove all tilesets + caches
if (useIon) {
geo.addTerrain(ionTerrain, { imagery: OSM_RASTER });
geo.add3DTiles(ionBuildings);
} else {
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: OSM_RASTER });
}
}
You can also geo.remove(tileset) to drop a single layer, or
geo.setVisible(tileset, false) to hide one without unloading it (handy when the
terrain is also feeding physics colliders — see tutorial 9).
Next#
- Tutorial 4 — Anchors & Picking: put your own models on the globe and click to find out what's under the cursor.