From aa1afc15666a63e486d6ee9d9ef5971ae166af1b Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 13 Mar 2024 09:17:05 +0100 Subject: [PATCH 1/4] Delegate default dims to geometry --- mikeio/dataset/_dataarray.py | 44 ++++---------------------- mikeio/spatial/_FM_geometry.py | 22 +++++++++++++ mikeio/spatial/_FM_geometry_layered.py | 3 ++ mikeio/spatial/_geometry.py | 14 ++++++-- mikeio/spatial/_grid_geometry.py | 9 ++++++ 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/mikeio/dataset/_dataarray.py b/mikeio/dataset/_dataarray.py index 86c10e8cd..1b818341e 100644 --- a/mikeio/dataset/_dataarray.py +++ b/mikeio/dataset/_dataarray.py @@ -195,50 +195,18 @@ def _parse_dims( @staticmethod def _guess_dims( - ndim: int, shape: Tuple[int, ...], n_timesteps: int, geometry: GeometryType + ndim: int, shape: Tuple[int, ...], n_timesteps: int, geometry: Any ) -> Tuple[str, ...]: - # TODO delete default dims to geometry - # This is not very robust, but is probably a reasonable guess time_is_first = (n_timesteps > 1) or (shape[0] == 1 and n_timesteps == 1) dims = ["time"] if time_is_first else [] ndim_no_time = ndim if (len(dims) == 0) else ndim - 1 - if isinstance(geometry, GeometryFMPointSpectrum): - if ndim_no_time == 1: - dims.append("frequency") - if ndim_no_time == 2: - dims.append("direction") - dims.append("frequency") - elif isinstance(geometry, GeometryFM3D): - if ndim_no_time > 0: - dims.append("element") - elif isinstance(geometry, GeometryFM2D): - if geometry._type == DfsuFileType.DfsuSpectral1D: - if ndim_no_time > 0: - dims.append("node") - else: - if ndim_no_time > 0: - dims.append("element") - if geometry.is_spectral: - if ndim_no_time == 2: - dims.append("frequency") - elif ndim_no_time == 3: - dims.append("direction") - dims.append("frequency") - elif isinstance(geometry, Grid1D): - dims.append("x") - elif isinstance(geometry, Grid2D): - dims.append("y") - dims.append("x") - else: - # gridded - if ndim_no_time > 2: - dims.append("z") - if ndim_no_time > 1: - dims.append("y") - if ndim_no_time > 0: - dims.append("x") + # TODO geometry should not be None + if geometry is None: + geometry = GeometryUndefined() + spdims = geometry.default_dims(ndim_no_time) + dims.extend(spdims) return tuple(dims) def _check_time_data_length(self, time: Sized) -> None: diff --git a/mikeio/spatial/_FM_geometry.py b/mikeio/spatial/_FM_geometry.py index 7cc5183e4..8b4852339 100644 --- a/mikeio/spatial/_FM_geometry.py +++ b/mikeio/spatial/_FM_geometry.py @@ -57,6 +57,12 @@ def __init__( self.x = x self.y = y + def default_dims(self, ndim_no_time: int) -> List[str]: + if ndim_no_time == 1: + return ["frequency"] + else: + return ["direction", "frequency"] + @property def is_layered(self) -> bool: return False @@ -449,6 +455,22 @@ def _area_is_polygon(area: Sequence[Tuple[float, float]] | Sequence[float]) -> b return True + def default_dims(self, ndim_no_time: int) -> List[str]: + dims = [] + if self._type == DfsuFileType.DfsuSpectral1D: + if ndim_no_time > 0: + dims.append("node") + else: + if ndim_no_time > 0: + dims.append("element") + if self.is_spectral: + if ndim_no_time == 2: + dims.append("frequency") + elif ndim_no_time == 3: + dims.append("direction") + dims.append("frequency") + return dims + @property def type_name(self): """Type name, e.g. Mesh, Dfsu2D""" diff --git a/mikeio/spatial/_FM_geometry_layered.py b/mikeio/spatial/_FM_geometry_layered.py index 8dfd1445d..5169b909e 100644 --- a/mikeio/spatial/_FM_geometry_layered.py +++ b/mikeio/spatial/_FM_geometry_layered.py @@ -700,6 +700,9 @@ def __init__( ) self.plot = _GeometryFMPlotter(self) + def default_dims(self, ndim_no_time: int) -> List[str]: + return ["element"] + @property def boundary_polylines(self): return self.geometry2d.boundary_polylines diff --git a/mikeio/spatial/_geometry.py b/mikeio/spatial/_geometry.py index b4e7098ae..c674b97df 100644 --- a/mikeio/spatial/_geometry.py +++ b/mikeio/spatial/_geometry.py @@ -1,6 +1,7 @@ from abc import ABC, abstractmethod from collections import namedtuple +from typing import List from mikecore.Projections import MapProjection @@ -15,6 +16,9 @@ def __init__(self, projection: str = "LONG/LAT") -> None: self._projstr = projection + def default_dims(self, ndim_no_time: int) -> List[str]: + return {0: [], 1: ["x"], 2: ["y", "x"], 3: ["z", "y", "x"]}[ndim_no_time] # type: ignore + @property def projection_string(self) -> str: """The projection string""" @@ -41,13 +45,17 @@ def ndim(self) -> int: pass -class GeometryUndefined: +class GeometryUndefined(_Geometry): def __repr__(self): return "GeometryUndefined()" + @property + def ndim(self) -> int: + return 0 + class GeometryPoint2D(_Geometry): - def __init__(self, x: float, y: float, projection:str = "LONG/LAT"): + def __init__(self, x: float, y: float, projection: str = "LONG/LAT"): super().__init__(projection) self.x = x self.y = y @@ -75,7 +83,7 @@ def to_shapely(self): class GeometryPoint3D(_Geometry): - def __init__(self, x: float, y: float, z: float, projection:str = "LONG/LAT"): + def __init__(self, x: float, y: float, z: float, projection: str = "LONG/LAT"): super().__init__(projection) self.x = x diff --git a/mikeio/spatial/_grid_geometry.py b/mikeio/spatial/_grid_geometry.py index 755c8ce8f..e7383285b 100644 --- a/mikeio/spatial/_grid_geometry.py +++ b/mikeio/spatial/_grid_geometry.py @@ -140,6 +140,9 @@ def __init__( def ndim(self) -> int: return 1 + def default_dims(self, ndim_no_time: int) -> List[str]: + return ["x"] + def __repr__(self) -> str: out = ["", _print_axis_txt("x", self.x, self.dx)] return "\n".join(out) @@ -489,6 +492,9 @@ def __init__( self.plot = _Grid2DPlotter(self) + def default_dims(self, ndim_no_time: int) -> List[str]: + return ["y", "x"] + @property def ndim(self) -> int: return 2 @@ -1132,6 +1138,9 @@ def __init__( self._origin = origin self._orientation = orientation + def default_dims(self, ndim_no_time: int) -> List[str]: + return ["z", "y", "x"] + @property def ndim(self) -> int: return 3 From 1c675ba860c8e59b3677755b2187cfde79e242b0 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 13 Mar 2024 10:42:34 +0100 Subject: [PATCH 2/4] Use property with no args --- mikeio/dataset/_dataarray.py | 32 +++++++++++++++++++------- mikeio/spatial/_FM_geometry.py | 29 ++++++++--------------- mikeio/spatial/_FM_geometry_layered.py | 3 --- mikeio/spatial/_geometry.py | 22 ++++++++++++++---- mikeio/spatial/_grid_geometry.py | 17 ++++++++------ 5 files changed, 61 insertions(+), 42 deletions(-) diff --git a/mikeio/dataset/_dataarray.py b/mikeio/dataset/_dataarray.py index 1b818341e..e6bc81b5e 100644 --- a/mikeio/dataset/_dataarray.py +++ b/mikeio/dataset/_dataarray.py @@ -11,7 +11,15 @@ Mapping, MutableMapping, ) -from typing import Any, Union, Literal, TYPE_CHECKING, overload, Callable, Tuple +from typing import ( + Any, + Union, + Literal, + TYPE_CHECKING, + overload, + Callable, + Tuple, +) import numpy as np @@ -151,13 +159,15 @@ def __init__( *, time: pd.DatetimeIndex | str | None = None, item: ItemInfo | None = None, - geometry: GeometryType = GeometryUndefined(), + geometry: GeometryType | None = None, zn: np.ndarray | None = None, dims: Sequence[str] | None = None, ) -> None: # TODO: add optional validation validate=True self._values = self._parse_data(data) self.time: pd.DatetimeIndex = self._parse_time(time) + + geometry = GeometryUndefined() if geometry is None else geometry self.dims = self._parse_dims(dims, geometry) self._check_time_data_length(self.time) @@ -195,18 +205,24 @@ def _parse_dims( @staticmethod def _guess_dims( - ndim: int, shape: Tuple[int, ...], n_timesteps: int, geometry: Any + ndim: int, shape: Tuple[int, ...], n_timesteps: int, geometry: GeometryType ) -> Tuple[str, ...]: # This is not very robust, but is probably a reasonable guess time_is_first = (n_timesteps > 1) or (shape[0] == 1 and n_timesteps == 1) dims = ["time"] if time_is_first else [] ndim_no_time = ndim if (len(dims) == 0) else ndim - 1 - # TODO geometry should not be None - if geometry is None: - geometry = GeometryUndefined() - spdims = geometry.default_dims(ndim_no_time) - dims.extend(spdims) + if isinstance(geometry, GeometryUndefined): + DIMS_MAPPING = { + 0: [], + 1: ["x"], + 2: ["y", "x"], + 3: ["z", "y", "x"], + } + spdims = DIMS_MAPPING[ndim_no_time] + else: + spdims = geometry.default_dims + dims.extend(spdims) # type: ignore return tuple(dims) def _check_time_data_length(self, time: Sized) -> None: diff --git a/mikeio/spatial/_FM_geometry.py b/mikeio/spatial/_FM_geometry.py index 8b4852339..60bf8099a 100644 --- a/mikeio/spatial/_FM_geometry.py +++ b/mikeio/spatial/_FM_geometry.py @@ -57,11 +57,12 @@ def __init__( self.x = x self.y = y - def default_dims(self, ndim_no_time: int) -> List[str]: - if ndim_no_time == 1: - return ["frequency"] + @property + def default_dims(self) -> Tuple[str, ...]: + if self.directions is None: + return ("frequency",) else: - return ["direction", "frequency"] + return ("direction", "frequency") @property def is_layered(self) -> bool: @@ -328,6 +329,10 @@ def _reindex(self): self._node_ids = new_node_ids self._element_ids = new_element_ids + @property + def default_dims(self) -> Tuple[str, ...]: + return ("element",) + @property def is_spectral(self) -> bool: return False @@ -455,22 +460,6 @@ def _area_is_polygon(area: Sequence[Tuple[float, float]] | Sequence[float]) -> b return True - def default_dims(self, ndim_no_time: int) -> List[str]: - dims = [] - if self._type == DfsuFileType.DfsuSpectral1D: - if ndim_no_time > 0: - dims.append("node") - else: - if ndim_no_time > 0: - dims.append("element") - if self.is_spectral: - if ndim_no_time == 2: - dims.append("frequency") - elif ndim_no_time == 3: - dims.append("direction") - dims.append("frequency") - return dims - @property def type_name(self): """Type name, e.g. Mesh, Dfsu2D""" diff --git a/mikeio/spatial/_FM_geometry_layered.py b/mikeio/spatial/_FM_geometry_layered.py index 5169b909e..8dfd1445d 100644 --- a/mikeio/spatial/_FM_geometry_layered.py +++ b/mikeio/spatial/_FM_geometry_layered.py @@ -700,9 +700,6 @@ def __init__( ) self.plot = _GeometryFMPlotter(self) - def default_dims(self, ndim_no_time: int) -> List[str]: - return ["element"] - @property def boundary_polylines(self): return self.geometry2d.boundary_polylines diff --git a/mikeio/spatial/_geometry.py b/mikeio/spatial/_geometry.py index c674b97df..9b2726ec9 100644 --- a/mikeio/spatial/_geometry.py +++ b/mikeio/spatial/_geometry.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from collections import namedtuple -from typing import List +from typing import Tuple from mikecore.Projections import MapProjection @@ -16,9 +16,6 @@ def __init__(self, projection: str = "LONG/LAT") -> None: self._projstr = projection - def default_dims(self, ndim_no_time: int) -> List[str]: - return {0: [], 1: ["x"], 2: ["y", "x"], 3: ["z", "y", "x"]}[ndim_no_time] # type: ignore - @property def projection_string(self) -> str: """The projection string""" @@ -44,6 +41,11 @@ def is_local_coordinates(self) -> bool: def ndim(self) -> int: pass + @property + @abstractmethod + def default_dims(self) -> Tuple[str, ...]: + pass + class GeometryUndefined(_Geometry): def __repr__(self): @@ -53,6 +55,10 @@ def __repr__(self): def ndim(self) -> int: return 0 + @property + def default_dims(self) -> Tuple[str, ...]: + raise NotImplementedError() + class GeometryPoint2D(_Geometry): def __init__(self, x: float, y: float, projection: str = "LONG/LAT"): @@ -60,6 +66,10 @@ def __init__(self, x: float, y: float, projection: str = "LONG/LAT"): self.x = x self.y = y + @property + def default_dims(self) -> Tuple[str, ...]: + return () + def __repr__(self) -> str: return f"GeometryPoint2D(x={self.x}, y={self.y})" @@ -93,6 +103,10 @@ def __init__(self, x: float, y: float, z: float, projection: str = "LONG/LAT"): def __repr__(self): return f"GeometryPoint3D(x={self.x}, y={self.y}, z={self.z})" + @property + def default_dims(self) -> Tuple[str, ...]: + return () + @property def wkt(self) -> str: return f"POINT Z ({self.x} {self.y} {self.z})" diff --git a/mikeio/spatial/_grid_geometry.py b/mikeio/spatial/_grid_geometry.py index e7383285b..122c8ce8e 100644 --- a/mikeio/spatial/_grid_geometry.py +++ b/mikeio/spatial/_grid_geometry.py @@ -140,8 +140,9 @@ def __init__( def ndim(self) -> int: return 1 - def default_dims(self, ndim_no_time: int) -> List[str]: - return ["x"] + @property + def default_dims(self) -> Tuple[str, ...]: + return ("x",) def __repr__(self) -> str: out = ["", _print_axis_txt("x", self.x, self.dx)] @@ -492,8 +493,9 @@ def __init__( self.plot = _Grid2DPlotter(self) - def default_dims(self, ndim_no_time: int) -> List[str]: - return ["y", "x"] + @property + def default_dims(self) -> Tuple[str, ...]: + return ("y", "x") @property def ndim(self) -> int: @@ -1138,9 +1140,10 @@ def __init__( self._origin = origin self._orientation = orientation - def default_dims(self, ndim_no_time: int) -> List[str]: - return ["z", "y", "x"] - + @property + def default_dims(self) -> Tuple[str, ...]: + return ("z", "y", "x") + @property def ndim(self) -> int: return 3 From 0aad9a3cf303956667a8cdc2483bdf9a63d97e37 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 13 Mar 2024 11:47:45 +0100 Subject: [PATCH 3/4] Update mikeio/spatial/_geometry.py --- mikeio/spatial/_geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mikeio/spatial/_geometry.py b/mikeio/spatial/_geometry.py index 9b2726ec9..3cc047ac3 100644 --- a/mikeio/spatial/_geometry.py +++ b/mikeio/spatial/_geometry.py @@ -53,7 +53,7 @@ def __repr__(self): @property def ndim(self) -> int: - return 0 + return NotImplementedError() @property def default_dims(self) -> Tuple[str, ...]: From 7b4dedde69e52bf7e07656dab80f35cc61da17a4 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 13 Mar 2024 11:59:56 +0100 Subject: [PATCH 4/4] =?UTF-8?q?Raise=20not=20return=F0=9F=98=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mikeio/spatial/_geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mikeio/spatial/_geometry.py b/mikeio/spatial/_geometry.py index 3cc047ac3..3ab65e516 100644 --- a/mikeio/spatial/_geometry.py +++ b/mikeio/spatial/_geometry.py @@ -53,7 +53,7 @@ def __repr__(self): @property def ndim(self) -> int: - return NotImplementedError() + raise NotImplementedError() @property def default_dims(self) -> Tuple[str, ...]: