stac #
SpatioTemporal Asset Catalog (STAC) 1.1.0 items in OCaml.
Earth-observation archives publish their images as STAC items. Search
tools, tilers and notebooks can then find and read an image without knowing
anything about the archive it sits in. (A tiler is a server that reads the
pixels a STAC asset points at and cuts the large georeferenced image into
the small fixed-size tiles a web map asks for at each zoom level.) A STAC
item is a GeoJSON Feature (RFC 7946). Its properties hold the acquisition
metadata and its assets point at the pixels. This library builds items and
serialises them. It writes the footprint with the nox-json.geojson codec,
so the geometry comes out of an RFC 7946 implementation.
Stac.Itemis the item itself: an identifier, a footprint with its bounding box, the acquisition time, its links and its assets.Stac.Assetsays where some bytes live and what they contain.Stac.Banddescribes one band of the values in an asset.Stac.Data_typeis the sample width, named as the specification names it.Stac.Linkis one link out of an item.
Each constructor returns an error for anything the published JSON schema
forbids. That covers an empty identifier, a bounding box of any length but
four or six, a collection without a matching link, an empty band, and a
property that repeats the datetime. The schemas are committed under
test/schemas. The tests take the member names, the required lists and the
data_type vocabulary from those files, and REGEN=1 dune build @traces
fetches them again.
Installation #
Install with opam:
$ opam install stac
If opam cannot find the package, it may not yet be released in the public
opam-repository. Add the overlay repository, then install it:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install stac
Usage #
This is how the catalogue describes one capture:
# let point =
Geojson.point
(Geojson.Geojson_object.v [| -121.7222; 39.9962 |] None
(Json.Value.object' []))
val point : [> `Point of Geojson.Position.t Geojson.object' ] =
`Point <abstr>
# let at s = match Ptime.of_rfc3339 s with Ok (t, _, _) -> t | Error _ -> assert false
val at : string -> Ptime.t = <fun>
# let pixels =
Result.get_ok
(Stac.Asset.v ~href:"/captures/cap-0000/pixels"
~media_type:"application/octet-stream" ~roles:[ "data" ]
~bands:
[ Result.get_ok
(Stac.Band.v ~name:"nir" ~data_type:Stac.Data_type.Uint16 ()) ]
())
val pixels : Stac.Asset.t = <abstr>
# let item =
Result.get_ok
(Stac.Item.v ~id:"cap-0000"
~geometry:(point, [| -121.7222; 39.9962; -121.7222; 39.9962 |])
~datetime:(Stac.Datetime.At (at "2026-07-16T18:50:53Z"))
~links:
[ Result.get_ok
(Stac.Link.v ~rel:"self" ~href:"/captures/cap-0000"
~media_type:"application/geo+json" ()) ]
~assets:[ ("image", pixels) ] ())
val item : Stac.Item.t = <abstr>
# print_endline (Stac.Item.to_string item)
{"type":"Feature","stac_version":"1.1.0","id":"cap-0000","geometry":{"type":"Point","coordinates":[-121.7222,39.9962]},"bbox":[-121.7222,39.9962,-121.7222,39.9962],"properties":{"datetime":"2026-07-16T18:50:53Z"},"links":[{"rel":"self","href":"/captures/cap-0000","type":"application/geo+json"}],"assets":{"image":{"href":"/captures/cap-0000/pixels","type":"application/octet-stream","roles":["data"],"bands":[{"name":"nir","data_type":"uint16"}]}}}
- : unit = ()
A band is one spectral channel of an image; this one is near-infrared
(nir). It gives only the sample width. With no raster:scale, a band
holds raw digital numbers, the integers the sensor recorded for each pixel.
Those numbers turn into a physical quantity such as reflectance only once a
scale and an offset are applied. Leaving the scale out tells a reader not
to take them for one.
The schema allows two shapes of footprint and the library writes both. An
item without a geometry has null in its place and no bounding box:
# print_endline
(Stac.Item.to_string
(Result.get_ok
(Stac.Item.v ~id:"cap-0001"
~datetime:(Stac.Datetime.At (at "2026-07-16T18:50:53Z"))
~links:[] ~assets:[] ())))
{"type":"Feature","stac_version":"1.1.0","id":"cap-0001","geometry":null,"properties":{"datetime":"2026-07-16T18:50:53Z"},"links":[],"assets":{}}
- : unit = ()
Specification #
- STAC 1.1.0, item specification and common metadata.
- The published JSON schemas, committed
under
test/schemas. - RFC 7946, GeoJSON, implemented by
nox-json.geojson. - RFC 3339 section 5.6, the datetime spelling.
License #
ISC. See LICENSE.md.