Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Extend `bidx` to accept a band name (matching the `name` of a Band Object declared on the asset via
`eo:bands`, `raster:bands`, or the STAC 1.1+ common `bands` construct) alongside a 1-based index, and
restructure it as one array per position in `assets`, so a band selection can be tied unambiguously to a
specific asset when there is more than one. Documents `bidx`'s indexing convention, which was previously
unspecified ([#18](https://github.com/stac-extensions/render/issues/18))
- Add a Planet example (`examples/item-planet.json`), demonstrating `bidx` name-based selectors on a genuine
single multi-band asset (PlanetScope's 8-band analytic product), using STAC 1.1.0's common `bands` construct

## [2.1.0] - 2026-09-22

### Added
Expand Down
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Rendering extension aims at providings consumers with the possible rendering of
- Examples:
- [Landsat-8 example](examples/item-landsat8.json): Shows the basic usage of the extension in a landsat-8 STAC Item
- [Sentinel-2 example](examples/item-sentinel2.json): Shows the basic usage of the extension in a Sentinel-2 STAC Item
- [Planet example](examples/item-planet.json): Shows `bidx` selecting named bands from a single multi-band asset
- [Collection example](examples/collection.json): Shows the basic usage of the extension in a collection
- [JSON Schema](json-schema/schema.json)
- [Changelog](./CHANGELOG.md)
Expand Down Expand Up @@ -46,6 +47,7 @@ The fields in the table below can be used in these parts of STAC documents:
| resampling | string | Resampling algorithm to apply to the referenced assets. See [GDAL resampling algorithm](https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r) for some examples. |
| expression | string, object, array | Band arithmetic formula to apply to the referenced assets. The format is defined by the rendering application, e.g. a [TiTiler](https://developmentseed.org/titiler/) band math string or a [MapLibre](https://maplibre.org/maplibre-style-spec/expressions/) style expression array. |
| minmax_zoom | \[int] | Zoom levels range applicable for the visualization |
| bidx | \[\[int, string]] | Band(s) to select from each referenced asset, one entry per position in `assets` (an asset needing no sub-selection uses an empty array `[]`). Each selector is either a 1-based band index (matching the GDAL/rio-tiler/titiler convention) or a band name matching the `name` of a Band Object declared on that asset (via `eo:bands`, `raster:bands`, or the STAC [common `bands`](https://github.com/radiantearth/stac-spec/blob/v1.1.0/commons/common-metadata.md#bands) construct) — useful since `name` is not required on a Band Object. See [Band references](#band-references). |
| asset_as_band | boolean | Treat assets in `expression` as single bands. Required when expression uses multiple assets |

The `render` object is open ended, so additional fields can be provided according to the needs of the rendering application.
Expand All @@ -70,6 +72,60 @@ the second the green band and the third the blue band.
"assets": [ "red", "green", "blue" ]
```

## Band references

When one or more of the referenced `assets` is itself a multi-band raster, `bidx` selects which band(s) of
each asset to use.

`bidx` is an array with **one entry per position in `assets`** (`bidx.length` MUST equal `assets.length`),
where each entry is itself an array of the band selector(s) for that asset, in output order. An asset that
needs no sub-selection (e.g. it is already single-band, or all its bands should be used in their existing
order) uses an empty array `[]`. Note that JSON Schema cannot enforce the `bidx.length == assets.length` rule
itself; it is a MUST, not a validated constraint.

Each selector is either:

- a 1-based band index (matching the GDAL/rio-tiler/titiler convention: the first band is `1`, not `0`), with
no required correspondence to any STAC band metadata — only meaningful if you already know how that asset's
bands are physically ordered; or
- a band name, which MUST match the `name` of a
[Band Object](https://github.com/radiantearth/stac-spec/blob/v1.1.0/commons/common-metadata.md#bands)
declared on that asset, whether via its `eo:bands`, `raster:bands`, or the STAC 1.1+ common `bands`
construct (all three use the same `name` property). This ties the reference to metadata already present in
the Item or Asset, rather than to an implementation's internal band ordering. Since `name` is not a required
property of a Band Object, a name-based selector is only usable when the asset's bands actually declare one
— fall back to an index otherwise.

```json
"assets": [ "B04", "stacked" ],
"bidx": [ [], [ 3, "nir" ] ]
```

Here, `B04` is used as-is (empty array: no sub-selection needed), while two bands are selected from the
multi-band `stacked` asset: its 3rd band by index, and the band named `nir`.

See the [Planet example](examples/item-planet.json) for a realistic case: PlanetScope's 8-band analytic
product is delivered as a single multi-band GeoTIFF asset (unlike Sentinel-2/Landsat-8, which split each band
into its own asset), so `bidx` is the only way to select, say, a true-color RGB composite from it:

```json
"assets": [ "analytic" ],
"bidx": [ [ "red", "green", "blue" ] ]
```

A renderer that only accepts a numeric, per-asset band index (e.g. titiler's inline
`assets=<name>|bidx=<i1>,<i2>` syntax, which replaced its older flat, non-per-asset `bidx` query parameter for
the same reason `bidx` here is now per-asset) resolves a name-based selector to a 1-based index by looking up
its position in that asset's own band metadata:

```text
assets=B04&assets=stacked|bidx=3,5
```

(assuming `red` and `nir` are, respectively, the 3rd and 5th band declared on the `stacked` asset). Titiler
also accepts an inline `|bands=<name1>,<name2>` option; consult the target renderer's documentation for
whether it can resolve band names directly, or whether a client must perform this lookup itself.

## Rescaling

A rescaling of the values from the source asset(s) to the destination asset can be defined using the `vrt:rescale` field.
Expand Down Expand Up @@ -114,7 +170,7 @@ by simply specifying the `url` and `assets` query parameters.
| `colormap` | `colormap` | Color map JSON definition as defined in `colormap` object of the `asset` (overrides `colormap_name` if present ) |
| `color_formula` | `color_formula` | Color formula as defined in `color_formula` field of the `asset` |
| `resampling` | `resampling` | Resampling method to use when reprojecting the raster. |
| `bidx` | `bidx` | Dataset band indexes |
| `assets=<name>\|bidx=<i1>,<i2>` (or the legacy `bidx` param) | `bidx`, resolving any name-based selector to a 1-based index via the asset's band metadata | Per-asset dataset band indexes. See [Band references](#band-references). |
| `asset_as_band` | `asset_as_band` | Required when expression uses multiple single-band assets |

#### Shortwave Infra-red visual thermal signature example
Expand Down
10 changes: 1 addition & 9 deletions examples/item-landsat8.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,8 @@
],
"colormap_name": "rainbow",
"resampling": "bilinear",
"bidx": [
1
],
"width": 1024,
"height": 1024,
"bands": [
"B4",
"B3",
"B2"
]
"height": 1024
},
"ndvi": {
"title": "Normalized Difference Vegetation Index",
Expand Down
152 changes: 152 additions & 0 deletions examples/item-planet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
{
"type": "Feature",
"stac_version": "1.1.0",
"stac_extensions": [
"https://stac-extensions.github.io/render/v2.1.0/schema.json"
],
"id": "planet-psscene-8b-example",
"bbox": [
4.32,
50.82,
4.42,
50.9
],
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
4.32,
50.82
],
[
4.32,
50.9
],
[
4.42,
50.9
],
[
4.42,
50.82
],
[
4.32,
50.82
]
]
]
},
"properties": {
"datetime": "2024-06-01T10:30:00Z",
"platform": "PlanetScope",
"instruments": [
"PSB.SD"
],
"gsd": 3,
"renders": {
"true-color": {
"title": "True color composite",
"assets": [
"analytic"
],
"rescale": [
[
0,
10000
],
[
0,
10000
],
[
0,
10000
]
],
"resampling": "bilinear",
"bidx": [
[
"red",
"green",
"blue"
]
]
},
"false-color": {
"title": "False color (NIR-Red-Green) composite",
"assets": [
"analytic"
],
"rescale": [
[
0,
10000
],
[
0,
10000
],
[
0,
10000
]
],
"resampling": "bilinear",
"bidx": [
[
"nir",
"red",
"green"
]
]
}
}
},
"links": [],
"assets": {
"analytic": {
"title": "8-band analytic surface reflectance",
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
"roles": [
"data"
],
"href": "https://example.com/data/planet/psscene_8b_analytic.tif",
"bands": [
{
"name": "coastal",
"description": "Coastal Blue, 431-452nm"
},
{
"name": "blue",
"description": "Blue, 465-515nm"
},
{
"name": "green05",
"description": "Green I, 513-549nm"
},
{
"name": "green",
"description": "Green, 547-583nm"
},
{
"name": "yellow",
"description": "Yellow, 600-620nm"
},
{
"name": "red",
"description": "Red, 650-680nm"
},
{
"name": "rededge",
"description": "Red Edge, 697-713nm"
},
{
"name": "nir",
"description": "Near-Infrared, 845-885nm"
}
]
}
}
}
5 changes: 4 additions & 1 deletion json-schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,10 @@
"bidx": {
"type": "array",
"items": {
"type": "number"
"type": "array",
"items": {
"type": ["number", "string"]
}
}
},
"asset_as_band": {
Expand Down
Loading