Skip to content

Commit

Permalink
Specify NGFF 0.6-dev in output data (#849)
Browse files Browse the repository at this point in the history
* specify NGFF 0.6-dev in output data

* add RasterFormatV02

* fix ome-zarr patch after adding RasterFormatV02
  • Loading branch information
LucaMarconato authored Feb 23, 2025
1 parent d71aff4 commit 5e2b1a4
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/spatialdata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,6 @@
from spatialdata._core.query.spatial_query import bounding_box_query, polygon_query
from spatialdata._core.spatialdata import SpatialData
from spatialdata._io._utils import get_dask_backing_files, save_transformations
from spatialdata._io.format import SpatialDataFormat
from spatialdata._io.io_zarr import read_zarr
from spatialdata._utils import get_pyramid_levels, unpad_raster
39 changes: 37 additions & 2 deletions src/spatialdata/_io/format.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

from collections.abc import Iterator
from typing import Any

import ome_zarr.format
import zarr
from anndata import AnnData
from ome_zarr.format import CurrentFormat
from ome_zarr.format import CurrentFormat, Format, FormatV01, FormatV02, FormatV03, FormatV04
from pandas.api.types import CategoricalDtype
from shapely import GeometryType

Expand Down Expand Up @@ -120,6 +122,22 @@ def validate_coordinate_transformations(
def spatialdata_format_version(self) -> str:
return "0.1"

@property
def version(self) -> str:
return "0.4"


class RasterFormatV02(RasterFormatV01):
@property
def spatialdata_format_version(self) -> str:
return "0.2"

@property
def version(self) -> str:
# 0.1 -> 0.2 changed the version string for the NGFF format, from 0.4 to 0.6-dev-spatialdata as discussed here
# https://github.com/scverse/spatialdata/pull/849
return "0.4-dev-spatialdata"


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


CurrentRasterFormat = RasterFormatV01
CurrentRasterFormat = RasterFormatV02
CurrentShapesFormat = ShapesFormatV02
CurrentPointsFormat = PointsFormatV01
CurrentTablesFormat = TablesFormatV01
Expand All @@ -229,12 +247,29 @@ def validate_table(
}
RasterFormats = {
"0.1": RasterFormatV01(),
"0.2": RasterFormatV02(),
}
SpatialDataContainerFormats = {
"0.1": SpatialDataContainerFormatV01(),
}


def format_implementations() -> Iterator[Format]:
"""Return an instance of each format implementation, newest to oldest."""
yield RasterFormatV02()
# yield RasterFormatV01() # same format string as FormatV04
yield FormatV04()
yield FormatV03()
yield FormatV02()
yield FormatV01()


# monkeypatch the ome_zarr.format module to include the SpatialDataFormat (we want to use the APIs from ome_zarr to
# read, but signal that the format we are using is a dev version of NGFF, since it builds on some open PR that are
# not released yet)
ome_zarr.format.format_implementations = format_implementations


def _parse_formats(formats: SpatialDataFormat | list[SpatialDataFormat] | None) -> dict[str, SpatialDataFormat]:
parsed = {
"raster": CurrentRasterFormat(),
Expand Down
26 changes: 25 additions & 1 deletion tests/io/test_format.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import json
import tempfile
from pathlib import Path
from typing import Any

import pytest
from shapely import GeometryType

from spatialdata._io.format import CurrentPointsFormat, CurrentShapesFormat, ShapesFormatV01
from spatialdata._io.format import (
CurrentPointsFormat,
CurrentShapesFormat,
RasterFormatV01,
RasterFormatV02,
ShapesFormatV01,
SpatialDataFormat,
)
from spatialdata.models import PointsModel, ShapesModel

Points_f = CurrentPointsFormat()
Expand Down Expand Up @@ -71,3 +81,17 @@ def test_format_shapes_v2(
metadata: dict[str, Any] = {attrs_key: {"version": Shapes_f.spatialdata_format_version}}
metadata[attrs_key].pop("version")
assert metadata[attrs_key] == Shapes_f.attrs_to_dict({})

@pytest.mark.parametrize("format", [RasterFormatV01, RasterFormatV02])
def test_format_raster_v1_v2(self, images, format: type[SpatialDataFormat]) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
images.write(Path(tmpdir) / "images.zarr", format=format())
zattrs_file = Path(tmpdir) / "images.zarr/images/image2d/.zattrs"
with open(zattrs_file) as infile:
zattrs = json.load(infile)
ngff_version = zattrs["multiscales"][0]["version"]
if format == RasterFormatV01:
assert ngff_version == "0.4"
else:
assert format == RasterFormatV02
assert ngff_version == "0.4-dev-spatialdata"

0 comments on commit 5e2b1a4

Please # to comment.