Source code for geoh5py.data.referenced_data
# Copyright (c) 2024 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
import numpy as np
from .data_type import GeometricDataValueMapType, ReferenceDataType
from .geometric_data import GeometricDataConstants
from .integer_data import IntegerData
from .primitive_type_enum import PrimitiveTypeEnum
from .reference_value_map import ReferenceValueMap
[docs]
class ReferencedData(IntegerData):
"""
Reference data described by indices and associated strings.
"""
def __init__(self, **kwargs):
self._data_maps = None
super().__init__(**kwargs)
@property
def data_maps(self) -> dict[str, GeometricDataConstants] | None:
"""
A reference dictionary mapping properties to numpy arrays.
"""
if self._data_maps is None and self.metadata is not None:
data_maps = {}
for name, uid in self.metadata.items():
child = self.workspace.get_entity(uid)[0]
if isinstance(child, GeometricDataConstants) and isinstance(
child.entity_type, GeometricDataValueMapType
):
data_maps[name] = child
if data_maps:
self._data_maps = data_maps
return self._data_maps
@data_maps.setter
def data_maps(self, data_map: dict[str, GeometricDataConstants] | None):
if data_map is not None:
if not isinstance(data_map, dict):
raise TypeError("Property maps must be a dictionary")
for key, val in data_map.items():
if not isinstance(val, GeometricDataConstants):
raise TypeError(
f"Property maps value for '{key}' must be a 'GeometricDataConstants'."
)
if (
not isinstance(val.entity_type, GeometricDataValueMapType)
or val.entity_type.value_map is None
):
raise ValueError(
f"Property maps value for '{key}' must have a "
"'GeometricDataValueMapType' entity type and a 'value_map' assigned."
)
self.metadata = {child.name: child.uid for child in data_map.values()}
self._data_maps = data_map
if self.on_file:
self.workspace.update_attribute(self, "data_map")
@property
def entity_type(self) -> ReferenceDataType:
"""
The associated reference data type.
"""
return self._entity_type
@entity_type.setter
def entity_type(self, data_type: ReferenceDataType):
if not isinstance(data_type, ReferenceDataType):
raise TypeError("entity_type must be of type ReferenceDataType")
self._entity_type = data_type
if self.on_file:
self.workspace.update_attribute(self, "entity_type")
@property
def mapped_values(self) -> np.ndarray:
"""
The values mapped from the reference data.
"""
if self.value_map is None:
raise ValueError("Entity type must have a value map.")
return self.value_map.map_values(self.values)
[docs]
@classmethod
def primitive_type(cls) -> PrimitiveTypeEnum:
return PrimitiveTypeEnum.REFERENCED
@property
def value_map(self) -> ReferenceValueMap | None:
"""
Pointer to the :obj:`data.data_type.DataType.value_map`
"""
return self.entity_type.value_map
[docs]
def remove_data_map(self, name: str):
"""
Remove a data map from the list of children.
:param name: The name of the data map to remove.
"""
if self.data_maps is None or name not in self.data_maps:
return
child = self.data_maps[name]
child.allow_delete = True
self.workspace.remove_entity(child)
self.workspace.remove_entity(child.entity_type)
del self.data_maps[name]
self.data_maps = self._data_maps
[docs]
def add_data_map(self, name: str, data: np.ndarray | dict):
"""
Add a data map to the value map.
:param name: The name of the data map.
:param data: The data map to add.
"""
value_map = self.data_maps or {}
if name in value_map:
raise KeyError(f"Data map '{name}' already exists.")
if not isinstance(data, dict | np.ndarray):
raise TypeError("Data map values must be a numpy array or dict")
if isinstance(data, np.ndarray) and data.ndim != 2:
raise ValueError("Data map must be a 2D array")
reference_data = ReferenceValueMap(data, name=name)
if self.entity_type.value_map is None:
raise ValueError("Entity type must have a value map.")
if not set(reference_data.map["Key"]).issubset(
set(self.entity_type.value_map.map["Key"])
):
raise KeyError("Data map keys must be a subset of the value map keys.")
data_type = GeometricDataValueMapType(
self.workspace,
value_map=reference_data,
parent=self.parent,
name=self.entity_type.name + f": {name}",
)
geom_data = self.parent.add_data(
{
name: {
"association": self.association,
"entity_type": data_type,
}
}
)
value_map[name] = geom_data
self.data_maps = value_map