Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,8 @@ ngff-zarr upgrade src.zarr -o dst.zarr --to 0.5

The target version is selected with `--to` (alias `--version`), one of `0.4`,
`0.5`, `0.6`, or `0.9.dev1` (default `0.6`). `0.9.dev1` is the development
version that adopts RFC-3; OME publishes no JSON Schema for it yet, so
`--validate` cannot check a store at that version. Add `--validate` to validate
the source metadata against the NGFF schema while reading. For the write-to-new-store mode,
version that adopts RFC-3. Add `--validate` to validate the source metadata
against the NGFF schema while reading. For the write-to-new-store mode,
`--overwrite` (the default) replaces any pre-existing data at the output store,
while `--no-overwrite` refuses to; both flags are ignored for an in-place
upgrade, which never overwrites array data.
Expand Down
6 changes: 3 additions & 3 deletions docs/spec_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ supported by `ngff-zarr`.
and transformations.
- **OME-Zarr 0.9.dev1**: Reads and writes the development version that adopts
RFC-3, which extends support for the number, names, types and order
of axes. It is opt-in: pass `version="0.9.dev1"` explicitly. The default
target is unchanged, and OME publishes no JSON Schema for it yet, so schema
validation is unavailable at that version.
of axes. It is opt-in: pass `version="0.9.dev1"` explicitly, the default
target is unchanged. The schemas of the `0.9.dev1` release are bundled, so
`validate=True` checks a store at that version as it does at any other.

## High Content Screening (HCS)

Expand Down
3 changes: 3 additions & 0 deletions docs/validation/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ finest-to-coarsest dataset ordering, OMERO channel color format, RFC 4
anatomical orientation, and HCS plate/well consistency. Most rules enforce v0.4
MUSTs; two additional rules (`zarr-format`, `ome-namespace`) enforce the v0.5
`ome`-namespace and Zarr v3 store conventions and fire only for v0.5 metadata.
The three RFC 4 orientation rules are normative from OME-Zarr 0.9.dev1: they
are inert when the caller declares an earlier version and stay on, as a
strictness choice, when no version is declared (see [[rule-reference]]).

The rules operate on the already-parsed metadata object (the `Metadata`,
`Plate`, and `Well` dataclasses in Python; their equivalents in TypeScript), so
Expand Down
19 changes: 18 additions & 1 deletion docs/validation/parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ for usage, see [[api]].

## The contract

Both ports must agree on five observable dimensions:
Both ports must agree on six observable dimensions:

1. **Rule identifiers** — the same `SpecRule` string values, in the same
canonical declaration/iteration order.
Expand All @@ -42,6 +42,10 @@ Both ports must agree on five observable dimensions:
inert for the versions that adopt the RFC-3 free-form axis model. Both ports
must treat exactly the same version strings as RFC-3, or the same metadata
validates in one language and not the other.
6. **The RFC-4 orientation version set** — the three orientation rules gate the
opposite way: they are normative at exactly the versions that adopt RFC-4
(and when no version is given), and inert below. Both ports must treat the
same version strings as RFC-4 for the same reason.

Because both test suites assert these facts against the **same literal
identifier list**, adding, removing, renaming, or reordering a rule — or
Expand Down Expand Up @@ -85,6 +89,15 @@ at every other. Rule 4, `axis-names-unique`, is never inert: RFC-3 *adds* it,
and ngff-zarr applies it at all versions as a strictness choice (see
[[rule-reference]]).

The versions at which the RFC-4 orientation rules are normative are pinned the
same way, as a `CANONICAL_RFC4_VERSIONS` literal:

1. `0.9.dev1`

Rules 9–11 gate the opposite way from the RFC-3 set: they are enforced at
those versions and when no version is given, and inert at every earlier
version, where RFC-4 has no normative status (see [[rule-reference]]).

## The parity tests

| Language | Test file |
Expand Down Expand Up @@ -116,6 +129,10 @@ Each suite independently locks:
`CANONICAL_RFC3_VERSIONS` and enforced at every other supported version and
when no version is given, asserted through the public orchestrator rather
than through the internal predicate.
- **RFC-4 orientation version set** — the orientation rules are enforced at
exactly the versions in `CANONICAL_RFC4_VERSIONS` (and when no version is
given) and inert at every other supported version, asserted the same way,
once per orientation rule.

Four of the fifteen rules never appear in `EXPECTED_EVALUATION_ORDER`, each for
its own reason. The two v0.5 namespacing rules (`zarr-format`, `ome-namespace`)
Expand Down
30 changes: 18 additions & 12 deletions docs/validation/rule-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ stable, lower-kebab-case identifier (the `SpecRule` value) that is identical
across the Python and TypeScript ports — see [[parity]] for the guarantee. Most
rules enforce OME-Zarr v0.4 MUSTs; the two v0.5 namespacing rules
(`zarr-format`, `ome-namespace`) fire only for v0.5 metadata and are inert (a
no-op) for v0.4. For the conceptual background and the two validation levels,
see [[overview]]; for invocation, see [[api]].
no-op) for v0.4. The three RFC 4 orientation rules (9–11) are normative from
OME-Zarr 0.9.dev1, which incorporates RFC-4 through `ome/ngff-spec#190`: they are
inert when the caller declares 0.4, 0.5 or 0.6, where RFC 4 has no normative
status, and stay on when no version is declared — a strictness choice, like
`axis-names-unique` below 0.9.dev1. For the conceptual background and the two
validation levels, see [[overview]]; for invocation, see [[api]].

Location strings are dotted-segment, JSON-Pointer-style identifiers of the
offending metadata node, emitted byte-for-byte identically in both languages.
Expand All @@ -44,9 +48,9 @@ the `SpecRule` enum declares them and the orchestrators evaluate them.
| 6 | `global-coord-transform-after-per-level` | images/multiscales | Exactly one `scale` per dataset, and a `translation` must follow — not precede — its `scale`. | v0.4: each dataset defines exactly one scale; a translation follows its scale. | `multiscales[0].datasets[1].coordinateTransformations` |
| 7 | `dataset-order-highest-to-lowest` | images/multiscales | Datasets ordered finest → coarsest; the spatial scale must not decrease as the level index rises. | v0.4: multiscale datasets ordered from highest to lowest resolution. | `multiscales[0].datasets[2]` |
| 8 | `omero-channel-color-format` | OMERO | Each OMERO channel `color` is exactly six hexadecimal digits (RGB). | v0.4: OMERO channel color is 6 hex digits. | `multiscales[0].omero.channels[0].color` |
| 9 | `axis-orientation-anatomical-type` | RFC 4 orientation | Every declared spatial-axis `orientation` has `type` `anatomical`. | RFC 4: an orientation's `type` is `anatomical`. | `multiscales[0].axes` |
| 10| `axis-orientation-on-non-space` | RFC 4 orientation | An `orientation` is declared only on `space` axes, never on a non-spatial axis. | RFC 4: orientation applies to spatial axes only. | `multiscales[0].axes[0]` |
| 11| `axis-orientation-unique-axis` | RFC 4 orientation | No two spatial axes declare orientations describing the same anatomical axis. | RFC 4: each spatial axis describes a distinct anatomical axis. | `multiscales[0].axes` |
| 9 | `axis-orientation-anatomical-type` | RFC 4 orientation | Every declared spatial-axis `orientation` has `type` `anatomical`. Inert below 0.9.dev1 when the caller declares a version; kept on when none is given. | RFC 4 (normative from 0.9.dev1): an orientation's `type` is `anatomical`. No released spec below 0.9.dev1 adopts RFC 4, so enforcement without a declared version is a strictness choice, not a spec MUST of those versions. | `multiscales[0].axes` |
| 10| `axis-orientation-on-non-space` | RFC 4 orientation | An `orientation` is declared only on `space` axes, never on a non-spatial axis. Inert below 0.9.dev1 when the caller declares a version; kept on when none is given. | RFC 4 (normative from 0.9.dev1): orientation applies to spatial axes only. Same below-0.9.dev1 status as rule 9. | `multiscales[0].axes[0]` |
| 11| `axis-orientation-unique-axis` | RFC 4 orientation | No two spatial axes declare orientations describing the same anatomical axis. Inert below 0.9.dev1 when the caller declares a version; kept on when none is given. | RFC 4 (normative from 0.9.dev1): each spatial axis describes a distinct anatomical axis. Same below-0.9.dev1 status as rule 9. | `multiscales[0].axes` |
| 12| `zarr-format` | images/multiscales (v0.5) | A v0.5 entry implies a Zarr v3 store; a `zarr_format` value that leaked into the entry must be exactly `3`. Inert for v0.4. | v0.5: metadata is backed by a Zarr v3 store (`zarr_format == 3`). | `multiscales[0]` |
| 13| `ome-namespace` | images/multiscales (v0.5) | A v0.5 entry must not retain a group-level `ome` or `multiscales` wrapper key — the `ome` namespace wraps the group attributes, not each entry. Inert for v0.4. | v0.5: multiscales live under the top-level `ome` namespace, with `version` hoisted to `ome.version`. | `multiscales[0]` |
| 14| `plate-row-index-consistency` | HCS plate | Each well's `path` is `<row>/<column>`, naming declared row/column entries, with `rowIndex`/`columnIndex` equal to those entries' positions. | v0.4: well `rowIndex`/`columnIndex` match the named row/column positions in `plate.rows`/`plate.columns`. | `plate.wells[3]` |
Expand All @@ -65,11 +69,12 @@ rules:
class-ordering then spatial-name ordering for `axis-order`; per-dataset
scale-count then transform-ordering for
`global-coord-transform-after-per-level`. The v0.5 namespacing rules (12 and
13) run last and are inert for v0.4 input. The orientation rules
`axis-orientation-on-non-space` (10) and `axis-orientation-unique-axis` (11)
fire only for specific axis shapes, so the linear fail-fast cascade for a v0.4
metadata is an 11-step sequence ending at
`axis-orientation-anatomical-type`.
13) run last and are inert for v0.4 input, and the orientation rules (9–11)
are inert when the caller declares a version below 0.9.dev1. The orientation
rules `axis-orientation-on-non-space` (10) and `axis-orientation-unique-axis`
(11) fire only for specific axis shapes, so the linear fail-fast cascade for
a v0.4-shaped metadata validated with no declared version is an 11-step
sequence ending at `axis-orientation-anatomical-type`.
- **`validate_plate` / `validatePlate`** evaluates rule **14**
(`plate-row-index-consistency`).
- **`validate_well` / `validateWell`** evaluates rule **15**
Expand All @@ -96,8 +101,9 @@ TypeScript by checking the target version in `axisViews`.
`global-coord-transform-after-per-level`,
`dataset-order-highest-to-lowest`.
- **OMERO** — `omero-channel-color-format`.
- **RFC 4 orientation** — `axis-orientation-anatomical-type`,
`axis-orientation-on-non-space`, `axis-orientation-unique-axis`.
- **RFC 4 orientation** (normative from 0.9.dev1) —
`axis-orientation-anatomical-type`, `axis-orientation-on-non-space`,
`axis-orientation-unique-axis`.
- **v0.5 namespacing** — `zarr-format`, `ome-namespace` (inert for v0.4).
- **HCS plate / well** — `plate-row-index-consistency`,
`well-acquisition-missing`.
10 changes: 10 additions & 0 deletions py/ngff_zarr/spec/0.9/schemas/_version.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ngff.openmicroscopy.org/0.9.dev1/schemas/_version.schema",
"title": "OME-Zarr version",
"description": "OME-Zarr version.",
"type": "string",
"enum": [
"0.9.dev1"
]
}
46 changes: 46 additions & 0 deletions py/ngff_zarr/spec/0.9/schemas/axes.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ngff.openmicroscopy.org/0.9.dev1/schemas/axes.schema",
"title": "Axes",
"description": "OME-Zarr Axes.",
"type": "array",
"uniqueItems": true,
"minItems": 1,
"items": {
"$ref": "#/$defs/axis"
},
"$defs": {
"axis": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "Name of the axis. Must be unique within the coordinate system."
},
"longName": {
"type": "string",
"description": "Longer name or description of the axis."
},
"type": {
"type": "string",
"description": "Type of the axis. Can be a predefined type or custom type."
},
"discrete": {
"type": "boolean",
"description": "Whether the dimension is discrete"
},
"unit": {
"type": "string",
"description": "Unit for the axis"
},
"orientation": {
"$ref": "axis_orientation.schema"
}
},
"required": [
"name"
]
}
}
}
41 changes: 41 additions & 0 deletions py/ngff_zarr/spec/0.9/schemas/axis_orientation.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ngff.openmicroscopy.org/0.9.dev1/schemas/axis_orientation.schema",
"title": "Axis Orientation",
"description": "Controlled vocabulary for orientation of each spatial axis according to subject or subject-global reference.",
"type": "object",
"properties": {
"type": {
"enum": ["anatomical"]
},
"value": {
"enum": [
"left-to-right",
"right-to-left",
"anterior-to-posterior",
"posterior-to-anterior",
"inferior-to-superior",
"superior-to-inferior",
"dorsal-to-ventral",
"ventral-to-dorsal",
"dorsal-to-palmar",
"palmar-to-dorsal",
"dorsal-to-plantar",
"plantar-to-dorsal",
"rostral-to-caudal",
"caudal-to-rostral",
"cranial-to-caudal",
"caudal-to-cranial",
"proximal-to-distal",
"distal-to-proximal",
"superficial-to-deep",
"deep-to-superficial",
"apical-to-basal",
"basal-to-apical",
"apex-to-base",
"base-to-apex"
]
}
},
"required": ["type", "value"]
}
32 changes: 32 additions & 0 deletions py/ngff_zarr/spec/0.9/schemas/bf2raw.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ngff.openmicroscopy.org/0.9.dev1/schemas/bf2raw.schema",
"title": "bioformats2raw",
"description": "OME-Zarr bioformats2raw metadata.",
"type": "object",
"properties": {
"ome": {
"description": "The versioned OME-Zarr Metadata namespace",
"type": "object",
"properties": {
"bioformats2raw.layout": {
"description": "The top-level identifier metadata added by bioformats2raw",
"type": "number",
"enum": [
3
]
},
"version": {
"$ref": "https://ngff.openmicroscopy.org/0.9.dev1/schemas/_version.schema"
}
},
"required": [
"bioformats2raw.layout",
"version"
]
}
},
"required": [
"ome"
]
}
31 changes: 31 additions & 0 deletions py/ngff_zarr/spec/0.9/schemas/coordinate_systems.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ngff.openmicroscopy.org/0.9.dev1/schemas/coordinate_systems.schema",
"title": "Coordinate systems",
"description": "OME-Zarr coordinate system.",
"type": "array",
"uniqueItems": true,
"items": {
"$ref": "#/$defs/coordinateSystem"
},
"$defs": {
"coordinateSystem": {
"description": "Coordinate Systems for OME-NGFF",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "Name of coordinate system. Must be unique among all coordinate systems."
},
"axes": {
"$ref": "axes.schema"
}
},
"required": [
"name",
"axes"
]
}
}
}
Loading