Expand description
Arrow-based tile payload format.
A tile’s payload is one or more layers. Each layer is a single Arrow
RecordBatch serialised as an Arrow IPC stream. Geometry is encoded
using the GeoArrow interleaved-coordinate convention so the payload can be
consumed directly by GeoArrow-aware renderers (e.g. @geoarrow/deck.gl).
§Per-layer schema
| column | type | notes |
|---|---|---|
id | UInt64 | feature id |
start_time | Int64 | Unix ms, absolute |
end_time | Int64 | Unix ms, absolute |
geometry | GeoArrow point / linestring / polygon | interleaved f64 lon/lat |
vertex_time | List<UInt16> deltas or List<Int64> (nullable) | per-vertex Unix ms (optional; see [build_vertex_time_array]) |
vertex_value | List<Float32> (nullable) | per-vertex scalar, e.g. SST (optional) |
triangles | List<UInt16> or List<UInt32> | pre-baked earcut indices, feature-local (optional; polygon only) |
<property> | Float64 or Dictionary<UInt16,Utf8> | one column per property |
All layers in one tile are concatenated with a tiny frame so a tile can
carry, say, a linestring layer and a point layer side by side. Two frame
shapes exist, selected by EncoderConfig::format_version (the payload
side of the packed format’s manifest.formatVersion — the two are bumped
in lockstep, see docs/spec/stt-packed-format.md §9):
v1 (format_version: 1, the 0.3.x wire shape — frozen, byte-identical):
[u16 layer_count | ALIGNED_FRAME_FLAG]
repeated: [u16 name_len][name utf8][u32 ipc_len][pad to 8][ipc stream bytes]The leading u16’s top bit (ALIGNED_FRAME_FLAG) marks the aligned
frame: zero padding after each ipc_len places every Arrow IPC stream at
an 8-byte boundary relative to the payload start, so readers can hand the
stream to an Arrow implementation zero-copy (Arrow buffers are 8-byte
aligned within a stream; the stream itself must start aligned for that
to survive). The pad length is not stored — readers derive it as
(8 - pos % 8) % 8 from the position after ipc_len. Frames without the
flag (every archive written before the flag existed) carry no padding and
decode exactly as before.
v2 (format_version: 2, default for new stt-build output): a
sectioned frame that hoists each layer’s Arrow IPC schema message into a
per-dataset template (referenced by blake3-128 hash, resolved through
the manifest’s schemas table) so the per-tile schema tax disappears, and
carries the per-tile-varying metadata in a compact TILE_META
section instead of the schema:
u16 0xFFFF # v2 escape (see FRAME_V2_ESCAPE)
u8 frame_version = 2
u8 flags = 0 # reserved, MUST be 0
u16 layer_count
per layer:
u16 name_len, [name utf8]
u8 ref_kind_core # 0 = INLINE_SCHEMA_CORE section present;
# 1 = the next 16 bytes are the template hash
[16] core template hash # iff ref_kind_core == 1
u8 ref_kind_props # 0/1 as above; 2 = NO props sections at all
[16] props template hash # iff ref_kind_props == 1
u8 section_count
per section (TOC): u8 tag, u32 length # at-rest bytes, pad excluded
[pad to 8, derived]
per section: [section bytes][pad to 8, derived]Reserved columns (id/times/geometry/vertex_*/triangles) form the CORE
batch; property columns form the PROPS batch with its own schema/template
(emitted only when properties exist). Each *_BATCH section is the IPC
stream’s tail — dictionary batch(es) + record batch + end-of-stream —
and a reader materialises concat(template, tail) for a stock Arrow
reader. Unknown section tags are skippable via the TOC. Rows are
stable-sorted by start_time at encode (after id assignment), declared by
TILE_META.sorted. The v2 escape is unreachable from the v1 writer: the
v1 path caps layer_count below 0x7fff, so an aligned v1 frame can
never start with 0xFFFF.
Structs§
- Attr
Quant - Per-numeric-property quantization affine:
value = o + q * s, whereois the column minimum (the dequantization offset) andsthe requested ground precision (the step). Reconstruction is lossy to ≤s/2; the reader applies the identical math (tile.ts). - Columnar
Layer - One decoded/encodable tile layer.
- Decoded
Layer - A decoded tile layer: its name and the raw Arrow
RecordBatch. - Encoder
Config - Resolved, explicit encoder settings — the values the tile encoder reads at encode time (coordinate + attribute quantization, vector grouping, the point-elevation fold, vertex-time precision).
- Quant
Affine - Per-layer coordinate-quantization affine. Coordinates ship as
i32grid indices; the decoder reconstructslon = x0 + qx*sx,lat = y0 + qy*sy.sx/sy(degrees per quantum) are sized from a target ground precision in meters at the layer’s mid-latitude, so the worst-case error is ≤ half a quantum (~meters/2). This trades GeoArrow self-describing Float64 for size (coords are the dominant, near-incompressible column) and is opt-in. - Template
Collector - Thread-safe encode-side sink for the schema templates a v2 build produces.
- Template
Registry - Decode-side template lookup: blake3-128 hash → raw template bytes.
- Tile
Meta - The per-tile-varying metadata a v2 frame carries in its
TILE_METAsection instead of the (now dataset-constant, template-resident) Arrow schema metadata. Canonical serialization (design §4.3): JSON, keys sorted — field order below IS alphabetical and theqamap is aBTreeMap— no whitespace. Readers MUST ignore unknown keys (serde’s default here), so the section can evolve additively. Presence rules: a key is present iff the corresponding feature is (qaomits non-quantized columns entirely;t0iff a start-time column exists;vtiff delta-encoded vertex_time;vbiff a value matrix). - Vector
Group - A build-time directive to fuse several scalar numeric properties into one
GPU-ready interleaved
PropertyColumn::Vector. The component order is the vector’s component order (e.g.["qx","qy","qz","qw"]→instanceQuaternions). Applied at encode time (like the quantization maps), so the producer keeps emitting plain scalar columns and the renderer still binds the result zero-copy. Seeset_vector_groups.
Enums§
- Geometry
Column - Geometry for one layer, grouped by kind. Every feature in a layer shares one kind — the tiler emits a separate layer per geometry type.
- Property
Column - A property column. Values are per-feature and may be missing.
- Vector
Elem - Leaf element type of a
PropertyColumn::Vector— the GPU upload type the renderer binds the decoded child buffer as.
Constants§
- ALIGNED_
FRAME_ FLAG - Top bit of the layer frame’s leading u16: marks an aligned frame whose
IPC streams are padded to 8-byte boundaries (see the module docs). The
remaining 15 bits carry the layer count, so a tile may have at most
0x7ffflayers. Old frames never set this bit (layer counts are tiny). - DEFAULT_
VERTEX_ TIME_ MAX_ STEP_ MS - Default ceiling (ms) on the u16-delta
vertex_timequantization step. - FORMAT_
VERSION_ V1 - The frame format this encoder emits when nothing opts in explicitly —
v1, the frozen 0.3.x wire shape.
stt-buildopts into v2 explicitly (its--format-versiondefault is 2);stt-serveand every other existing caller stays v1 without changes (design §7). - FORMAT_
VERSION_ V2 - The sectioned template-referencing frame (packed formatVersion 2).
- FRAME_
V2_ ESCAPE - Leading u16 of a v2 layer frame. Deliberately unreachable from the v1
writer: the v1 encoder rejects tiles whose
count | ALIGNED_FRAME_FLAGwould collide with this escape, so the two frame shapes are disjoint on their first two bytes. ManifestformatVersionremains the authoritative discriminator (design ★F6); this escape is defense-in-depth. - MIN_
QUANTIZE_ COORDS_ M - Finest coordinate-quantization precision (meters) the world-anchored grid
supports: the grid is anchored at
(-180, -90)with a uniform step ofmeters / M_PER_DEG_LATdegrees, so the largest longitude index is360 * M_PER_DEG_LAT / meters. Below this floor (≈ 0.0187 m, ~19 mm) that index exceedsi32::MAXand quantization would silently snap far-east coordinates to wrong locations — so finer precisions are rejected. - SECTION_
CORE_ BATCH - v2 section tag: CORE batch IPC tail (dict batches + record batch + EOS).
- SECTION_
INLINE_ SCHEMA_ CORE - v2 section tag: full IPC schema prefix for the CORE batch (self-contained
mode,
ref_kind == REF_KIND_INLINE). - SECTION_
INLINE_ SCHEMA_ PROPS - v2 section tag: full IPC schema prefix for the PROPS batch (as 0x01).
- SECTION_
PROPS_ BATCH - v2 section tag: PROPS batch IPC tail (as 0x03, props schema).
- SECTION_
TILE_ META - v2 section tag: the canonical
TileMetaJSON. - STT_
QUANT_ ATTR_ META_ KEY - Field-metadata key flagging a numeric property column that ships as
fixed-point integer indices (smallest of
UInt16/Int32) instead ofFloat64. Its value is theAttrQuantJSON (value = o + q*s). Sibling ofSTT_QUANT_META_KEYfor geometry; lives on the property field, so a reader reconstructs Float64 the same way it does coordinates. - STT_
QUANT_ META_ KEY - Schema-metadata key (on the
geometryfield) that flags a tile whose coordinates are fixed-pointi32grid indices rather than GeoArrow Float64 lon/lat. Its value is theQuantAffineJSON; absent ⇒ standard Float64. - TRIANGLES_
METADATA_ KEY - Schema-metadata key set on layers that carry pre-baked triangle indices.
Functions§
- decode_
layer - Decode a single-layer Arrow IPC stream into a
RecordBatch. - decode_
tile - Decode a full tile payload (the layer frame) into its layers.
- decode_
tile_ with_ templates decode_tilewith the dataset’sTemplateRegistry, so v2 frames can resolve their 16-byte template-hash references. v1 frames decode unchanged (the registry is simply unused).- encode_
layer - Encode a single layer to an Arrow IPC stream.
- encode_
layer_ quantized encode_layerwith optional fixed-point coordinate quantization.- encode_
layer_ with encode_layerwith a fully-explicitEncoderConfig— no process-wide globals are read. This is the concurrency- and multi-config-safe entry point (e.g. a dynamic server fronting several datasets with different settings).- encode_
tile - Encode a full tile payload (one or more layers) with the layer frame.
- encode_
tile_ quantized encode_tilewith optional fixed-point coordinate quantization applied to every layer (seeencode_layer_quantized).quantize_m = Noneis byte-identical toencode_tile; the other encoder settings come from the process-wide globals.- encode_
tile_ with encode_tilewith a fully-explicitEncoderConfig— no process-wide globals are read. The concurrency- and multi-config-safe entry point a dynamic per-request tile server uses so each dataset/request encodes with its own settings without touching shared state.- format_
version - The build-global frame format version.
- frame_
v2_ template_ refs - Walk ONLY a v2 frame’s header structure — escape/version/flags/count,
per-layer name + schema ref_kinds (+ their 16-byte hashes), TOC-driven
section skips; no Arrow decode, no section parse — and return every
template hash the frame references. The packed-format validators use this
to prove each referenced hash resolves in
manifest.schemaswithout decoding tiles. Errors (never panics) on truncated/malformed headers. - is_
frame_ v2 - Whether a tile payload carries the v2 sectioned frame (leading u16 is the
FRAME_V2_ESCAPE). The manifest’sformatVersionremains authoritative (★F6) — this sniff is defense-in-depth for the payload level. - point_
elevation_ column - The current point-elevation column name (empty ⇒ disabled).
- quantize_
attrs - The current numeric-property quantization map (a clone).
- quantize_
attrs_ auto - Whether automatic numeric-property quantization is enabled.
- quantize_
coords_ m - The build-global quantization precision in meters, or
Nonewhen off. - set_
format_ version - Set the build-global frame format version for every subsequent default
encode_tilecall. Errors (storing nothing) on anything but 1 or 2. - set_
point_ elevation_ column - Set the build-global point-elevation column name (see above). Empty disables.
- set_
quantize_ attrs - Replace the build-global numeric-property quantization map.
- set_
quantize_ attrs_ auto - Enable/disable automatic range-adaptive quantization of every otherwise-raw
Float64numeric property (see [QUANTIZE_ATTRS_AUTO]). Explicit precisions inset_quantize_attrsalways win. - set_
quantize_ coords_ m - Set the build-global coordinate quantization precision in meters for every
subsequent default
encode_tilecall.<= 0(the default) turns it off — coordinates stay Float64 GeoArrow. Seeencode_layer_quantizedfor the size/precision trade-off. Errors (storing nothing) for a positive precision belowMIN_QUANTIZE_COORDS_M, which would overflow the world grid’s longitude index. - set_
template_ collector - Install (or clear, with
None) the build-globalTemplateCollector. - set_
vector_ groups - Replace the build-global vector-group list.
- set_
vertex_ time_ max_ step_ ms - Override the u16-delta
vertex_timestep ceiling for every subsequentencode_layercall. Values below 1 ms clamp to 1 (every layer with a span beyond u16 milliseconds then takes the exactList<Int64>path). - template_
collector - The current build-global template collector, if any.
- tessellate_
polygon - Tessellate one polygon feature (a list of rings) using earcut. Returns the flat triangle index list — each triple of indices is one triangle, indices are LOCAL (relative to the start of the feature’s coordinate run, where the exterior ring sits first followed by every hole).
- validate_
quantize_ coords_ m - Validate a coordinate-quantization precision against the world grid’s
MIN_QUANTIZE_COORDS_Mfloor.meters <= 0(quantization off) passes. Public so config-building paths (EncoderConfigconsumers likestt-serve) can fail fast at startup with the same error the global setter and the encode-time guard produce. - vector_
groups - The current vector-group list (a clone).
- vertex_
time_ max_ step_ ms - The currently configured u16-delta
vertex_timestep ceiling (ms).
Type Aliases§
- Coord
- A single coordinate pair (lon, lat) in WGS84 degrees.