Fetch and stream grid data
You are viewing in-progress documentation for v2 (Beta). Switch to the stable version for the current production release.
A grid’s raster values are read back per band, per chunk. Large grids are tiled into chunks so you can stream them without pulling the whole array at once; you fetch each chunk and place it into the full array using the chunk’s offset. This guide walks that loop.
Prerequisites
Section titled “Prerequisites”-
An API key: my-api-key.
-
A domain and a completed grid: your-domain-id and your-grid-id — e.g. the fuel-load grid from Build a surface fuel grid from LANDFIRE.
Step 1 — Discover the chunk layout
Section titled “Step 1 — Discover the chunk layout”GET the grid. Two fields drive the rest: georeference.shape (the full
(height, width)) and chunks.count (how many chunks to fetch per band).
curl -X 'GET' \ 'https://api-v2-prod-nyvjyh5ywa-uw.a.run.app/domains/your-domain-id/grids/your-grid-id' \ -H 'accept: application/json' \ -H 'api-key: my-api-key'import fastfuels_sdk.v2 as ff
ff.set_api_key("my-api-key")
grid = ff.get_grid("your-domain-id", "your-grid-id")
info = grid.to_dict()print(info["georeference"]["shape"]) # full (height, width)print(info["chunks"]["count"]) # chunks to fetch per bandprint([band["key"] for band in info["bands"]]){ "id": "your-grid-id", "domain_id": "your-domain-id", "status": "completed", "bands": [ { "key": "fuel_load.1hr", "type": "continuous", "unit": "kg/m**2", "index": 0, "nodata": null }, { "key": "fuel_load.10hr", "type": "continuous", "unit": "kg/m**2", "index": 1, "nodata": null }, { "key": "fuel_load.100hr", "type": "continuous", "unit": "kg/m**2", "index": 2, "nodata": null }, { "key": "fuel_load.live_herb", "type": "continuous", "unit": "kg/m**2", "index": 3, "nodata": null }, { "key": "fuel_load.live_woody", "type": "continuous", "unit": "kg/m**2", "index": 4, "nodata": null }, { "key": "fuel_depth", "type": "continuous", "unit": "m", "index": 5, "nodata": null } ], "georeference": { "crs": "EPSG:32611", "transform": [ 29.49945488757609, 0.0, 720226.0, 0.0, -29.499454887583852, 5190646.9836466275 ], "shape": [30, 45] }, "chunks": { "shape": [512, 512], "count": 1, "count_by_axis": { "x": 1, "y": 1 } }}bands lists the band keys you can request. chunks.count here is 1 (this
grid fits in a single tile); a larger grid reports more, laid out
count_by_axis.
Step 2 — Fetch a band’s chunk
Section titled “Step 2 — Fetch a band’s chunk”GET /grids/{{GRID_ID}}/data/{band}/{chunk_index}. The response carries the
chunk’s own shape, an offset into the full array, the chunk’s affine
transform, and the values.
curl -X 'GET' \ 'https://api-v2-prod-nyvjyh5ywa-uw.a.run.app/domains/your-domain-id/grids/your-grid-id/data/fuel_load.1hr/0' \ -H 'accept: application/json' \ -H 'api-key: my-api-key'import fastfuels_sdk.v2 as ff
ff.set_api_key("my-api-key")
grid = ff.get_grid("your-domain-id", "your-grid-id")
# The SDK fetches every chunk of the band and places each at its offset —# dense or sparse, single- or multi-chunk — returning the full array.fuel_load = grid.to_numpy("fuel_load.1hr") # -> shape (height, width){ "shape": [30, 45], "order": "C", "metadata": { "index": 0, "shape": [30, 45], "offset": [0, 0], "transform": [ 29.438767164252283, 0.0, 720227.9398802927, 0.0, -29.438767164262632, 5190646.487014395 ] }, "data": { "format": "dense", "values": [ 1.3001821041107178, 0.2129608541727066, 0.2129608541727066, 1.3001821041107178, 1.3001821041107178, 0.2129608541727066, 0.2129608541727066, 0.02241693250834942, 0.11208466440439224, 0.11208466440439224, 0.11208466440439224, 0.11208466440439224 ] }}data.values is the real chunk data — a flat array of shape[0] × shape[1]
values in order ("C", row-major). It’s shown abridged above; the full
chunk here is 30 × 45 = 1350 floats. Two formats:
dense(the default) —valuesholds every cell, in order.sparse— request it with?array_format=sparse.valuesholds only the cells that differ fromfill_value, paired withindices; reconstruct by filling withfill_valuethen scatteringvaluesintoindices. This is worth requesting for chunks that are mostly one value (e.g. a masked grid that’s largely zero) — it also lets an oversized chunk fit under the response size limit.
Step 3 — Read the whole band
Section titled “Step 3 — Read the whole band”The SDK does the per-chunk loop for you. grid.to_numpy(band) fetches every
chunk, handles dense vs sparse, and places each at its offset — returning the
full (height, width) array ((z, y, x) for a voxel grid). grid.to_xarray()
returns every band as a georeferenced xarray.Dataset, with x/y coordinates
derived from the grid’s affine transform.
import fastfuels_sdk.v2 as ff
ff.set_api_key("my-api-key")
grid = ff.get_grid("your-domain-id", "your-grid-id")
# Every band at once as a georeferenced xarray.Dataset, with x / y coordinate# vectors derived from the grid's affine transform and the CRS on the dataset.dataset = grid.to_xarray()Working over raw HTTP with curl instead? Loop chunk_index from 0 to
chunks.count - 1 and position each chunk by its metadata.offset, exactly as
the responses above show.
Common pitfalls
Section titled “Common pitfalls”These apply when you fetch over raw HTTP (the curl path); the SDK’s
to_numpy() / to_xarray() handle band iteration, chunk offsets, and the
dense/sparse formats for you.
- Forgetting it’s per band. Each
data/{band}/{chunk_index}call returns one band. Loop overbands(from step 1) as well as over chunks. - Ignoring
offset. Concatenating chunks in fetch order mis-places them. Always position a chunk with itsmetadata.offset— that’s what makes the reassembled array correct for multi-chunk grids. - Assuming a format. The response format follows the
array_formatyou request (denseby default), and every chunk comes back in that format. Branch ondata.formatif your code requestssparseanywhere, so the reassembly handles both shapes. - Grid not
completed. Data is only readable once the grid finishes;chunksisnulland there’s nothing to fetch while it’s pending.