Taos Engine · Geo Tutorials

Geo Tutorials · Step 11

Data Sources

A reference tour of where geo data comes from. Tutorials 1–10 mostly stream the keyless defaults; this one catalogs every source the geo module can pull from — OpenStreetMap, AWS, Cesium ion, the Google Map Tiles API, Gaussian-splat tiles, and Google Street View — what each gives you, what it costs, and the one-line call that wires it into a GeoScene. Sources: src/geo/imagery.ts, src/geo/raster_dem.ts, src/geo/cesium_ion.ts, src/geo/google_tiles.ts, samples/geo_splat.ts, samples/geo_street_view.ts.

This is a reference tutorial — read it when you're choosing what to stream, not as a step in the linear path. Everything here plugs into the same GeoScene add* methods you met in tutorial 3; only the source object changes.

The shape of it#

A GeoScene consumes four kinds of data, and each kind has several interchangeable sources:

KindGeoScene methodKeyless sourceKeyed alternatives
Imagery (color drape)imagery: option on terrainESRI_WORLD_IMAGERY, OSM_RASTER, CARTO_*, STADIA_*MapTiler/Bing via xyzImagery, ion bingAerial
Terrain (elevation)addRasterDemTerrain / addTerrainAWS_TERRARIUM (raster DEM)maptilerTerrainRgb, resolveMapTilerTerrain (quantized-mesh), ion worldTerrain
Vector tiles (buildings/roads)addVectorTilesOPENFREEMAP_VECTORmaptilerVector
3D Tiles (city models, photoreal)add3DTiles—ion (osmBuildings, googlePhotorealistic, …), resolveGoogleTiles, UrlTilesetSource

Plus two non-GeoScene integrations: Gaussian splats (composited over the scene via GeoSplatFeature) and Street View (a DOM panel via StreetViewPanel).

The split that matters: keyless sources (OSM-derived, AWS, Carto) need no account and stream straight from localhost; keyed sources (Cesium ion, Google, MapTiler, Stadia in some cases) want a token or API key. Tutorials 1–9 stay entirely keyless on purpose.


OpenStreetMap-derived (keyless)#

OSM is the data behind most of the keyless sources — re-served in several forms:

Vector-tile buildings — OPENFREEMAP_VECTOR#

OSM building footprints + roads in the OpenMapTiles schema, free and keyless from openfreemap.org. addVectorTiles extrudes the building layer into 3D:

TypeScript
import { OPENFREEMAP_VECTOR } from 'taos/geo/index.js';

geo.addVectorTiles(OPENFREEMAP_VECTOR);   // decodes off the main thread automatically

Raster map imagery — OSM_RASTER and the Carto styles#

OSM rendered to raster tiles, draped over terrain as color:

TypeScript
import { OSM_RASTER, CARTO_LIGHT, CARTO_DARK } from 'taos/geo/index.js';

geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: OSM_RASTER });   // standard OSM carto
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: CARTO_LIGHT });  // clean light basemap

Built-in imagery providers (no key for most): ESRI_WORLD_IMAGERY (aerial photos, the default), OSM_RASTER, CARTO_LIGHT, CARTO_DARK, CARTO_LIGHT_LABELS, CARTO_DARK_LABELS (transparent label-only overlays — see the imageryStack recipe in tutorial 3), CARTO_LIGHT_RETINA, STADIA_ALIDADE_SMOOTH, STADIA_ALIDADE_DARK.

Any XYZ tile server — xyzImagery#

The escape hatch for any {z}/{x}/{y} slippy-map server (your own, Mapbox, Thunderforest, etc.). The key, if any, rides in the URL template:

TypeScript
import { xyzImagery } from 'taos/geo/index.js';

const myTiles = xyzImagery({
  label: 'My Tiles',
  attribution: '© Me',
  template: 'https://tiles.example.com/{z}/{x}/{y}.png?key=ABC',  // {s} subdomain optional
  maxZoom: 18,
});
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: myTiles });

AWS — elevation (keyless)#

AWS_TERRARIUM is the keyless default terrain: AWS-hosted Terrain Tiles (Mapzen Terrarium PNG encoding — height packed into RGB), sourced from SRTM / GMTED2010 / ETOPO1. It's a raster DEM: addRasterDemTerrain decodes the PNG heights and bakes terrain mesh on the GPU.

TypeScript
import { AWS_TERRARIUM, ESRI_WORLD_IMAGERY } from 'taos/geo/index.js';

// Geometry from AWS elevation, color from Esri imagery draped over it.
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: ESRI_WORLD_IMAGERY });

Global coverage to zoom 15 (~5 m near the equator). For finer terrain you reach for a keyed quantized-mesh source (ion or MapTiler — below).


Cesium ion — terrain, buildings, photoreal, other worlds (keyed)#

Cesium ion is the highest-fidelity catalog and the one keyed source the tutorials use (in tutorial 10). Get a free Bearer token at https://cesium.com/ion/tokens.

You stream an ion asset by first resolving its endpoint — ion hands back the real tile URL, a per-asset access token, and the attribution HTML you're required to display. resolveIonAsset does that round trip:

TypeScript
import { resolveIonAsset, ION_ASSETS } from 'taos/geo/index.js';

const ION_TOKEN = '…';   // your Bearer token

// Cesium World Terrain (quantized-mesh — note addTerrain, not addRasterDemTerrain):
geo.addTerrain(await resolveIonAsset(ION_ASSETS.worldTerrain, ION_TOKEN),
  { imagery: ESRI_WORLD_IMAGERY });

// Cesium OSM Buildings — a global 3D Tileset of extruded OSM footprints:
geo.add3DTiles(await resolveIonAsset(ION_ASSETS.osmBuildings, ION_TOKEN),
  { useWorker: () => true });

// Google Photorealistic 3D Tiles, proxied through ion (terrain + buildings baked in):
geo.add3DTiles(await resolveIonAsset(ION_ASSETS.googlePhotorealistic, ION_TOKEN));

ION_ASSETS names the well-known IDs: worldTerrain (1), osmBuildings, googlePhotorealistic, bingAerial (Bing aerial imagery), and cesiumMoon (a 3D Tileset of the Moon — see tutorial 10). Any other ion asset ID works too; just pass the number.

Attribution is mandatory. resolveIonAsset returns the asset's attributions (Cesium / Google ToS) — display them. For the visible-tile copyrights of photoreal tiles, see the Google section below.


Google Map Tiles API — photoreal, ion-free (keyed)#

You can stream Google Photorealistic 3D Tiles directly from Google's Map Tiles API, with no Cesium ion in the path (and so no ion quota). Needs a Google Maps Platform API key with the Map Tiles API enabled:

TypeScript
import { resolveGoogleTiles } from 'taos/geo/index.js';

geo.add3DTiles(resolveGoogleTiles(GOOGLE_MAPS_KEY), { useWorker: () => true });

The key rides in the root URL and the session parameter is inherited down the whole tile tree automatically — Tileset3D streams it like any other 3D Tiles asset.

Attribution differs from ion. Google doesn't return one attribution string; each glTF tile carries an asset.copyright listing its data providers, and you must show the providers of the currently visible tiles, updated as they stream in and out. That live set is collected for you:

TypeScript
const { tileset } = geo.add3DTiles(resolveGoogleTiles(GOOGLE_MAPS_KEY));
// each frame, after geo.update():
const credits = tileset.visibleCopyrights;   // deduped, ref-stable until the set changes
attributionEl.textContent = credits.join(', ');

The reference is tileset.visibleCopyrights's !==-stable contract — it only changes identity when the on-screen providers change, so you can guard the DOM write.


MapTiler — keyed terrain & vector (alternative to ion)#

MapTiler Cloud (free tier available) is the drop-in keyed alternative when you don't want an ion account. Three flavors:

TypeScript
import { resolveMapTilerTerrain, maptilerTerrainRgb, maptilerVector } from 'taos/geo/index.js';

// Quantized-mesh terrain (same format as Cesium World Terrain → addTerrain):
geo.addTerrain(resolveMapTilerTerrain(MAPTILER_KEY), { imagery: ESRI_WORLD_IMAGERY });

// …or raster terrain-RGB elevation (→ addRasterDemTerrain), like AWS but keyed:
geo.addRasterDemTerrain(maptilerTerrainRgb(MAPTILER_KEY), { imagery: ESRI_WORLD_IMAGERY });

// OpenMapTiles vector tiles (buildings/roads), keyed alternative to OpenFreeMap:
geo.addVectorTiles(maptilerVector(MAPTILER_KEY));

Arbitrary 3D Tiles by URL — UrlTilesetSource#

Have a tileset.json somewhere (your own build, a public OGC 3D Tiles endpoint)? Point add3DTiles straight at the URL — no resolution step:

TypeScript
import { UrlTilesetSource } from 'taos/geo/index.js';

geo.add3DTiles(new UrlTilesetSource('https://example.com/tileset.json', '© Example'),
  { useWorker: () => true });

The second argument is the attribution HTML to display. This is how planet_explorer loads a user-pasted tileset URL.


Gaussian splats — GeoSplatFeature#

3D Gaussian-splat scenes (photoreal point-cloud captures) aren't a GeoScene tileset — they render as a composite over the already-rendered geo scene. The loader decodes a glTF carrying KHR_gaussian_splatting (incl. the spz_2 compression), and GeoSplatFeature draws it, depth-occluded against the terrain in front of it:

TypeScript
import { loadGaussianSplatGlb, transformSplatData } from 'taos/splats/gltf_splat_loader.js';
import { GeoSplatFeature } from 'taos/splats/geo_splat_feature.js';

const { data, transform } = await loadGaussianSplatGlb(glbBuffer);
transformSplatData(data, placementMatrix);   // bake the world placement into the cloud

// Register AFTER the tonemap feature — it composites over the final image.
engine.addFeature(new GeoSplatFeature(data));

transformSplatData bakes a placement matrix into the cloud (the splat pass has no per-cloud model matrix), so first build the matrix that puts your capture at its lat/lon on the globe. samples/geo_splat.ts walks the full placement on streamed AWS terrain.

For streaming splat tilesets with LOD (the Cesium "3D Tiles Gaussian Splats with LOD" analog), GaussianSplatTileset drives a quadtree of splat tiles into one merged GPU buffer with budget-aware SSE refinement — see samples/splat_tileset.ts.


Google Street View — StreetViewPanel#

Street View isn't 3D geometry — it's Google's panorama widget rendered into a DOM element, two-way-synced with the globe. Needs a Google Maps JavaScript API key with the Street View service enabled. Tutorial 10 covers the full round trip; the gist:

TypeScript
import { StreetViewPanel } from 'taos/geo/index.js';

const panel = await StreetViewPanel.load({
  apiKey: GOOGLE_MAPS_KEY,
  container: document.getElementById('streetview')!,
  lon: -122.4194, lat: 37.7749,
  onPositionChanged: (lon, lat) => placeGlobeMarker(lon, lat),  // panorama → globe
});
panel.setLocation(lon, lat);   // drive it from a globe click (globe → panorama)

Mixing sources#

Every source above plugs into the same GeoScene, so you mix freely — keyless AWS terrain under keyed ion buildings, OSM imagery with MapTiler elevation, whatever. Add terrain first (content clamps to its height), and remember the stacking rules from tutorial 3:

TypeScript
geo.addRasterDemTerrain(AWS_TERRARIUM, { imagery: ESRI_WORLD_IMAGERY });        // keyless geometry + color
geo.add3DTiles(await resolveIonAsset(ION_ASSETS.osmBuildings, ION_TOKEN));      // keyed buildings on top

The dataSourceLog (src/geo/data_source.ts) records every fetch per source — handy for confirming what's actually streaming and spotting a 401/403 from a bad key.

Next#