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
pyramids.json— an OGC API-Records crawlable catalog (also the muDM pyramid index); followrel=itemlinks to each dataset'sdatasets/<id>/tilejson.jsondescriptor (the per-dataset record).catalog.parquet— one row per dataset, queryable with DuckDB over HTTP (no server):
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.