Skip to content

Commit 5e2b1a4

Browse files
Specify NGFF 0.6-dev in output data (#849)
* specify NGFF 0.6-dev in output data * add RasterFormatV02 * fix ome-zarr patch after adding RasterFormatV02
1 parent d71aff4 commit 5e2b1a4

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

src/spatialdata/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,6 @@
7777
from spatialdata._core.query.spatial_query import bounding_box_query, polygon_query
7878
from spatialdata._core.spatialdata import SpatialData
7979
from spatialdata._io._utils import get_dask_backing_files, save_transformations
80+
from spatialdata._io.format import SpatialDataFormat
8081
from spatialdata._io.io_zarr import read_zarr
8182
from spatialdata._utils import get_pyramid_levels, unpad_raster

src/spatialdata/_io/format.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import annotations
22

3+
from collections.abc import Iterator
34
from typing import Any
45

6+
import ome_zarr.format
57
import zarr
68
from anndata import AnnData
7-
from ome_zarr.format import CurrentFormat
9+
from ome_zarr.format import CurrentFormat, Format, FormatV01, FormatV02, FormatV03, FormatV04
810
from pandas.api.types import CategoricalDtype
911
from shapely import GeometryType
1012

@@ -120,6 +122,22 @@ def validate_coordinate_transformations(
120122
def spatialdata_format_version(self) -> str:
121123
return "0.1"
122124

125+
@property
126+
def version(self) -> str:
127+
return "0.4"
128+
129+
130+
class RasterFormatV02(RasterFormatV01):
131+
@property
132+
def spatialdata_format_version(self) -> str:
133+
return "0.2"
134+
135+
@property
136+
def version(self) -> str:
137+
# 0.1 -> 0.2 changed the version string for the NGFF format, from 0.4 to 0.6-dev-spatialdata as discussed here
138+
# https://github.com/scverse/spatialdata/pull/849
139+
return "0.4-dev-spatialdata"
140+
123141

124142
class ShapesFormatV01(SpatialDataFormat):
125143
"""Formatter for shapes."""
@@ -211,7 +229,7 @@ def validate_table(
211229
raise ValueError("`table.obs[instance_key]` must not contain null values, but it does.")
212230

213231

214-
CurrentRasterFormat = RasterFormatV01
232+
CurrentRasterFormat = RasterFormatV02
215233
CurrentShapesFormat = ShapesFormatV02
216234
CurrentPointsFormat = PointsFormatV01
217235
CurrentTablesFormat = TablesFormatV01
@@ -229,12 +247,29 @@ def validate_table(
229247
}
230248
RasterFormats = {
231249
"0.1": RasterFormatV01(),
250+
"0.2": RasterFormatV02(),
232251
}
233252
SpatialDataContainerFormats = {
234253
"0.1": SpatialDataContainerFormatV01(),
235254
}
236255

237256

257+
def format_implementations() -> Iterator[Format]:
258+
"""Return an instance of each format implementation, newest to oldest."""
259+
yield RasterFormatV02()
260+
# yield RasterFormatV01() # same format string as FormatV04
261+
yield FormatV04()
262+
yield FormatV03()
263+
yield FormatV02()
264+
yield FormatV01()
265+
266+
267+
# monkeypatch the ome_zarr.format module to include the SpatialDataFormat (we want to use the APIs from ome_zarr to
268+
# read, but signal that the format we are using is a dev version of NGFF, since it builds on some open PR that are
269+
# not released yet)
270+
ome_zarr.format.format_implementations = format_implementations
271+
272+
238273
def _parse_formats(formats: SpatialDataFormat | list[SpatialDataFormat] | None) -> dict[str, SpatialDataFormat]:
239274
parsed = {
240275
"raster": CurrentRasterFormat(),

tests/io/test_format.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1+
import json
2+
import tempfile
3+
from pathlib import Path
14
from typing import Any
25

36
import pytest
47
from shapely import GeometryType
58

6-
from spatialdata._io.format import CurrentPointsFormat, CurrentShapesFormat, ShapesFormatV01
9+
from spatialdata._io.format import (
10+
CurrentPointsFormat,
11+
CurrentShapesFormat,
12+
RasterFormatV01,
13+
RasterFormatV02,
14+
ShapesFormatV01,
15+
SpatialDataFormat,
16+
)
717
from spatialdata.models import PointsModel, ShapesModel
818

919
Points_f = CurrentPointsFormat()
@@ -71,3 +81,17 @@ def test_format_shapes_v2(
7181
metadata: dict[str, Any] = {attrs_key: {"version": Shapes_f.spatialdata_format_version}}
7282
metadata[attrs_key].pop("version")
7383
assert metadata[attrs_key] == Shapes_f.attrs_to_dict({})
84+
85+
@pytest.mark.parametrize("format", [RasterFormatV01, RasterFormatV02])
86+
def test_format_raster_v1_v2(self, images, format: type[SpatialDataFormat]) -> None:
87+
with tempfile.TemporaryDirectory() as tmpdir:
88+
images.write(Path(tmpdir) / "images.zarr", format=format())
89+
zattrs_file = Path(tmpdir) / "images.zarr/images/image2d/.zattrs"
90+
with open(zattrs_file) as infile:
91+
zattrs = json.load(infile)
92+
ngff_version = zattrs["multiscales"][0]["version"]
93+
if format == RasterFormatV01:
94+
assert ngff_version == "0.4"
95+
else:
96+
assert format == RasterFormatV02
97+
assert ngff_version == "0.4-dev-spatialdata"

0 commit comments

Comments
 (0)