Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Scene class #220

Merged
merged 13 commits into from
Sep 26, 2024
2 changes: 2 additions & 0 deletions python-spec/src/somacore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from .query import AxisColumnNames
from .query import AxisQuery
from .query import ExperimentAxisQuery
from .scene import Scene
from .spatial import GeometryDataFrame
from .spatial import ImageProperties
from .spatial import MultiscaleImage
Expand Down Expand Up @@ -67,6 +68,7 @@
"SpatialRead",
"Experiment",
"Measurement",
"Scene",
"ImageProperties",
"MultiscaleImage",
"SpatialDataFrame",
Expand Down
2 changes: 2 additions & 0 deletions python-spec/src/somacore/ephemeral/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
from .collections import Collection
from .collections import Experiment
from .collections import Measurement
from .collections import Scene

__all__ = (
"Collection",
"Experiment",
"Measurement",
"Scene",
)
160 changes: 158 additions & 2 deletions python-spec/src/somacore/ephemeral/collections.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
from typing import Any, Dict, Iterator, NoReturn, Optional, TypeVar

from typing import (
Any,
Dict,
Iterator,
NoReturn,
Optional,
Sequence,
Tuple,
TypeVar,
Union,
)

import pyarrow as pa
from typing_extensions import Literal, Self

from .. import base
from .. import collection
from .. import coordinates
from .. import data
from .. import experiment
from .. import measurement
from .. import options
from .. import scene
from .. import spatial

_Elem = TypeVar("_Elem", bound=base.SOMAObject)

Expand Down Expand Up @@ -120,6 +134,14 @@ class Collection( # type: ignore[misc] # __eq__ false positive
]
"""The loosest possible constraint of the abstract Measurement type."""

_BasicAbstractScene = scene.Scene[
spatial.MultiscaleImage,
spatial.PointCloud,
spatial.GeometryDataFrame,
base.SOMAObject,
]
"""The loosest possible constraint of the abstract Scene type."""


class Measurement( # type: ignore[misc] # __eq__ false positive
BaseCollection[base.SOMAObject], _BasicAbstractMeasurement
Expand All @@ -129,11 +151,145 @@ class Measurement( # type: ignore[misc] # __eq__ false positive
__slots__ = ()


class Scene( # type: ignore[misc] # __eq__ false positive
BaseCollection[base.SOMAObject], _BasicAbstractScene
):
"""An in-memory Collection with Scene semantics."""

__slots__ = ()

@property
def coordinate_space(self) -> coordinates.CoordinateSpace:
"""Coordinate system for this scene."""
raise NotImplementedError()

@coordinate_space.setter
def coordinate_space(self, value: coordinates.CoordinateSpace) -> None:
raise NotImplementedError()

def add_geometry_dataframe(
self,
key: str,
subcollection: Union[str, Sequence[str]],
transform: Optional[coordinates.CoordinateTransform],
*,
uri: str,
schema: pa.Schema,
index_column_names: Sequence[str] = (
options.SOMA_JOINID,
options.SOMA_GEOMETRY,
),
axis_names: Sequence[str] = ("x", "y"),
domain: Optional[Sequence[Optional[Tuple[Any, Any]]]] = None,
platform_config: Optional[options.PlatformConfig] = None,
context: Optional[Any] = None,
) -> spatial.GeometryDataFrame:
raise NotImplementedError()

def add_multiscale_image(
self,
key: str,
subcollection: Union[str, Sequence[str]],
transform: Optional[coordinates.CoordinateTransform],
*,
uri: str,
type: pa.DataType,
image_type: str = "CYX", # TODO: Replace this arg after PR #219 is merged
reference_level_shape: Sequence[int],
axis_names: Sequence[str] = ("c", "x", "y"),
) -> spatial.MultiscaleImage:
raise NotImplementedError()

def add_new_point_cloud(
self,
key: str,
subcollection: Union[str, Sequence[str]],
transform: Optional[coordinates.CoordinateTransform],
*,
uri: Optional[str] = None,
schema: pa.Schema,
index_column_names: Sequence[str] = (options.SOMA_JOINID,),
axis_names: Sequence[str] = ("x", "y"),
domain: Optional[Sequence[Optional[Tuple[Any, Any]]]] = None,
platform_config: Optional[options.PlatformConfig] = None,
) -> spatial.PointCloud:
raise NotImplementedError()

def set_transform_to_geometry_dataframe(
self,
key: str,
transform: coordinates.CoordinateTransform,
*,
subcollection: Union[str, Sequence[str]] = "obsl",
coordinate_space: Optional[coordinates.CoordinateSpace] = None,
) -> spatial.GeometryDataFrame:
raise NotImplementedError()

def set_transform_to_multiscale_image(
self,
key: str,
transform: coordinates.CoordinateTransform,
*,
subcollection: Union[str, Sequence[str]] = "img",
coordinate_space: Optional[coordinates.CoordinateSpace] = None,
) -> spatial.MultiscaleImage:
raise NotImplementedError()

def set_transform_to_point_cloud(
self,
key: str,
transform: coordinates.CoordinateTransform,
*,
subcollection: Union[str, Sequence[str]] = "obsl",
coordinate_space: Optional[coordinates.CoordinateSpace] = None,
) -> spatial.PointCloud:
raise NotImplementedError()

def get_transform_from_geometry_dataframe(
self, key: str, *, subcollection: Union[str, Sequence[str]] = "obsl"
) -> coordinates.CoordinateTransform:
raise NotImplementedError()

def get_transform_from_multiscale_image(
self,
key: str,
*,
subcollection: str = "img",
level: Optional[Union[str, int]] = None,
) -> coordinates.CoordinateTransform:
raise NotImplementedError()

def get_transform_from_point_cloud(
self, key: str, *, subcollection: str = "obsl"
) -> coordinates.CoordinateTransform:
raise NotImplementedError()

def get_transform_to_geometry_dataframe(
self, key: str, *, subcollection: Union[str, Sequence[str]] = "obsl"
) -> coordinates.CoordinateTransform:
raise NotImplementedError()

def get_transform_to_multiscale_image(
self,
key: str,
*,
subcollection: str = "img",
level: Optional[Union[str, int]] = None,
) -> coordinates.CoordinateTransform:
raise NotImplementedError()

def get_transform_to_point_cloud(
self, key: str, *, subcollection: str = "obsl"
) -> coordinates.CoordinateTransform:
raise NotImplementedError()


class Experiment( # type: ignore[misc] # __eq__ false positive
BaseCollection[base.SOMAObject],
experiment.Experiment[
data.DataFrame,
collection.Collection[_BasicAbstractMeasurement],
collection.Collection[_BasicAbstractScene],
base.SOMAObject,
],
):
Expand Down
11 changes: 10 additions & 1 deletion python-spec/src/somacore/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@
from . import data
from . import measurement
from . import query
from . import scene

_DF = TypeVar("_DF", bound=data.DataFrame)
"""An implementation of a DataFrame."""
_MeasColl = TypeVar("_MeasColl", bound=collection.Collection[measurement.Measurement])
"""An implementation of a collection of Measurements."""
_SceneColl = TypeVar("_SceneColl", bound=collection.Collection[scene.Scene])
"""An implemenation of a collection of spatial data."""
_RootSO = TypeVar("_RootSO", bound=base.SOMAObject)
"""The root SOMA object type of the implementation."""


class Experiment(collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _RootSO]):
class Experiment(
collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _SceneColl, _RootSO]
):
"""A collection subtype representing an annotated 2D matrix of measurements.

In single cell biology, this can represent multiple modes of measurement
Expand All @@ -38,6 +43,7 @@ class Experiment(collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _Ro
# somacore.Experiment[
# ImplDataFrame, # _DF
# ImplMeasurement, # _MeasColl
# ImplScene, # _SceneColl
# ImplSOMAObject, # _RootSO
# ],
# ):
Expand All @@ -57,6 +63,9 @@ class Experiment(collection.BaseCollection[_RootSO], Generic[_DF, _MeasColl, _Ro
ms = _mixin.item[_MeasColl]()
"""A collection of named measurements."""

spatial = _mixin.item[_SceneColl]() # TODO: Discuss the name of this element.
"""A collection of named spatial scenes."""

def axis_query(
self,
measurement_name: str,
Expand Down
Loading
Loading