Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Environment Configuration — Terrain Stacks

Real-terrain v2 combines a gap-free DEM base with finer DEM insets, sunlit and permanently-shadowed-region (PSR) colour, masks, and quality layers. This guide covers catalogs, region requests (RealTerrain(body, lat_deg, lon_deg, size_m) or srb asset download terrain --lat --lon --size-m), deshading, download consent, and companion arbitration with the ephemeris sun.

For the fusion engine itself (blending, provenance, budgets) see Real Terrain Assets; for authoring catalog layers and v2 manifest sites see Adding Terrain Sites.

The target pair is Isaac Sim 6.1.0 and Isaac Lab 3.0.0. Terrain physics and renderer claims require runtime evidence; rasters and Isaac-free fixtures cover catalog and byte contracts. See Installation requirements and Real Terrain Assets.

Coverage is curated and limited. The shipped catalogs contain 19 Moon layers and two each for Mars and Earth. A region outside their footprints produces an empty plan: environments fall back to procedural terrain with a warning, while CLI downloads report an error. See Catalog status per body. Set SRB_TERRAIN_CATALOG_DIR to use a custom or fixture catalog.

The catalog model

Cache and catalog compatibility

Named single-source site cache keys include effective patch geometry, ortho, and attribution; equivalent integer and floating-point geometry share a key. Old named-site keys are orphaned, while legacy serialization and v2 stack or region keys retain their contracts. Old bakes may need to be recreated.

SRB_TERRAIN_CACHE_ROOT overrides the default cache location. Checkouts use the SRB scenery cache; installed wheels use $XDG_CACHE_HOME/srb/terrain, falling back to ~/.cache/srb/terrain when XDG is unset or relative. Raw archives are shared by content identity: CLI cleanup with --raw or --all requires omitting the site argument. Site-specific baked cleanup remains available. Incomplete unpublished bakes can be cleaned, but a corrupt published pointer remains an error that needs inspection.

On POSIX, different raw sources can download concurrently; requests for the same content wait for acquisition. Raw cleanup waits for active fetches and verification. Consent refusal or a size limit aborts the stack. Cached windows distinguish padding and resampling method and preserve nodata margins.

Catalog validation preserves the body/layers YAML shape and rejects unknown catalog or alignment keys, malformed footprints, and polar caps outside (0, 90] (without checking polygon topology). Alignment offsets are dx_m, dy_m, and dz_m. Region coordinates normalize antimeridian values and signed zero; target GSD must be positive and finite.

Colour fusion normalizes each integer source by its dtype maximum. Floating-point loaders must provide unit-scale values; photometric class does not specify scale or offset. Mixed stacks normalize sources before compositing.

Each per-body catalog (srb/terrain/catalog/<body>.yaml) pins approved data products, URLs, hashes, footprints, native GSD, licenses, priority, and the curated half of the large-download consent gate. The resolver intersects footprints with a region and builds prioritized DEM, colour, mask, and quality stacks for fusion. Preview a plan without moving a byte:

srb asset stack --body moon --lat -89.46 --lon 137.3 --size 2000
srb asset stack --body moon --lat -89.46 --lon 137.3 --size 2000 --json

srb asset stack reports the request’s --deshade flag; a site’s manifest can also enable deshading for its bake (see Deshade).

There are two ways to consume the catalog:

  1. Declared-stack site: a version: 2 manifest entry names catalog layers in dem_stack/color_stack; use the same RealTerrain(..., site=...) and srb asset download terrain <site> interfaces as v1.
  2. Region request: no manifest entry; the resolver finds layers by footprint intersection.

Catalog status per body

BodyLayersCurated families
moon19pgda_ldem_83s_10mpp / pgda_ldem_80s_20mpp (LOLA polar DEM bases); lroc_nac_dtm_shackrdge02 / nobile03 / malapert02 / malapert03 (NAC DTMs); usgs_astro_haworth_sfs_dem_1m (Haworth SfS DEM); zenodo_sfs_a3clr06_connridge_dem_5m / zenodo_sfs_a3clr06_connridge_omos_5m / zenodo_sfs_a3clr02_haworth_dem_5m / zenodo_sfs_a3clr02_haworth_omos_5m (SfS A3CLR DEM + orthomosaic, Connecting Ridge and Haworth); lroc_shadowcam_dtm_faustini_6m + lroc_shadowcam_cmosaic_faustini01_20m (ShadowCam DTM + cmosaic); lroc_nac_roi_haworth_lo1_20m (NAC ROI); lroc_wac_emp_643nm_south_100m (WAC_EMP 643 nm colour); imbrium_ldam_50s_1000m (LOLA LDAM 1064 nm normal albedo, nominal 1000 m at the pole, cap:50S); pgda_lpsr_85s_60m (PGDA LPSR mask); lroc_nac_pole_south_cm065_p892s2250_1m + lroc_nac_pole_south_cm135_p892s2250_1m (NAC_POLE_SOUTH_CM band-1 tile P892S2250, one per subsolar-longitude epoch — reference_only: true, so resolvable only by explicit layer_id, never as bake input)
mars2usgs_astro_hirise_jezero_dtm_1m + usgs_astro_hirise_jezero_ortho_1m (HiRISE Jezero DTM + ortho)
earth2copernicus_glo30_dryvalleys_s78e161 (Copernicus GLO-30 polar); usgs_3dep_nm_southeast_x45y364_1m (3DEP 1 m non-polar)

Named site: Jezero v2

The shipped manifest keeps the v1 jezero site and adds jezero_v2. The named v2 site is centered at (18.44, 77.44) with a 2,000 m medium patch. Its declared stack names the pinned Mars catalog layers usgs_astro_hirise_jezero_dtm_1m (DEM base) and usgs_astro_hirise_jezero_ortho_1m (colour base). The catalog records the DTM SHA-256 b710df2893d0c668e58ec6e4c3fffa4e1b8b1759b71fdb4c582dfe56ef479822 and the ortho SHA-256 4ceef4f3a1ceda63987ed66ccb0b387cfe0461d0bff5af8cc047b348502a1c58.

Use either consumption form with the same declared stack:

RealTerrain(body="mars", site="jezero_v2")
srb asset download terrain jezero_v2 --lod medium

Offline fixtures exercise named-site resolution and fusion using checked-in rasters; they do not validate the pinned HiRISE bytes.

Coverage is curated, not exhaustive. A v2 site whose declared base does not cover the footprint fails with DeclaredBaseUnavailableError instead of falling back to v1; a region request yields an empty plan. LDAM is pinned as imbrium_ldam_50s_1000m: a detached PDS3 .LBL/.IMG bundle from the HTTPS-only imbrium provider, with independent file hashes and sizes. Its cap:50S footprint is the inscribed extent, and its nominal 1000 m/pixel resolution is true at the pole. WAC_EMP’s priority 3 keeps its 643 nm product ahead of priority-4 LDAM where both cover; consumers must tolerate the possible 643 nm/1064 nm spectral seam. The Connecting Ridge P892S2250 band-1 tiles are pinned by epoch as reference_only: true comparison references; they cannot be used in a bake. Other tiles remain unpinned because their large sizes require a site-specific decision. See Adding Terrain Sites → Adding a v2 source layer for the authoring workflow to curate more layers.

Providers

Catalog provider names are registered in srb/terrain/sources/registry.py. Remote providers enforce host allowlists in their implementations; examples:

ProviderBodyHostsAuth
uahirisemarshirise.lpl.arizona.edu, www.uahirise.org, uahirise.orgnone
copernicus_s3earthcopernicus-dem-30m.s3.amazonaws.com, copernicus-dem-90m.s3.amazonaws.comnone
usgs_3depearthprd-tnm.s3.amazonaws.comnone
imbriummoonimbrium.mit.edunone

The imbrium provider accepts https only. Fetches re-check the allowed scheme and host on every redirect, so an HTTPS-to-HTTP downgrade is rejected.

Providers use unauthenticated HTTP(S) GETs; imbrium requires HTTPS. Authenticated sources such as private buckets or API-key-gated endpoints are unsupported. Existing Mars USGS products (CTX, HRSC-MOLA) need no new provider — the existing usgs_astro hosts already cover them.

Region requests

from srb.assets.scenery import RealTerrain

terrain = RealTerrain(body="moon", lat_deg=-89.66, lon_deg=0.0, size_m=2000.0)

lat_deg / lon_deg / size_m are all-three-or-none — setting any one without the others is a validation error naming the missing field(s) — and mutually exclusive with both site= and dem_path= (and with the patch-override fields patch_size_m/patch_center_xy_m: a region’s own size_m and resolved center define the bake footprint). lod maps through; size_m may be at most 8000 m (MAX_REGION_SIZE_M — tile larger areas as multiple requests).

Because these are plain numeric fields (not a nested config), the intent is that they be CLI-addressable on any env whose scenery is a RealTerrain via Hydra’s dotted-override syntax:

srb agent teleop -e _ground \
  env.scenery.lat_deg=-89.66 env.scenery.lon_deg=0.0 env.scenery.size_m=2000

The ground-task Hydra registration exposes these fields in the structured env.scenery mapping, so ordinary dotted overrides work without a + prefix. The resolver constructs RealTerrain and derives body from env.domain when env.scenery.body is omitted. An explicit body is useful for a task whose domain is not the requested planetary body:

srb agent teleop -e _ground \
  env.domain=moon env.scenery.body=moon \
  env.scenery.lat_deg=-89.66 env.scenery.lon_deg=0.0 env.scenery.size_m=2000

The constructor and the dedicated srb asset download terrain --lat --lon --size-m / srb asset stack --lat --lon --size commands remain equivalent entry points. All three region coordinates are still required together, and the normal Hydra struct validation rejects unknown scenery keys.

Region identity: the slug

A region request’s cache identity is a deterministic, machine-independent slug used everywhere a manifest site’s name would be:

region_{body}_{lat_deg:+08.4f}_{lon_deg:+09.4f}_{int(round(size_m))}m
# e.g. region_moon_-89.6600_+000.0000_2000m

The request’s fields themselves are normalized before the slug (or anything else) reads them, so two requests within the same quantum are genuinely the same region — same fields, same cache entry, same bake:

  • lon_deg is wrapped to [-180, 180) (so lon=180 and lon=-180 are one region), and signed zero collapses to 0.0;
  • lat_deg and lon_deg are rounded to 1e-4 degrees (≈3 m on the Moon) — requests differing only inside that quantum share one cached region;
  • size_m is rounded to whole metres.

Changing these widths changes region slugs and orphans existing cache entries.

Bodies and canonical CRSs

Moon, Mars, and Earth are supported. Requests use each body’s canonical polar and geographic coordinate reference systems (CRSs):

BodySouth-polarNorth-polarGeographic
moonIAU_2015:30135IAU_2015:30130IAU_2015:30100
marsIAU_2015:49935IAU_2015:49930IAU_2015:49900
earthEPSG:3031EPSG:3413EPSG:4326

Mars uses the IAU 2015 sphere radius 3396190.0 m; Earth uses WGS84 (6378137.0 m). CRS matching accounts for projection scale. User-supplied DEMs with a different projection are reprojected rather than treated as a canonical grid.

Body-aware, non-polar-capable resolution

Requests at |lat_deg| >= 60° use the body’s canonical polar CRS; others use a local stereographic CRS centered on the request. Local CRS construction rejects the poles, which use the canonical polar CRS instead. Environments fall back to procedural terrain and the CLI reports an error.

Footprints: the bbox: form

A catalog layer’s footprint field (see Adding Terrain Sites) now accepts a third shorthand alongside cap:<lat><N|S> and WKT POLYGON(...)/MULTIPOLYGON(...) in projected metres:

bbox:<lonmin>,<latmin>,<lonmax>,<latmax>

— a plain geographic-degrees footprint, body-agnostic and independent of whatever projected CRS the region happens to resolve into. This is the form non-polar (and most Mars/Earth) layers are expected to use, since authoring a WKT polygon in a per-region local stereographic CRS that doesn’t exist until request time is impractical. Rules, enforced at schema load and at footprint-containment time:

  • lonmin/lonmax accept the full [-360, 360] pre-normalization range — the dominant convention for planetary product metadata (LOLA/HRSC/CTX 0–360° east longitude) works unmodified — and are normalized to [-180, 180) once, at parse time, before any containment check runs.
  • lonmin > lonmax after normalization is a deliberate antimeridian wrap (the box spans ±180°), not an error — containment is lon >= lonmin or lon <= lonmax in that case, mirroring the region-slug’s own lon handling.
  • latmin <= latmax is enforced (unlike lon, an inverted latitude band has no valid meaning): latmin > latmax raises a pydantic.ValidationError naming the footprint string, at catalog-load time, before any byte moves.
  • Containment against a request is evaluated by projecting each sample point back to lat/lon (xy_to_latlon in the region’s own resolved CRS, whatever that is — canonical polar or local stereographic) and testing the degree box directly, so bbox: footprints work identically in every body-aware CRS from the section above.

cap: and WKT-in-projected-metres are unchanged; WKT stays usable only when the catalog author and the region resolver agree on a shared canonical projected CRS (effectively: polar layers only, since a non-polar region’s CRS is generated fresh per request and can’t be known ahead of time when the catalog entry is authored).

What happens when a region can’t resolve

An empty DEM plan — any request whose footprint no curated layer covers (most requests today, since coverage is narrow — see Catalog status per body), or a request against a body/region with no catalog coverage at all — raises RegionUnresolvedError inside the bake path. The two consumers treat it differently, on purpose:

  • Environments (RealTerrain as env.scenery): the existing terrain degrade ladder catches it and falls back to the procedural terrain, with a one-time warning naming the region slug and the reason. Training keeps running; the terrain just isn’t real.
  • CLI (srb asset download terrain --lat ...): prints the same reason and exits 1 — a download command has no procedural fallback by design: a download that cannot resolve should say so, not silently bake noise. When the body’s loaded catalog really has zero layers, the error is followed by a note naming the catalog file and the dormancy state.

CLI

# Arbitrary region (mutually exclusive with a positional site target,
# --all, and the site-only flags --patch/--patch-size/--center/--no-ortho):
srb asset download terrain --lat -89.66 --lon 0.0 --size-m 2000 \
  [--body moon] [--lod medium] [--deshade] [--force]

# Preview what a region would resolve to (moves no bytes):
srb asset stack --body moon --lat -89.66 --lon 0.0 --size 2000 [--deshade]

# Assess without baking (doctor/info know region-derived cache entries):
srb asset doctor terrain [--deshade]

The region download routes through the exact same bake_or_load path the envs use, so consent, caching, and provenance all apply unchanged. A remediation command printed by doctor/availability (download_command) reproduces the request faithfully — including --deshade when the assessment was deshade-keyed, since a deshaded bake is a different cache entry (below).

Per-env location variety

Region requests (only region requests — a curated site or a local dem_path have a fixed footprint) can fan out into K distinct patches, one baked USD per patch, distributed across environments by the Isaac Lab clone planner instead of every env sharing the same terrain:

from srb.assets.scenery import RealTerrain

# K=8 patches: index 0 is the requested center verbatim; the other 7 are
# deterministic seeded offsets within a 3000 m disk around it.
terrain = RealTerrain(
    body="moon",
    lat_deg=-89.66,
    lon_deg=0.0,
    size_m=2000.0,
    num_locations=8,
    location_spread_m=3000.0,
)

# Explicit centers instead of derived offsets (all share size_m/lod/deshade):
terrain = RealTerrain(
    body="moon",
    size_m=2000.0,
    locations=[(-89.66, 0.0), (-89.40, 15.0), (-89.80, -20.0)],
)

Pre-bake the same set from the CLI (serial, same bake_or_load path, same consent/caching/provenance rules as a single-location download):

srb asset download terrain --lat -89.66 --lon 0.0 --size-m 2000 \
  --num-locations 8 --location-spread-m 3000

Each line printed is <slug>: <path>, one per baked location, in derivation order. --num-locations/--location-spread-m are rejected (exit 2) off the --lat/--lon/--size-m region form, exactly like --patch-size/--center; --num-locations below 1 is also exit 2. A RegionUnresolvedError on any location prints that location’s slug and stops the run — locations after the failing one are never attempted, and any already-baked predecessors are kept (partial completion, not all-or-nothing).

Semantics

AspectBehavior
Primary patchIndex 0 is always the requested (lat_deg, lon_deg) center verbatim (or locations[0] for the explicit-list form). Companions, the SimForge augment layer, and any other single-path reader use RealTerrain.primary_baked_usd_path, which anchors to this patch.
DerivationRequests 1..K-1 come from numpy.random.default_rng([905, seed]) (a fixed salt decoupling this stream from every other consumer of seed), sampled uniformly in a disk of radius location_spread_m (r = location_spread_m · √u) in the request’s own resolved projected CRS (canonical polar-stereographic near the poles, local stereographic elsewhere — see Bodies and canonical CRSs). location_spread_m defaults to size_m when omitted.
Prefix-stabilityThe RNG stream does not depend on K, so raising num_locations (e.g. 4 → 8) keeps the first 4 derived slugs — and their cache entries — unchanged; it only appends new ones.
Slug uniquenessAfter the usual 1e-4 deg / whole-metre normalization, the K requests must resolve to K distinct slugs. A collision (spread too small relative to the quantum, or duplicate entries in an explicit locations list) raises RegionInvalidError naming the duplicate slugs and suggesting a larger location_spread_m or de-duplicating locations — never a silent dedupe or re-jitter.
Any latitudeThe offset math runs in the request’s own projected CRS (canonical polar-stereographic near the poles, local oblique stereographic elsewhere — see body-aware resolution above), so neither derived nor explicit centers are restricted to the polar band.
locations vs num_locationsMutually exclusive: locations fixes K to its own length and requires size_m (it has no single center to pair with lat_deg/lon_deg); passing both locations and num_locations != 1, or locations together with a lat_deg/lon_deg center, is a validation error.
CompanionsA region request has no curated companions regardless of K (unchanged from the single-location case) — they attach only for a curated site.

K==1 (the default) uses the single-patch behavior: same UsdFileCfg spawner, cache entries, and slugs. K>1 instead spawns a genuine MultiAssetSpawnerCfg (func=spawn_multi_asset_compat) at the env-scoped prim path, which Isaac Lab’s clone planner distributes one variant per environment.

Deshade (illumination-corrected colour)

Real sunlit colour mosaics have the sun baked into them: topographic shading from the capture-time illumination fights the renderer’s own lighting. deshade=True corrects for this at fusion time — for each sunlit colour layer, the engine models a hillshade of the fused DEM at that layer’s catalogued capture illumination (sun_azimuth_deg/sun_elevation_deg on the catalog layer) and divides it out, mean-preserving (the layer’s overall brightness does not shift) and NaN-safe (nodata holes pass through untouched). Rules:

  • PSR colour layers are never deshaded — a permanently shadowed region has no capture-time sun to divide out.
  • A sunlit layer without catalogued sun angles is left un-deshaded, with a warning naming it that lands in the bake’s meta.json fusion warnings — never a silent skip.
  • If no fused DEM is available to model hillshade from, colour is left un-deshaded with a warning.

Three knob surfaces, one precedence rule:

SurfaceForm
Site manifestdeshade: true on the site entry (srb/terrain/manifest.yaml)
PythonRealTerrain(..., deshade=True)
CLI--deshade on srb asset download terrain / srb asset stack / srb asset doctor

Precedence is manifest or request: a request-level deshade=True can only turn deshading on; it can never disable a manifest-declared deshade: true. The effective flag is folded into the v2 cache key (any knob that changes baked pixels must be), so deshaded and non-deshaded bakes of the same region/site coexist as distinct cache entries; the default False keeps every pre-existing cache key byte-identical.

v1 sites cannot deshade. A v1 (single-source sources.dem) site has no fusion engine in its bake path, so deshade=True there is not silently inert — it raises (DeshadeUnsupportedError), and the CLI checks the same manifest fact before a single byte is fetched rather than after the whole DEM download.

A v2 catalog layer whose estimated fetch exceeds 1 GiB needs both the layer’s curated allow_large_download: true and the operator’s SRB_TERRAIN_LARGE_OK=1 environment variable at fetch time (check_layer_consent, an AND — env-var only, never an interactive prompt). Region requests add no second consent mechanism; the same gate governs every byte the region path moves. See allow_large_download + SRB_TERRAIN_LARGE_OK for the full rules and why srb asset stack’s printed “consent required” line is only a descriptive preview.

Companions and the ephemeris sun

A curated real-terrain site can carry companion products — a sky dome, a lighting rig (possibly ephemeris-baked; see Terrain lighting), and rock sets. The environment path attaches them automatically: BaseEnvCfg._add_scenery calls attach_companions() whenever the resolved scenery is a RealTerrain with attach_companions_enabled=True (the default; set it False to disable attachment).

Some shipped companion catalog entries are placeholders, so those products may be unavailable. If a site’s companion set cannot be built, the whole set is dropped with a warning and the environment continues without companions.

Both a companion lighting product and an enabled env.ephemeris want to own “what lights this scene”, so attachment is arbitrated (srb.terrain.companion_scene.arbitrate_companion_scene) under a fixed precedence. Every suppression warns once, naming both conflicting knobs:

#ConditionOutcome
1env.ephemeris.enabled and drive_sunlightThe ephemeris sun owns lighting: the companion lighting product is suppressed (warn-once). If drive_skydome is also set, the companion sky is suppressed too. Companion rocks still attach.
2Else, the companion set contains lightingThe companion owns the scene sun: scene.sunlight is set to None and the four static sunlight randomizers (randomize_sunlight_*) are skipped (warn-once when any were configured) — scene.lighting and scene.sunlight are mutually exclusive. A manifest lighting: "ephemeris" site under an env without env.ephemeris.enabled gets its baked ephemeris preset this way.
3The companion set contains skyThe companion sky replaces the domain-default skydome and randomize_skydome_orientation is nulled (deterministic curated sky) — unless the env explicitly configured a non-default skydome string (e.g. env.skydome=high_res, which wins over the companion sky) or explicitly disabled the skydome (env.skydome=null/false — an operator disable also wins; the companion must not silently re-add a dome).

Additional rules:

  • Region terrains have no curated companions (there is no manifest entry to declare them), so a region request attaches nothing — silently: absence is not a conflict, nothing warns. env.ephemeris is the way to get a real sun on a region patch; a manifest lighting: "ephemeris" is unreachable for region requests until a manifest entry exists.

Illumination sidecar

A baked patch can carry a precomputed illumination sidecar (illumination.npz, schema srb_illum/2) alongside its terrain.usd — a separate, out-of-band artifact produced by srb ephemeris import, not a bake input (it is not part of the CacheKey). RealTerrain.illumination_product_path exposes it (derived from the primary/index-0 patch only — see the per-env location variety limits above for what that means under num_locations > 1), and srb/core/mdp/illumination.py’s masked-spawn term and illumination_fraction_at reward helper consume it. See Illumination Products for the full schema, importer, and consumer reference.

Augmentation database (srb_augdb/1)

Terrain bakes may include a deterministic augmentation.npz sidecar containing synthetic crater and rock placements. The sidecar is generated from the baked patch and can be read with srb.terrain.augmentation.database.load_augmentation_db(baked_dir); a missing sidecar returns None, while invalid data raises ValueError. RealTerrain.augmentation_db_path refers to the primary patch’s sidecar.

The srb_augdb/1 schema stores craters as (N, 4) arrays of x_m, y_m, radius_m, depth_m, and rocks as (N, 6) arrays of x_m, y_m, z_m, scale, yaw_rad, asset_idx. Coordinates are patch-local. Placements are deterministic for the same seed, terrain, and preset. Steep terrain can reduce the number retained; requested and kept counts are recorded in bake metadata. Earth has no default augmentation; Moon and Mars default to rocks, while crater stamping requires an explicit crater preset and the augment_stamp option. Synthetic craters are a documented terrain profile, not a physical crater model.

Explicit rock scatter — augment="database"

RealTerrain.augment supports "none" (default), "simforge", and "database". The database mode places rocks at the recorded transforms, with augment_max_rocks limiting the number used. Missing or empty placement data produces a warning and leaves the terrain usable. "simforge" retains its existing generated-scatter behavior.

Synthetic super-resolution layer slot

The catalog schema’s radiometry enum includes a "synthetic" class, reserved for a future super-resolution or model-generated DEM/color product. The resolver treats it as a lowest-priority, opt-in slot:

  • Skipped by default. radiometry == "synthetic" layers never become candidates at all in _build_sorted_candidates unless the request explicitly admits them — a catalog with one synthetic DEM layer and one real DEM layer resolves to the real one only, no warning needed (there is nothing wrong to warn about).
  • Strictly last when admitted. The resolver’s total sort key is (is_synthetic, priority_class, native_gsd_m, layer_id) — a synthetic layer sorts after every non-synthetic candidate regardless of its own priority_class/native_gsd_m. Even a synthetic priority_class=1, native_gsd_m=0.5 layer loses to a real priority_class=9 layer.
  • Declared-stack sites admit synthetic layers implicitly. A v2 site’s manifest-declared dem_stack/color_stack entries are matched by layer_id, not filtered by radiometry — naming a synthetic layer in a manifest is explicit authorial intent, so it is never skipped the way an unrequested footprint-search candidate would be. The opt-in gate only matters for footprint-searched (resolve_stacks) and declared-site mask/quality/PSR-color candidates.

Opt-in surface. RegionRequest.allow_synthetic: bool = False and TerrainSpec.allow_synthetic: bool = False (threaded through exactly like deshade). The flag is deliberately not part of the cache-site slug (a region’s identity string), but it is pixel-changing once a catalog ships a synthetic layer, so CacheKey.hash12() appends "synthetic=1" to the hashed material only when the flag is True — the same append-only “inert default” precedent deshade=1 established. allow_synthetic=False keeps hash12() byte-identical to every existing golden hash; the default plan for every existing catalog is unaffected either way, since none ships a synthetic layer.

A region-form TerrainSpec.allow_synthetic=True whose region sub-spec disagrees (region.allow_synthetic=False) raises at spec-construction time — region terrain specs read the effective flag from RegionRequest.allow_synthetic, so a spec-level flag that the region does not also carry would silently do nothing, which the boundary check refuses to allow.

Scope. The slot is schema + resolver plumbing only. No shipped catalog carries a synthetic layer; no super-resolution model is integrated. This is unrelated to the augmentation database’s rock scatter, which is wired explicitly via augment="database" — no synthetic-radiometry product feeds rock/crater placement today, and nothing here changes with that landing.

Current limits

  • Catalog coverage is limited to the products listed in Catalog status per body. Uncovered requests use procedural terrain in environments and fail on the CLI.
  • Catalog sources are public and unauthenticated. A source requiring an API key or private-bucket credentials cannot be added with the current providers.
  • cog_remote is a compatibility label: SRB downloads and verifies the full source before reading a local window. First use needs space for that source; the large-download consent gate applies where configured.
  • Integer colour rasters currently have no separate validity-mask channel. For cross-CRS reads without source nodata metadata, out-of-footprint pixels may be read as zero. Avoid interpreting those pixels as valid colour data.
  • Companion attachment is all-or-nothing per site.
  • Region num_locations bakes every requested patch serially, even when a run uses fewer environments. The selected patch for environment i is i % num_locations; stacked scenes use the first patch only.