Skip to content

Commit

Permalink
StackOrientation features
Browse files Browse the repository at this point in the history
- Move more functionality onto enum to make it more interoperable with
string
- CoordinateTransformer, ProjectStack use StackOrientation internally
  • Loading branch information
clbarnes committed Nov 1, 2018
1 parent 63d3039 commit 0ce8fe6
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 21 deletions.
41 changes: 28 additions & 13 deletions catpy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,40 @@ def __str__(self):


class StackOrientation(IntEnum):
"""Can be iterated over or indexed like the lower-case string representation of the orientation"""
XY = 0
XZ = 1
ZY = 2

def __str__(self):
return self.name.lower()

@classmethod
def from_str(cls, s):
return {o.name: o for o in StackOrientation}[s.upper()]

@classmethod
def from_value(cls, value, default='xy'):
"""Convert an int, str or StackOrientation into a StackOrientation.
A NoneType ``value`` will use the default orientation."""
if value is None:
value = default

if isinstance(value, string_types):
return cls.from_str(value)
elif isinstance(value, int):
return cls(value)
else:
raise TypeError("Cannot create a StackOrientation from {}".format(type(value).__name__))

orientation_strs = {o: str(o) for o in StackOrientation}
def __iter__(self):
return iter(str(self))

def __getitem__(self, item):
return str(self)[item]

def __contains__(self, item):
return item in str(self)


def make_url(base_url, *args):
Expand Down Expand Up @@ -401,7 +426,7 @@ def __init__(self, resolution=None, translation=None, orientation=StackOrientati
StackOrientation
int corresponding to StackOrientation
'xy', 'xz', or 'zy'
None (reverts to default)
None (reverts to default 'xy')
Default StackOrientation.XY
scale_z : bool
Whether or not to scale z coordinates when using stack_to_scaled* methods. Default False is recommended, but
Expand All @@ -416,7 +441,7 @@ def __init__(self, resolution=None, translation=None, orientation=StackOrientati
self.translation = {dim: translation.get(dim, 0) for dim in 'zyx'}
self.scale_z = scale_z

self.orientation = self._validate_orientation(orientation)
self.orientation = StackOrientation.from_value(orientation)
self.depth_dim = [dim for dim in 'zyx' if dim not in self.orientation][0]

# mapping of project dimension to stack dimension, based on orientation
Expand All @@ -428,16 +453,6 @@ def __init__(self, resolution=None, translation=None, orientation=StackOrientati
# mapping of stack dimension to project dimension, based on orientation
self._p2s = {value: key for key, value in self._s2p.items()}

def _validate_orientation(self, orientation):
if orientation is None:
orientation = StackOrientation.XY
orientation = orientation_strs.get(orientation, orientation)
lower = orientation.lower()
if lower not in orientation_strs.values():
raise ValueError("orientation must be a StackOrientation, 'xy', 'xz', or 'zy'")

return lower

@classmethod
def from_catmaid(cls, catmaid_client, stack_id):
"""
Expand Down
8 changes: 4 additions & 4 deletions catpy/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from requests_futures.sessions import FuturesSession

from catpy import CoordinateTransformer

from catpy.client import StackOrientation

logger = logging.getLogger()

Expand Down Expand Up @@ -438,7 +438,7 @@ def __init__(self, dimension, translation, resolution, orientation, broken_slice
super(ProjectStack, self).__init__(dimension, broken_slices, canary_location)
self.translation = translation
self.resolution = resolution
self.orientation = orientation
self.orientation = StackOrientation.from_value(orientation)

@classmethod
def from_stack_info(cls, stack_info):
Expand All @@ -455,7 +455,7 @@ def from_stack_info(cls, stack_info):
"""
stack = cls(
stack_info['dimension'], stack_info['translation'], stack_info['resolution'],
cls.orientation_choices[stack_info['orientation']], stack_info['broken_slices'],
stack_info['orientation'], stack_info['broken_slices'],
stack_info['canary_location']
)
mirrors = [StackMirror.from_dict(d) for d in stack_info['mirrors']]
Expand Down Expand Up @@ -882,7 +882,7 @@ def roi_to_scaled(self, roi, roi_mode, zoom_level):
if roi_mode == ROIMode.PROJECT:
if not isinstance(self.stack, ProjectStack):
raise ValueError("ImageFetcher's stack is not related to a project, cannot use ROIMode.PROJECT")
if self.stack.orientation.lower() != 'xy':
if self.stack.orientation != StackOrientation.XY:
warn("Stack orientation differs from project: returned array's orientation will reflect"
"stack orientation, not project orientation")
roi_tgt = self.coord_trans.project_to_stack_array(roi_tgt, dims=self.target_orientation)
Expand Down
8 changes: 4 additions & 4 deletions tests/test_utils/test_coordinate_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,14 @@ def test_stack_to_scaled_array(coordinate_generator, default_coord_transformer,
])
def test_can_validate_orientation_valid(orientation):
trans = CoordinateTransformer(orientation=orientation)
assert trans.orientation == 'xy'
assert trans.orientation == StackOrientation.XY
assert trans.depth_dim == 'z'


@pytest.mark.parametrize('orientation,expected_exception', [
[3, AttributeError],
['xyz', ValueError],
['xc', ValueError]
[3, ValueError],
['xyz', KeyError],
['xc', KeyError]
])
def test_can_validate_orientation_invalid(orientation, expected_exception):
with pytest.raises(expected_exception):
Expand Down

0 comments on commit 0ce8fe6

Please sign in to comment.