Source code for geoh5py.objects.grid_object
# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
# Copyright (c) 2025 Mira Geoscience Ltd. '
# '
# This file is part of geoh5py. '
# '
# geoh5py is free software: you can redistribute it and/or modify '
# it under the terms of the GNU Lesser General Public License as published by '
# the Free Software Foundation, either version 3 of the License, or '
# (at your option) any later version. '
# '
# geoh5py is distributed in the hope that it will be useful, '
# but WITHOUT ANY WARRANTY; without even the implied warranty of '
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the '
# GNU Lesser General Public License for more details. '
# '
# You should have received a copy of the GNU Lesser General Public License '
# along with geoh5py. If not, see <https://www.gnu.org/licenses/>. '
# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
from __future__ import annotations
from abc import ABC, abstractmethod
from numbers import Real
import numpy as np
from .object_base import ObjectBase
ORIGIN_TYPE = np.dtype([("x", float), ("y", float), ("z", float)])
[docs]
class GridObject(ObjectBase, ABC):
"""
Base class for object with centroids.
:param origin: Origin of the object.
:param rotation: Rotation angle (clockwise) about the vertical axis.
"""
_attribute_map = ObjectBase._attribute_map.copy()
def __init__(
self,
origin: np.ndarray | tuple = (0.0, 0.0, 0.0),
rotation: float = 0.0,
**kwargs,
):
self._centroids: np.ndarray | None = None
super().__init__(**kwargs)
self.origin = origin
self.rotation = rotation
@property
@abstractmethod
def centroids(self) -> np.ndarray:
"""
Cell center locations in world coordinates of shape (n_cells, 3).
"""
@property
def n_cells(self) -> int:
"""
Total number of cells
"""
return int(np.prod(self.shape))
@property
def rotation(self) -> float:
"""
Clockwise rotation angle (degree) about the vertical axis.
"""
return self._rotation
@rotation.setter
def rotation(self, value: np.ndarray | Real):
if isinstance(value, Real):
value = np.r_[value]
if not isinstance(value, np.ndarray) or value.shape != (1,):
raise TypeError("Rotation angle must be a float of shape (1,)")
self._centroids = None
self._rotation = value.astype(float).item()
if self.on_file:
self.workspace.update_attribute(self, "attributes")
@property
def origin(self) -> np.ndarray:
"""
Coordinates of the origin, shape (3, ).
"""
return self._origin
@origin.setter
def origin(self, values: np.ndarray | list | tuple):
if isinstance(values, (list, tuple)):
values = np.array(values)
if not isinstance(values, (np.ndarray, np.void)):
raise TypeError(
"Attribute 'origin' must be a list, tuple or numpy array. "
f"Object of type {type(values)} provided."
)
if np.issubdtype(values.dtype, np.number):
if len(values) != 3:
raise ValueError(
"Attribute 'origin' must be a list or array of shape (3,). "
f"Array of shape {values.shape} provided."
)
values = np.asarray(tuple(values), dtype=ORIGIN_TYPE)
if values.dtype != np.dtype(ORIGIN_TYPE):
raise ValueError(f"Array of 'origin' must be of dtype = {ORIGIN_TYPE}")
self._centroids = None
if getattr(self, "_origin", None) is not None and self.on_file:
self.workspace.update_attribute(self, "attributes")
self._origin = values
@property
@abstractmethod
def shape(self) -> np.ndarray:
"""
Cell center locations in world coordinates.
"""