ngio 0.4.8__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ngio/__init__.py +5 -2
- ngio/common/__init__.py +11 -6
- ngio/common/_masking_roi.py +34 -54
- ngio/common/_pyramid.py +322 -75
- ngio/common/_roi.py +258 -330
- ngio/experimental/iterators/_feature.py +3 -3
- ngio/experimental/iterators/_rois_utils.py +10 -11
- ngio/hcs/_plate.py +192 -136
- ngio/images/_abstract_image.py +539 -35
- ngio/images/_create_synt_container.py +45 -47
- ngio/images/_create_utils.py +406 -0
- ngio/images/_image.py +524 -248
- ngio/images/_label.py +257 -180
- ngio/images/_masked_image.py +2 -2
- ngio/images/_ome_zarr_container.py +658 -255
- ngio/io_pipes/_io_pipes.py +9 -9
- ngio/io_pipes/_io_pipes_masked.py +7 -7
- ngio/io_pipes/_io_pipes_roi.py +6 -6
- ngio/io_pipes/_io_pipes_types.py +3 -3
- ngio/io_pipes/_match_shape.py +6 -8
- ngio/io_pipes/_ops_slices_utils.py +8 -5
- ngio/ome_zarr_meta/__init__.py +29 -18
- ngio/ome_zarr_meta/_meta_handlers.py +402 -689
- ngio/ome_zarr_meta/ngio_specs/__init__.py +4 -0
- ngio/ome_zarr_meta/ngio_specs/_axes.py +152 -51
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +129 -91
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +69 -69
- ngio/ome_zarr_meta/v04/__init__.py +5 -1
- ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +55 -86
- ngio/ome_zarr_meta/v05/__init__.py +27 -0
- ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
- ngio/ome_zarr_meta/v05/_v05_spec.py +495 -0
- ngio/resources/__init__.py +1 -1
- ngio/resources/resource_model.py +1 -1
- ngio/tables/_tables_container.py +82 -24
- ngio/tables/backends/_abstract_backend.py +7 -0
- ngio/tables/backends/_anndata.py +60 -7
- ngio/tables/backends/_anndata_utils.py +2 -4
- ngio/tables/backends/_csv.py +3 -19
- ngio/tables/backends/_json.py +10 -13
- ngio/tables/backends/_parquet.py +3 -31
- ngio/tables/backends/_py_arrow_backends.py +222 -0
- ngio/tables/backends/_utils.py +1 -1
- ngio/tables/v1/_roi_table.py +41 -24
- ngio/utils/__init__.py +8 -12
- ngio/utils/_cache.py +48 -0
- ngio/utils/_zarr_utils.py +354 -236
- {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/METADATA +12 -5
- ngio-0.5.0.dist-info/RECORD +88 -0
- ngio/images/_create.py +0 -276
- ngio/tables/backends/_non_zarr_backends.py +0 -196
- ngio/utils/_logger.py +0 -50
- ngio-0.4.8.dist-info/RECORD +0 -85
- {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/WHEEL +0 -0
- {ngio-0.4.8.dist-info → ngio-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,6 +15,7 @@ from ngio.ome_zarr_meta.ngio_specs._axes import (
|
|
|
15
15
|
DefaultTimeUnit,
|
|
16
16
|
SpaceUnits,
|
|
17
17
|
TimeUnits,
|
|
18
|
+
build_axes_handler,
|
|
18
19
|
build_canonical_axes_handler,
|
|
19
20
|
canonical_axes_order,
|
|
20
21
|
canonical_label_axes_order,
|
|
@@ -40,6 +41,7 @@ from ngio.ome_zarr_meta.ngio_specs._ngio_image import (
|
|
|
40
41
|
NgioImageLabelMeta,
|
|
41
42
|
NgioImageMeta,
|
|
42
43
|
NgioLabelMeta,
|
|
44
|
+
NgioLabelsGroupMeta,
|
|
43
45
|
)
|
|
44
46
|
from ngio.ome_zarr_meta.ngio_specs._pixel_size import PixelSize
|
|
45
47
|
|
|
@@ -62,11 +64,13 @@ __all__ = [
|
|
|
62
64
|
"NgioImageLabelMeta",
|
|
63
65
|
"NgioImageMeta",
|
|
64
66
|
"NgioLabelMeta",
|
|
67
|
+
"NgioLabelsGroupMeta",
|
|
65
68
|
"NgioPlateMeta",
|
|
66
69
|
"NgioWellMeta",
|
|
67
70
|
"PixelSize",
|
|
68
71
|
"SpaceUnits",
|
|
69
72
|
"TimeUnits",
|
|
73
|
+
"build_axes_handler",
|
|
70
74
|
"build_canonical_axes_handler",
|
|
71
75
|
"canonical_axes_order",
|
|
72
76
|
"canonical_label_axes_order",
|
|
@@ -4,7 +4,7 @@ from collections.abc import Sequence
|
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from typing import Literal, TypeAlias, TypeVar
|
|
6
6
|
|
|
7
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
8
8
|
|
|
9
9
|
from ngio.utils import NgioValidationError, NgioValueError
|
|
10
10
|
|
|
@@ -153,9 +153,51 @@ class AxesSetup(BaseModel):
|
|
|
153
153
|
c: str = "c"
|
|
154
154
|
t: str = "t"
|
|
155
155
|
others: list[str] = Field(default_factory=list)
|
|
156
|
+
allow_non_canonical_axes: bool = False
|
|
157
|
+
strict_canonical_order: bool = False
|
|
156
158
|
|
|
157
159
|
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
158
160
|
|
|
161
|
+
@model_validator(mode="after")
|
|
162
|
+
def _validate_axes_values(self) -> "AxesSetup":
|
|
163
|
+
"""Validate the axes values."""
|
|
164
|
+
canonical = {"x", "y", "z", "c", "t"}
|
|
165
|
+
axes = {"x": self.x, "y": self.y, "z": self.z, "c": self.c, "t": self.t}
|
|
166
|
+
|
|
167
|
+
for axis_name, axis_value in axes.items():
|
|
168
|
+
reserved = canonical - {axis_name}
|
|
169
|
+
if axis_value in reserved:
|
|
170
|
+
raise NgioValueError(
|
|
171
|
+
f"The {axis_name} axis cannot be called: '{axis_value}'. "
|
|
172
|
+
f"{axis_value} is reserved. If you want to set a non canonical "
|
|
173
|
+
"axis order, please set the 'strict_canonical_order'to False."
|
|
174
|
+
)
|
|
175
|
+
return self
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def from_ordered_list(
|
|
179
|
+
cls, axes_names: Sequence[str], canonical_order: Sequence[str]
|
|
180
|
+
):
|
|
181
|
+
"""Create an AxesSetup from an ordered list of axes names."""
|
|
182
|
+
# Make sure to only keep as many default axes as provided in axes_names
|
|
183
|
+
if len(axes_names) > len(canonical_order):
|
|
184
|
+
raise NgioValueError(
|
|
185
|
+
f"Cannot create AxesSetup from axes names {axes_names} "
|
|
186
|
+
f"and canonical order {canonical_order}. "
|
|
187
|
+
"The number of axes names cannot be greater than the "
|
|
188
|
+
"number of canonical axes."
|
|
189
|
+
)
|
|
190
|
+
canonical_order = list(canonical_order)
|
|
191
|
+
chanonical_axes = canonical_axes_order()
|
|
192
|
+
axes_mapping = {}
|
|
193
|
+
for ax in reversed(axes_names):
|
|
194
|
+
c_ax = canonical_order.pop()
|
|
195
|
+
if ax in chanonical_axes:
|
|
196
|
+
axes_mapping[ax] = ax
|
|
197
|
+
else:
|
|
198
|
+
axes_mapping[c_ax] = ax
|
|
199
|
+
return cls(**axes_mapping)
|
|
200
|
+
|
|
159
201
|
def canonical_map(self) -> dict[str, str]:
|
|
160
202
|
"""Get the canonical map of axes."""
|
|
161
203
|
return {
|
|
@@ -197,9 +239,9 @@ def _check_unique_names(axes: Sequence[Axis]):
|
|
|
197
239
|
)
|
|
198
240
|
|
|
199
241
|
|
|
200
|
-
def _check_non_canonical_axes(axes_setup: AxesSetup
|
|
242
|
+
def _check_non_canonical_axes(axes_setup: AxesSetup):
|
|
201
243
|
"""Check if all axes are known."""
|
|
202
|
-
if not allow_non_canonical_axes and len(axes_setup.others) > 0:
|
|
244
|
+
if not axes_setup.allow_non_canonical_axes and len(axes_setup.others) > 0:
|
|
203
245
|
raise NgioValidationError(
|
|
204
246
|
f"Unknown axes {axes_setup.others}. Please set "
|
|
205
247
|
"`allow_non_canonical_axes=True` to ignore them"
|
|
@@ -219,11 +261,9 @@ def _check_axes_validity(axes: Sequence[Axis], axes_setup: AxesSetup):
|
|
|
219
261
|
)
|
|
220
262
|
|
|
221
263
|
|
|
222
|
-
def _check_canonical_order(
|
|
223
|
-
axes: Sequence[Axis], axes_setup: AxesSetup, strict_canonical_order: bool
|
|
224
|
-
):
|
|
264
|
+
def _check_canonical_order(axes: Sequence[Axis], axes_setup: AxesSetup):
|
|
225
265
|
"""Check if the axes are in the canonical order."""
|
|
226
|
-
if not strict_canonical_order:
|
|
266
|
+
if not axes_setup.strict_canonical_order:
|
|
227
267
|
return
|
|
228
268
|
_names = [ax.name for ax in axes]
|
|
229
269
|
_canonical_order = []
|
|
@@ -242,24 +282,18 @@ def _check_canonical_order(
|
|
|
242
282
|
def validate_axes(
|
|
243
283
|
axes: Sequence[Axis],
|
|
244
284
|
axes_setup: AxesSetup,
|
|
245
|
-
allow_non_canonical_axes: bool = False,
|
|
246
|
-
strict_canonical_order: bool = False,
|
|
247
285
|
) -> None:
|
|
248
286
|
"""Validate the axes."""
|
|
249
|
-
if allow_non_canonical_axes and strict_canonical_order:
|
|
250
|
-
raise
|
|
287
|
+
if axes_setup.allow_non_canonical_axes and axes_setup.strict_canonical_order:
|
|
288
|
+
raise NgioValueError(
|
|
251
289
|
"`allow_non_canonical_axes` and"
|
|
252
290
|
"`strict_canonical_order` cannot be true at the same time."
|
|
253
291
|
"If non canonical axes are allowed, the order cannot be checked."
|
|
254
292
|
)
|
|
255
293
|
_check_unique_names(axes=axes)
|
|
256
|
-
_check_non_canonical_axes(
|
|
257
|
-
axes_setup=axes_setup, allow_non_canonical_axes=allow_non_canonical_axes
|
|
258
|
-
)
|
|
294
|
+
_check_non_canonical_axes(axes_setup=axes_setup)
|
|
259
295
|
_check_axes_validity(axes=axes, axes_setup=axes_setup)
|
|
260
|
-
_check_canonical_order(
|
|
261
|
-
axes=axes, axes_setup=axes_setup, strict_canonical_order=strict_canonical_order
|
|
262
|
-
)
|
|
296
|
+
_check_canonical_order(axes=axes, axes_setup=axes_setup)
|
|
263
297
|
|
|
264
298
|
|
|
265
299
|
class AxesHandler:
|
|
@@ -278,29 +312,22 @@ class AxesHandler:
|
|
|
278
312
|
axes: Sequence[Axis],
|
|
279
313
|
# user defined args
|
|
280
314
|
axes_setup: AxesSetup | None = None,
|
|
281
|
-
allow_non_canonical_axes: bool = False,
|
|
282
|
-
strict_canonical_order: bool = False,
|
|
283
315
|
):
|
|
284
316
|
"""Create a new AxesMapper object.
|
|
285
317
|
|
|
286
318
|
Args:
|
|
287
319
|
axes (list[Axis]): The axes on disk.
|
|
288
320
|
axes_setup (AxesSetup, optional): The axis setup. Defaults to None.
|
|
289
|
-
allow_non_canonical_axes (bool, optional): Allow non canonical axes.
|
|
290
|
-
strict_canonical_order (bool, optional): Check if the axes are in the
|
|
291
|
-
canonical order. Defaults to False.
|
|
292
321
|
"""
|
|
293
322
|
axes_setup = axes_setup if axes_setup is not None else AxesSetup()
|
|
294
323
|
|
|
295
324
|
validate_axes(
|
|
296
325
|
axes=axes,
|
|
297
326
|
axes_setup=axes_setup,
|
|
298
|
-
allow_non_canonical_axes=allow_non_canonical_axes,
|
|
299
|
-
strict_canonical_order=strict_canonical_order,
|
|
300
327
|
)
|
|
301
328
|
|
|
302
|
-
self._allow_non_canonical_axes = allow_non_canonical_axes
|
|
303
|
-
self._strict_canonical_order = strict_canonical_order
|
|
329
|
+
self._allow_non_canonical_axes = axes_setup.allow_non_canonical_axes
|
|
330
|
+
self._strict_canonical_order = axes_setup.strict_canonical_order
|
|
304
331
|
|
|
305
332
|
self._canonical_order = canonical_axes_order()
|
|
306
333
|
|
|
@@ -352,6 +379,7 @@ class AxesHandler:
|
|
|
352
379
|
|
|
353
380
|
@property
|
|
354
381
|
def axes_names(self) -> tuple[str, ...]:
|
|
382
|
+
"""On disk axes names."""
|
|
355
383
|
return tuple(ax.name for ax in self._axes)
|
|
356
384
|
|
|
357
385
|
@property
|
|
@@ -421,8 +449,48 @@ class AxesHandler:
|
|
|
421
449
|
return AxesHandler(
|
|
422
450
|
axes=new_axes,
|
|
423
451
|
axes_setup=self.axes_setup,
|
|
424
|
-
|
|
425
|
-
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
def rename_axes(self, axes_names: Sequence[str]) -> "AxesHandler":
|
|
455
|
+
"""Rename the axes.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
axes_names (Sequence[str]): The new axes names.
|
|
459
|
+
"""
|
|
460
|
+
if len(axes_names) != len(self.axes):
|
|
461
|
+
raise NgioValueError(
|
|
462
|
+
f"Cannot rename axes. "
|
|
463
|
+
f"Expected {len(self.axes)} axes, but got {len(axes_names)}."
|
|
464
|
+
)
|
|
465
|
+
new_axes = []
|
|
466
|
+
axes_setup = self.axes_setup
|
|
467
|
+
for ax, new_name in zip(self.axes, axes_names, strict=True):
|
|
468
|
+
if ax.name == new_name:
|
|
469
|
+
new_axes.append(ax)
|
|
470
|
+
continue
|
|
471
|
+
new_ax = Axis(name=new_name, axis_type=ax.axis_type, unit=ax.unit)
|
|
472
|
+
match ax.name:
|
|
473
|
+
case axes_setup.x:
|
|
474
|
+
axes_setup = axes_setup.model_copy(update={"x": new_name})
|
|
475
|
+
case axes_setup.y:
|
|
476
|
+
axes_setup = axes_setup.model_copy(update={"y": new_name})
|
|
477
|
+
case axes_setup.z:
|
|
478
|
+
axes_setup = axes_setup.model_copy(update={"z": new_name})
|
|
479
|
+
case axes_setup.c:
|
|
480
|
+
axes_setup = axes_setup.model_copy(update={"c": new_name})
|
|
481
|
+
case axes_setup.t:
|
|
482
|
+
axes_setup = axes_setup.model_copy(update={"t": new_name})
|
|
483
|
+
case _:
|
|
484
|
+
if ax.name in axes_setup.others:
|
|
485
|
+
others = axes_setup.others.copy()
|
|
486
|
+
others.remove(ax.name)
|
|
487
|
+
others.append(new_name)
|
|
488
|
+
axes_setup = axes_setup.model_copy(update={"others": others})
|
|
489
|
+
new_axes.append(new_ax)
|
|
490
|
+
# Update the axes setup
|
|
491
|
+
return AxesHandler(
|
|
492
|
+
axes=new_axes,
|
|
493
|
+
axes_setup=axes_setup,
|
|
426
494
|
)
|
|
427
495
|
|
|
428
496
|
def get_index(self, name: str) -> int | None:
|
|
@@ -464,14 +532,33 @@ class AxesHandler:
|
|
|
464
532
|
self._axes = new_axes
|
|
465
533
|
|
|
466
534
|
|
|
535
|
+
def _build_axes_list_from_names(
|
|
536
|
+
axes_names: Sequence[str],
|
|
537
|
+
axes_setup: AxesSetup,
|
|
538
|
+
space_units: SpaceUnits | str | None = DefaultSpaceUnit,
|
|
539
|
+
time_units: TimeUnits | str | None = DefaultTimeUnit,
|
|
540
|
+
) -> list[Axis]:
|
|
541
|
+
"""Build a list of Axis objects from a list of axis names."""
|
|
542
|
+
axes = []
|
|
543
|
+
for name in axes_names:
|
|
544
|
+
c_name = axes_setup.get_canonical_name(name)
|
|
545
|
+
match c_name:
|
|
546
|
+
case "t":
|
|
547
|
+
axes.append(Axis(name=name, axis_type=AxisType.time, unit=time_units))
|
|
548
|
+
case "c":
|
|
549
|
+
axes.append(Axis(name=name, axis_type=AxisType.channel))
|
|
550
|
+
case "z" | "y" | "x":
|
|
551
|
+
axes.append(Axis(name=name, axis_type=AxisType.space, unit=space_units))
|
|
552
|
+
case _:
|
|
553
|
+
axes.append(Axis(name=name, axis_type=AxisType.space))
|
|
554
|
+
return axes
|
|
555
|
+
|
|
556
|
+
|
|
467
557
|
def build_canonical_axes_handler(
|
|
468
558
|
axes_names: Sequence[str],
|
|
559
|
+
canonical_channel_order: Sequence[str] | None = None,
|
|
469
560
|
space_units: SpaceUnits | str | None = DefaultSpaceUnit,
|
|
470
561
|
time_units: TimeUnits | str | None = DefaultTimeUnit,
|
|
471
|
-
# user defined args
|
|
472
|
-
axes_setup: AxesSetup | None = None,
|
|
473
|
-
allow_non_canonical_axes: bool = False,
|
|
474
|
-
strict_canonical_order: bool = False,
|
|
475
562
|
) -> AxesHandler:
|
|
476
563
|
"""Create a new canonical axes mapper.
|
|
477
564
|
|
|
@@ -482,33 +569,47 @@ def build_canonical_axes_handler(
|
|
|
482
569
|
- If an integer is provided, the axes are created from the last axis
|
|
483
570
|
to the first
|
|
484
571
|
e.g. 3 -> ["z", "y", "x"]
|
|
572
|
+
canonical_channel_order (Sequence[str], optional): The canonical channel
|
|
573
|
+
order. Defaults to None, which uses the default order.
|
|
485
574
|
space_units (SpaceUnits, optional): The space units. Defaults to None.
|
|
486
575
|
time_units (TimeUnits, optional): The time units. Defaults to None.
|
|
576
|
+
"""
|
|
577
|
+
if canonical_channel_order is None:
|
|
578
|
+
canonical_channel_order = canonical_axes_order()
|
|
579
|
+
axes_setup = AxesSetup.from_ordered_list(
|
|
580
|
+
axes_names=axes_names, canonical_order=canonical_channel_order
|
|
581
|
+
)
|
|
582
|
+
axes = _build_axes_list_from_names(
|
|
583
|
+
axes_names=axes_names,
|
|
584
|
+
axes_setup=axes_setup,
|
|
585
|
+
space_units=space_units,
|
|
586
|
+
time_units=time_units,
|
|
587
|
+
)
|
|
588
|
+
return AxesHandler(
|
|
589
|
+
axes=axes,
|
|
590
|
+
axes_setup=axes_setup,
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def build_axes_handler(
|
|
595
|
+
axes_names: Sequence[str],
|
|
596
|
+
axes_setup: AxesSetup | None = None,
|
|
597
|
+
) -> AxesHandler:
|
|
598
|
+
"""Create a new axes mapper.
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
axes_names (Sequence[str]): The axes names on disk.
|
|
487
602
|
axes_setup (AxesSetup, optional): The axis setup. Defaults to None.
|
|
488
603
|
allow_non_canonical_axes (bool, optional): Allow non canonical axes.
|
|
489
|
-
Defaults to False.
|
|
490
604
|
strict_canonical_order (bool, optional): Check if the axes are in the
|
|
491
605
|
canonical order. Defaults to False.
|
|
492
|
-
|
|
493
606
|
"""
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
case "c":
|
|
500
|
-
axes.append(Axis(name=name, axis_type=AxisType.channel))
|
|
501
|
-
case "z" | "y" | "x":
|
|
502
|
-
axes.append(Axis(name=name, axis_type=AxisType.space, unit=space_units))
|
|
503
|
-
case _:
|
|
504
|
-
raise NgioValueError(
|
|
505
|
-
f"Invalid axis name '{name}'. "
|
|
506
|
-
"Only 't', 'c', 'z', 'y', 'x' are allowed."
|
|
507
|
-
)
|
|
508
|
-
|
|
607
|
+
axes_setup = axes_setup if axes_setup is not None else AxesSetup()
|
|
608
|
+
axes = _build_axes_list_from_names(
|
|
609
|
+
axes_names=axes_names,
|
|
610
|
+
axes_setup=axes_setup,
|
|
611
|
+
)
|
|
509
612
|
return AxesHandler(
|
|
510
613
|
axes=axes,
|
|
511
614
|
axes_setup=axes_setup,
|
|
512
|
-
allow_non_canonical_axes=allow_non_canonical_axes,
|
|
513
|
-
strict_canonical_order=strict_canonical_order,
|
|
514
615
|
)
|
|
@@ -60,11 +60,20 @@ class Dataset:
|
|
|
60
60
|
@property
|
|
61
61
|
def pixel_size(self) -> PixelSize:
|
|
62
62
|
"""Return the pixel size for the dataset."""
|
|
63
|
+
scale = self._scale
|
|
64
|
+
pix_size_dict = {}
|
|
65
|
+
# Mandatory axes: x, y
|
|
66
|
+
for ax in ["x", "y"]:
|
|
67
|
+
index = self.axes_handler.get_index(ax)
|
|
68
|
+
assert index is not None
|
|
69
|
+
pix_size_dict[ax] = scale[index]
|
|
70
|
+
|
|
71
|
+
for ax in ["z", "t"]:
|
|
72
|
+
index = self.axes_handler.get_index(ax)
|
|
73
|
+
pix_size_dict[ax] = scale[index] if index is not None else 1.0
|
|
74
|
+
|
|
63
75
|
return PixelSize(
|
|
64
|
-
|
|
65
|
-
y=self.get_scale("y", default=1.0),
|
|
66
|
-
z=self.get_scale("z", default=1.0),
|
|
67
|
-
t=self.get_scale("t", default=1.0),
|
|
76
|
+
**pix_size_dict,
|
|
68
77
|
space_unit=self.axes_handler.space_unit,
|
|
69
78
|
time_unit=self.axes_handler.time_unit,
|
|
70
79
|
)
|
|
@@ -78,21 +87,3 @@ class Dataset:
|
|
|
78
87
|
def translation(self) -> tuple[float, ...]:
|
|
79
88
|
"""Return the translation as a tuple."""
|
|
80
89
|
return tuple(self._translation)
|
|
81
|
-
|
|
82
|
-
def get_scale(self, axis_name: str, default: float | None = None) -> float:
|
|
83
|
-
"""Return the scale for a given axis."""
|
|
84
|
-
idx = self.axes_handler.get_index(axis_name)
|
|
85
|
-
if idx is None:
|
|
86
|
-
if default is not None:
|
|
87
|
-
return default
|
|
88
|
-
raise ValueError(f"Axis {axis_name} not found in axes {self.axes_handler}.")
|
|
89
|
-
return self._scale[idx]
|
|
90
|
-
|
|
91
|
-
def get_translation(self, axis_name: str, default: float | None = None) -> float:
|
|
92
|
-
"""Return the translation for a given axis."""
|
|
93
|
-
idx = self.axes_handler.get_index(axis_name)
|
|
94
|
-
if idx is None:
|
|
95
|
-
if default is not None:
|
|
96
|
-
return default
|
|
97
|
-
raise ValueError(f"Axis {axis_name} not found in axes {self.axes_handler}.")
|
|
98
|
-
return self._translation[idx]
|