← Home

Programmatic & agent access

Every dataset is discoverable and fetchable with static files only — no server. Start at /pyramids.json (an OGC API-Records crawlable catalog), or /llms.txt.

Coordinate model (read this first)

Coordinates are image-pixel [X, Y, Z] in micrometres, NOT longitude/latitude. Spatial bounds are carried in mudm:bounds_um / mudm:coordinate_space, never in WGS84 fields.

Discover datasets

import duckdb
duckdb.sql("INSTALL httpfs; LOAD httpfs;")
duckdb.sql("SELECT id, title, features_url FROM 'https://ngrf.mudm.io/catalog.parquet' "
           "WHERE organism = 'Homo sapiens'").show()

Fetch raster tiles

https://ngrf.mudm.io/tiles2d/<id>/raster/{z}/{x}/{y}.png

Per-dataset zoom range + image size are in the tilejson.json descriptor (below).

Fetch vector tiles (MVT / protobuf) — for viewers

Cell / nucleus / transcript geometry is served as standard Mapbox Vector Tiles (gzipped protobuf) — the same tiles the bundled viewer renders, consumable by any MVT client (Leaflet.VectorGrid, MapLibre, OpenLayers):

https://ngrf.mudm.io/tiles2d/<id>/vectors/{z}/{x}/{y}.pbf

The per-dataset descriptor https://ngrf.mudm.io/datasets/<id>/tilejson.json (TileJSON 3.0; also the dataset's OGC API-Records record, linked rel=item from pyramids.json) is what a client reads to render: the tile URL template, the vector layers (e.g. cells, nuclei, transcripts) with geometry type + min/max zoom, the image bounds in µm, and — when present — a facets block declaring the per-cell attribute store.

Fetch per-cell attributes (facet store — range-readable)

High-cardinality per-cell values (protein-marker intensities, gene expression) are not in the tiles; they live in a typed, cell_id-keyed Parquet facet store, joined to tile features by cell_id (the bundled viewer range-reads one column to color cells):

https://ngrf.mudm.io/tiles2d/<id>/facets/markers.parquet     # wide: cell_id + one column per marker (CODEX/CosMx/MERSCOPE)
https://ngrf.mudm.io/tiles2d/<id>/facets/expression.parquet  # long: cell_id, gene, count (sparse panels, e.g. Xenium)

The descriptor's facets block gives the store href, layout (wide/long), join key (cell_id), and field list. Snappy + BYTE_STREAM_SPLIT, so it reads in-browser with pure-JS Parquet (e.g. hyparquet) — no zstd.

Fetch features (muDM Parquet — range-readable)

The features.parquet/ tree is muDM binary tile geometry, NOT GeoParquet/WKB, Hive-partitioned by zoom=. Range-read only the partitions you need:

import duckdb
duckdb.sql("INSTALL httpfs; LOAD httpfs;")
duckdb.sql("SELECT feature_id, geom_type, tags "
           "FROM read_parquet('https://ngrf.mudm.io/tiles2d/<id>/features.parquet/**/*.parquet', "
           "hive_partitioning=true) WHERE zoom = 5 LIMIT 10").show()

Columns: tile_x, tile_y, feature_id, geom_type, positions, indices, ring_lengths, tags, zoom. Decode positions/indices/ring_lengths per the muDM geometry spec — these are binary, not WKB, so ST_* functions do not apply. The download zips are an archival/bulk bundle only.

ML pipelines

Each dataset has a Croissant descriptor at https://ngrf.mudm.io/datasets/<id>/croissant.json (loadable by TensorFlow/JAX/PyTorch via TFDS, indexable by Google Dataset Search / HuggingFace).

CORS & HTTP Range

Objects are served with Access-Control-Allow-Origin: * and HTTP Range (Accept-Ranges: bytes); .parquet is served uncompressed so range reads address real byte offsets.