Tile Metadata¶
muDM describes tiled, multi-resolution datasets with a small set of Pydantic models in mudm.tilemodel. These follow the TileJSON 3.0 convention used by web maps, adapted for microscopy: physical units, multi-axis coordinate systems, and ontology-aware field metadata. This guide shows how to build and validate that metadata in Python.
These models describe tiles — they do not make them
The models on this page only describe a tiled dataset: where the tiles live, what zoom levels exist, the coordinate system, and the property schema each layer carries. The tiling engines and pipelines — slicing a FeatureCollection into tiles across zoom levels, writing tile files, and emitting these manifests — live in the separate mudm-tools package. Start there for processing work:
- 2D tiling and 3D tiling guides.
- The TileJSON reference, which documents these same
mudm.tilemodelmodels from the consumer/producer side, including the formal field-by-field spec.
Why TileJSON?
A microscopy slide is often gigapixels in size. As with web maps, you cannot load it all at once, so it is split into tiles across zoom levels (a pyramid). TileJSON is the well-understood manifest format describing such a pyramid; muDM reuses it so existing map tooling concepts transfer directly.
The tile manifest at a glance¶
A tile manifest is a TileModel. At minimum it needs a tilejson version string, a list of tiles URLs/paths, and one or more vector_layers. The placeholders {zlvl}, {x}, {y} are filled in by the tile reader at request time — {zlvl} is the zoom level, {x} / {y} the tile column and row.
Build and validate a tile manifest¶
The following example builds a complete manifest in Python, then validates it through TileJSON, the root wrapper used to parse manifests from disk or the network.
from mudm import TileModel, TileJSON, TileLayer
from mudm.tilemodel import Multiscale, Axis, AxisType, Unit, Scale, Translation
# A vector layer: the features carried by these tiles and their property schema.
cells = TileLayer(
id="cells",
description="Segmented cell nuclei",
minzoom=0,
maxzoom=10,
fields={"cellType": "String", "area_um2": "Number"},
fieldranges={"area_um2": [0.0, 250.0]},
fieldenums={"cellType": ["neuron", "astrocyte", "microglia"]},
fielddescriptions={"area_um2": "Cross-sectional area in square microns"},
)
# A physical coordinate system for the dataset (see "Coordinate systems" below).
coords = Multiscale(
axes=[
Axis(name="x", type=AxisType.SPACE, unit=Unit.MICROMETER, description="x-axis"),
Axis(name="y", type=AxisType.SPACE, unit=Unit.MICROMETER, description="y-axis"),
],
coordinateTransformations=[Scale(scale=[0.325, 0.325]), Translation(translation=[0.0, 0.0])],
)
tileset = TileModel(
tilejson="3.0.0",
name="Mouse cortex section 12",
description="DAPI + GFAP overlay, 20x objective.",
version="1.0.0",
tiles=["http://localhost:8080/tiles/{zlvl}/{x}/{y}.json"],
minzoom=0,
maxzoom=10,
bounds=[0.0, 0.0, 24000.0, 24000.0],
center=[12000.0, 12000.0, 0.0],
vector_layers=[cells],
multiscale=coords,
scale_factor=2.0,
)
# Round-trip through the root model to confirm it is a valid manifest.
doc = TileJSON.model_validate(tileset.model_dump())
print(doc.root.name) # Mouse cortex section 12
print(doc.root.vector_layers[0].id) # cells
multiscale transforms round-trip losslessly
Multiscale.coordinateTransformations is a discriminated union of
Identity / Translation / Scale keyed on type, so the concrete
scale / translation payloads survive model_dump() /
model_validate() and model_dump_json() / model_validate_json()
intact. (AffineTransform is applied to geometry separately — see
Coordinate Transforms — and is not a member of this list.)
To load a manifest from JSON (e.g. a file served by your tile host), validate the parsed dict:
import json
from mudm import TileJSON
raw = """
{
"tilejson": "3.0.0",
"tiles": ["http://localhost:8080/tiles/{zlvl}/{x}/{y}.json"],
"vector_layers": [{ "id": "cells", "fields": { "name": "string" } }]
}
"""
doc = TileJSON.model_validate(json.loads(raw))
print(doc.root.minzoom, doc.root.maxzoom) # 0 22 (defaults)
TileModel fields¶
| Field | Type | Default | Meaning |
|---|---|---|---|
tilejson |
str |
— (required) | TileJSON spec version, e.g. "3.0.0". |
tiles |
list[Path \| URL] |
— (required) | Tile URL/path templates with {zlvl} {x} {y} placeholders. |
name |
str |
None |
Human-readable dataset name. |
description |
str |
None |
Free-text description. |
version |
str |
None |
Version of the tileset (not the spec). |
attribution |
str |
None |
Attribution string (may contain HTML). |
template |
str |
None |
Mustache template for interactivity. |
legend |
str |
None |
Legend content. |
scheme |
str |
None |
Tile addressing scheme (e.g. "xyz"). |
grids |
Path \| URL |
None |
UTFGrid interaction data location. |
data |
Path \| URL |
None |
Companion data location. |
minzoom |
int |
0 |
Lowest (most zoomed-out) zoom level. |
maxzoom |
int |
22 |
Highest (most detailed) zoom level. |
bounds |
list[float] (4–10) |
None |
Bounding box. 4 values for 2D [minx, miny, maxx, maxy]; up to 10 for higher-dimensional extents. |
center |
list[float] (3–6) |
None |
Default center. 3 values for [x, y, zoom]; up to 6 for higher dimensions. |
fillzoom |
int |
None |
Zoom level whose tiles are overzoomed to fill higher levels. |
vector_layers |
list[TileLayer] |
— (required) | The layers carried by the tiles. |
multiscale |
Multiscale |
None |
Physical coordinate system (see below). |
scale_factor |
float |
None |
Linear downscale factor between adjacent zoom levels (commonly 2.0). |
Length constraints
bounds must have 4 to 10 floats and center 3 to 6 floats. Passing the wrong count raises a pydantic.ValidationError — this is exactly the kind of malformed manifest the validation tests reject. See Validation for how muDM tests accept valid documents and reject malformed ones.
Layers and field metadata¶
A TileLayer declares one vector layer and, crucially, the schema and semantics of its feature properties. Readers use this to build legends, filter UIs, and tooltips without scanning every feature.
from mudm import TileLayer
layer = TileLayer(
id="cells",
description="Segmented cell nuclei",
minzoom=0,
maxzoom=10,
# Property name -> type label (free-form, e.g. "String", "Number", "Bool").
fields={"cellType": "String", "area_um2": "Number"},
# Min/max (or allowed list) per numeric/ordinal field.
fieldranges={"area_um2": [0.0, 250.0]},
# Allowed categorical values per field.
fieldenums={"cellType": ["neuron", "astrocyte", "microglia"]},
# Human-readable description per field.
fielddescriptions={"area_um2": "Cross-sectional area in square microns"},
)
| Field | Type | Purpose |
|---|---|---|
id |
str |
Unique layer identifier (required). |
fields |
dict[str, str] |
Maps property name to a type label. |
minzoom / maxzoom |
int |
Zoom range where this layer is present (defaults 0 / 22). |
description |
str |
Layer description. |
fieldranges |
dict[str, list] |
Numeric/ordinal range hints per field. |
fieldenums |
dict[str, list[str]] |
Allowed categorical values per field. |
fielddescriptions |
dict[str, str] |
Per-field human-readable descriptions. |
vocabularies |
dict[str, Vocabulary] |
Ontology bindings for property values (see below). |
Deriving the schema automatically
You rarely hand-write fields, fieldranges, and fieldenums. The mudm-tools tiling pipeline scans a muDM file's feature properties and fills these in for you. See the 2D tiling guide for the end-to-end recipe.
Binding fields to an ontology¶
vocabularies maps a field name to a Vocabulary, which resolves raw property values to formal ontology terms. This makes the data FAIR-friendly: a downstream tool can turn "neuron" into a Cell Ontology URI.
from mudm import TileLayer, Vocabulary, OntologyTerm
layer = TileLayer(
id="cells",
fields={"cellType": "String"},
fieldenums={"cellType": ["neuron", "astrocyte"]},
vocabularies={
"cellType": Vocabulary(
namespace="http://purl.obolibrary.org/obo/CL_",
description="Cell Ontology types",
terms={
"neuron": OntologyTerm(uri="http://purl.obolibrary.org/obo/CL_0000540", label="neuron"),
"astrocyte": OntologyTerm(uri="http://purl.obolibrary.org/obo/CL_0000127", label="astrocyte"),
},
)
},
)
The Vocabulary and OntologyTerm models are documented in full on the Ontology Vocabularies guide.
Coordinate systems¶
The multiscale field attaches a physical coordinate system to the tileset via Multiscale, harmonized with the OME-NGFF model. It answers: what are the axes, in what units, and how do pixel/tile coordinates map to physical space?
from mudm.tilemodel import Multiscale, Axis, AxisType, Unit, Scale, Translation
coords = Multiscale(
axes=[
Axis(name="x", type=AxisType.SPACE, unit=Unit.MICROMETER),
Axis(name="y", type=AxisType.SPACE, unit=Unit.MICROMETER),
Axis(name="c", type=AxisType.CHANNEL),
Axis(name="t", type=AxisType.TIME, unit=Unit.MICROMETER), # any Unit may be attached
],
coordinateTransformations=[
Scale(scale=[0.325, 0.325, 1.0, 1.0]), # microns per pixel on x/y
Translation(translation=[0.0, 0.0, 0.0, 0.0]),
],
)
Axes¶
An Axis has a name, an optional type, an optional unit, and a description.
AxisType is a StrEnum with three members:
AxisType |
Wire value |
|---|---|
AxisType.SPACE |
"space" |
AxisType.TIME |
"time" |
AxisType.CHANNEL |
"channel" |
Units¶
Unit is a StrEnum covering SI length scales plus a few microscopy-relevant extras. Because it is a StrEnum, the value equals the lowercase string (Unit.MICROMETER == "micrometer"), so it serializes cleanly to JSON.
Notable members:
Unit |
Wire value |
|---|---|
Unit.MICROMETER |
"micrometer" |
Unit.NANOMETER |
"nanometer" |
Unit.MILLIMETER |
"millimeter" |
Unit.METER |
"meter" |
Unit.CENTIMETER |
"centimeter" |
Unit.ANGSTROM |
"angstrom" |
Unit.PIXEL |
"pixel" |
Unit.DEGREE |
"degree" |
Unit.RADIAN |
"radian" |
The full SI ladder is available too — from Unit.YOCTOMETER through Unit.YOTTAMETER — along with imperial units (Unit.INCH, Unit.FOOT, Unit.YARD, Unit.MILE) and Unit.PARSEC.
Coordinate transformations¶
coordinateTransformations is an ordered list applied in sequence. The mudm.tilemodel module defines three OME-aligned transformation types, all subclasses of CoordinateTransformation:
| Class | type value |
Payload |
|---|---|---|
Identity |
"identity" |
none |
Translation |
"translation" |
translation: list[float] |
Scale |
"scale" |
scale: list[float] |
from mudm.tilemodel import Identity, Scale, Translation
print(Identity().type) # identity
print(Scale(scale=[0.325, 0.325]).type) # scale
print(Translation(translation=[0.0, 0.0]).type) # translation
A fourth transform: AffineTransform
A fourth CoordinateTransformation subclass, AffineTransform (type: "affine", a 4×4 matrix), lives in mudm.transforms. It is not a member of the serialized coordinateTransformations list — that list admits only the three types above, the OME/TileJSON wire form. Use AffineTransform to apply a full affine to geometry (see Coordinate Transforms), or record a single affine in Multiscale.transformationMatrix.
Multiscale also accepts a raw transformationMatrix (list[list[float]]) as an alternative to the transformation list — useful when you already have an affine matrix.
Applying transforms to geometry
The multiscale block describes a coordinate system. To actually apply transforms to geometry — converting between voxel and physical coordinates, composing affines — see Coordinate Transforms, which documents mudm.transforms.AffineTransform and the voxel/physical helpers.
Pyramid manifests¶
A single physical dataset often contains several pyramids at once — for example one per imaging channel. PyramidJSON is a top-level manifest that lists them, each as a PyramidEntry.
from mudm import PyramidJSON, PyramidEntry
manifest = PyramidJSON(
pyramids=[
PyramidEntry(
id="dapi",
label="DAPI channel",
tilejson="dapi/tilejson3d.json",
features="dapi/features.json",
tiles=4096,
feature_count=18234,
size_bytes=734003200,
),
PyramidEntry(id="gfap", label="GFAP channel"), # paths fall back to defaults
]
)
print(manifest.version) # 1.0
print(manifest.pyramids[1].tilejson) # tilejson3d.json (default)
print(manifest.pyramids[1].features) # features.json (default)
PyramidEntry field |
Type | Default | Meaning |
|---|---|---|---|
id |
str |
— (required) | Unique identifier, used as the directory name. |
label |
str |
None |
Human-readable display label. |
tilejson |
str |
"tilejson3d.json" |
Relative path to the tile metadata file. |
features |
str |
"features.json" |
Relative path to the muDM FeatureCollection. |
tiles |
int |
None |
Total number of tile files. |
feature_count |
int |
None |
Number of unique features. |
size_bytes |
int |
None |
Total size on disk in bytes. |
PyramidJSON.version defaults to "1.0".
GeoJSON compatibility
The features file referenced by each pyramid is a standard muDM FeatureCollection, which is itself valid GeoJSON. Any GeoJSON is valid muDM, and any muDM document is valid GeoJSON — see the Core data-model reference for the feature models.
Where the tiling happens¶
Everything above is metadata. To actually generate pyramids — slicing a FeatureCollection into tiles across zoom levels, writing the tile files, and emitting these manifests — use mudm-tools, the companion pipeline package:
- 2D tiling guide
- 3D tiling guide
- TileJSON reference — the same
mudm.tilemodelmodels, documented from the producer/consumer side.
API reference¶
TileJSON
¶
Bases: RootModel
The root object of a TileJSON file.
TileModel
¶
Bases: BaseModel
A TileJSON object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tilejson
|
str
|
The TileJSON version. |
required |
tiles
|
List[Union[Path, AnyUrl]]
|
The list of tile URLs. |
required |
name
|
Optional[str]
|
The name of the tileset. |
required |
description
|
Optional[str]
|
The description of the tileset. |
required |
version
|
Optional[str]
|
The version of the tileset. |
required |
attribution
|
Optional[str]
|
The attribution of the tileset. |
required |
template
|
Optional[str]
|
The template of the tileset. |
required |
legend
|
Optional[str]
|
The legend of the tileset. |
required |
scheme
|
Optional[str]
|
The scheme of the tileset. |
required |
grids
|
Optional[Union[Path, AnyUrl]]
|
The grids of the tileset. |
required |
data
|
Optional[Union[Path, AnyUrl]]
|
The data of the tileset. |
required |
minzoom
|
Optional[int]
|
The minimum zoom level of the tileset. |
required |
maxzoom
|
Optional[int]
|
The maximum zoom level of the tileset. |
required |
bounds
|
Optional[conlist(float, min_length=4, max_length=10)]
|
The bounds of the tileset. |
required |
center
|
Optional[conlist(float, min_length=3, max_length=6)]
|
The center of the tileset. |
required |
fillzoom
|
Optional[int]
|
The fill zoom level of the tileset. |
required |
vector_layers
|
List[TileLayer]
|
The vector layers of the tileset. |
required |
TileLayer
¶
Bases: BaseModel
A vector layer in a TileJSON file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The unique identifier for the layer. |
required |
fields
|
Union[None, Dict[str, str]]
|
The fields in the layer. |
required |
minzoom
|
Optional[int]
|
The minimum zoom level for the layer. |
required |
maxzoom
|
Optional[int]
|
The maximum zoom level for the layer. |
required |
description
|
Optional[str]
|
A description of the layer. |
required |
fieldranges
|
Optional[Dict[str, List[Union[int, float, str]]]]
|
The ranges of the fields. |
required |
fieldenums
|
Optional[Dict[str, List[str]]]
|
The enums of the fields. |
required |
fielddescriptions
|
Optional[Dict[str, str]]
|
The descriptions of the fields. |
required |
vocabularies
|
Optional[Dict[str, Vocabulary]]
|
Ontology vocabularies mapping property values to formal terms. |
required |
Multiscale
¶
Bases: BaseModel
A coordinate system for MuDM coordinates
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
axes
|
List[Axis]
|
The axes of the coordinate system |
required |
coordinateTransformations
|
Optional[List[CoordinateTransform]]
|
A list of coordinate transformations (identity/translation/scale). |
required |
transformationMatrix
|
Optional[List[List[float]]]
|
A single transformation matrix. Mutually exclusive with
|
required |
Axis
¶
Bases: BaseModel
An axis of a coordinate system
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
StrictStr
|
The name of the axis |
required |
type
|
Optional[AxisType]
|
The type of the axis |
required |
unit
|
Optional[Unit]
|
The unit of the axis |
required |
description
|
Optional[str]
|
A description of the axis |
required |
PyramidJSON
¶
Bases: BaseModel
Root manifest listing all available pyramids.
Attributes:
| Name | Type | Description |
|---|---|---|
version |
str
|
Manifest format version. |
pyramids |
List[PyramidEntry]
|
List of pyramid entries. |
PyramidEntry
¶
Bases: BaseModel
One pyramid in a PyramidJSON manifest.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique identifier for this pyramid (used as directory name). |
label |
Optional[str]
|
Human-readable display label. |
tilejson |
str
|
Relative path to TileModel3D metadata file. |
features |
Optional[str]
|
Relative path to MuDM FeatureCollection. |
tiles |
Optional[int]
|
Total number of tile files. |
feature_count |
Optional[int]
|
Number of unique features. |
size_bytes |
Optional[int]
|
Total size on disk in bytes. |
Where to next¶
- Coordinate Transforms — apply the transforms a
Multiscaledescribes to actual geometry. - Spatial Layout — compute bounds and lay out features in a coordinate system.
- Ontology Vocabularies — the
Vocabulary/OntologyTermmodels used in layervocabularies. - Validation — how muDM accepts valid manifests and rejects malformed ones.
- Core data-model reference — the feature/geometry models that fill the tiles.
- The
mudm-toolssite for the tiling pipelines themselves: 2D tiling, 3D tiling, and the TileJSON reference.