Skip to content

Commit

Permalink
Add DataCodingFormat classes (3,7) for S104 v1_1
Browse files Browse the repository at this point in the history
  • Loading branch information
erin-nagel committed Oct 20, 2023
1 parent 8da5889 commit b1ae39c
Show file tree
Hide file tree
Showing 9 changed files with 437 additions and 77 deletions.
2 changes: 1 addition & 1 deletion s100py/s104/README.md
Expand Up @@ -159,7 +159,7 @@ update_meta = {
'num_instances': 1
}

data_file = s104.utils.create_s104("test_s104.h5")
data_file = s104.utils.create_s104("test_s104.h5", 2)

s104.utils.add_metadata(metadata, data_file)
data_series_time_001 = datetime_forecast_issuance + datetime_interval
Expand Down
4 changes: 2 additions & 2 deletions s100py/s104/__init__.py
Expand Up @@ -3,8 +3,8 @@
Tools for converting hydrodynamic models to S-104.
"""
from .api import WaterLevelUncertaintyInformation, WaterLevelUncertaintyDataset, GeometryValuesDataset, WaterLevelValues, \
WaterLevelGroup, WaterLevelGroupList, WaterLevelFeatureInstance, WaterLevelList, WaterLevelContainer, \
from .api import WaterLevelUncertaintyInformation, WaterLevelUncertaintyDataset, WaterLevelValues, \
WaterLevelGroup, WaterLevelGroupList, WaterLevelFeatureInstanceBase, WaterLevelListBase, WaterLevelContainerBase, \
WaterLevelFeatureDataset, GroupF, S104Root, S104File, S104Exception, PRODUCT_SPECIFICATION, EDITION

from .utils import create_s104, add_metadata, write_data_file
Expand Down
4 changes: 2 additions & 2 deletions s100py/s104/v1_0/__init__.py
Expand Up @@ -3,8 +3,8 @@
Tools for converting hydrodynamic models to S-104.
"""
from .api import WaterLevelUncertaintyInformation, WaterLevelUncertaintyDataset, GeometryValuesDataset, WaterLevelValues, \
WaterLevelGroup, WaterLevelGroupList, WaterLevelFeatureInstance, WaterLevelList, WaterLevelContainer, \
from .api import WaterLevelUncertaintyInformation, WaterLevelUncertaintyDataset, WaterLevelValues, \
WaterLevelGroup, WaterLevelGroupList, WaterLevelFeatureInstanceBase, WaterLevelListBase, WaterLevelContainerBase, \
WaterLevelFeatureDataset, GroupF, S104Root, S104File, S104Exception, PRODUCT_SPECIFICATION, EDITION

from .utils import create_s104, add_metadata, write_data_file
Expand Down
80 changes: 64 additions & 16 deletions s100py/s104/v1_0/api.py
Expand Up @@ -8,9 +8,12 @@
import numpy
import h5py

from s100py.s1xx import s1xx_sequence, S1xxObject, S1xxCollection, S1xxDatasetBase, S1xxGridsBase, S1XXFile, h5py_string_dtype, is_sub_class
from ...s100.v4_0.api import S100Exception, GeometryValuesDataset, FeatureContainerDCF2, FeatureInstanceDCF2, FeatureInformation, FeatureInformationDataset, GroupFBase, GeographicBoundingBox
from ...s100.v5_0.api import S100File, VERTICAL_DATUM, VERTICAL_DATUM_REFERENCE, VERTICAL_CS, VERTICAL_COORDINATE_BASE, HORIZONTAL_DATUM_REFERENCE, HORIZONTAL_CS, TYPE_OF_HORIZONTAL_CRS, PROJECTION_METHOD
from s100py.s1xx import s1xx_sequence, S1xxObject, S1xxCollection, S1xxDatasetBase, S1xxGridsBase, S1XXFile, \
h5py_string_dtype, is_sub_class
from ...s100.v4_0.api import S100Exception, FeatureContainerDCF2, FeatureInstanceDCF2, FeatureContainerDCF3, \
FeatureInstanceDCF3, FeatureInformation, FeatureInformationDataset, GroupFBase, GeographicBoundingBox
from ...s100.v5_0.api import S100File, VERTICAL_DATUM, VERTICAL_DATUM_REFERENCE, VERTICAL_CS, VERTICAL_COORDINATE_BASE,\
HORIZONTAL_DATUM_REFERENCE, HORIZONTAL_CS, TYPE_OF_HORIZONTAL_CRS, PROJECTION_METHOD

WATER_LEVEL = "WaterLevel"

Expand Down Expand Up @@ -270,10 +273,9 @@ def metadata_type(self) -> type:
return WaterLevelGroup


class WaterLevelFeatureInstance(FeatureInstanceDCF2):
""" Basic template for the name of the attribute
Attribute name will be automatically determined based on the array position
of the S104_MetadataList
class WaterLevelFeatureInstanceBase:
""" Basic template for the name of the attribute will be automatically determined
based on the array position of the S104_MetadataList
"""
__water_level_group_hdf_name__ = "Group" + r"[\._]\d+"
__uncertainty_dataset_hdf_name__ = "uncertainty"
Expand Down Expand Up @@ -335,7 +337,16 @@ def type_of_water_level_data_create(self):
self.type_of_water_level_data = list(self.__type_of_water_level_data_type__)[0]


class WaterLevelList(S104MetadataListBase):
class WaterLevelFeatureInstanceDCF2(FeatureInstanceDCF2, WaterLevelFeatureInstanceBase):
pass


class WaterLevelFeatureInstanceDCF3(FeatureInstanceDCF3, WaterLevelFeatureInstanceBase):
@property
def __number_of_nodes_type__(self) -> Type[int]:
return numpy.uint32

class WaterLevelListBase(S104MetadataListBase):
"""
This is the set of WaterLevel.NN that act like a list here.
They will contain a list of Groups.NNN as well as other attributes etc.
Expand All @@ -349,12 +360,20 @@ def __version__(self) -> int:
def metadata_name(self) -> str:
return WATER_LEVEL


class WaterLevelListDCF2(WaterLevelListBase):
@property
def metadata_type(self) -> Type[WaterLevelFeatureInstance]:
return WaterLevelFeatureInstance
def metadata_type(self) -> Type[WaterLevelFeatureInstanceBase]:
return WaterLevelFeatureInstanceDCF2


class WaterLevelContainer(FeatureContainerDCF2):
class WaterLevelListDCF3(WaterLevelListBase):
@property
def metadata_type(self) -> Type[WaterLevelFeatureInstanceBase]:
return WaterLevelFeatureInstanceDCF3


class WaterLevelContainerBase:
""" This is the WaterLevel right off the root of the HDF5 which has possible attributes from S100 spec table 10c-10
This will hold child groups named WaterLevel.NN
"""
Expand All @@ -370,10 +389,6 @@ class WaterLevelContainer(FeatureContainerDCF2):
def __version__(self) -> int:
return 1

@property
def __water_level_type__(self):
return WaterLevelList

def water_level_create(self):
# noinspection PyAttributeOutsideInit
# pylint: disable=attribute-defined-outside-init
Expand Down Expand Up @@ -451,6 +466,19 @@ def method_water_level_product_create(self):
# pylint: disable=attribute-defined-outside-init
self.method_water_level_product = self.__method_water_level_product_type__()


class WaterLevelContainerDCF2(FeatureContainerDCF2, WaterLevelContainerBase):
@property
def __water_level_type__(self):
return WaterLevelListDCF2


class WaterLevelContainerDCF3(FeatureContainerDCF3, WaterLevelContainerBase):
@property
def __water_level_type__(self):
return WaterLevelListDCF3


class WaterLevelFeatureDataset(FeatureInformationDataset):
"""Create group_f feature dataset"""

Expand Down Expand Up @@ -1160,7 +1188,7 @@ def water_level(self) -> S1xxObject:

@property
def __water_level_type__(self):
return WaterLevelContainer
return WaterLevelContainerBase

def water_level_create(self):
# noinspection PyAttributeOutsideInit
Expand Down Expand Up @@ -1202,5 +1230,25 @@ class S104File(S100File):
""" HDF5 file object"""
PRODUCT_SPECIFICATION = 'INT.IHO.S-104.1.0'

@staticmethod
def make_container_for_dcf(data_coding_format):
if data_coding_format == 2:
container = WaterLevelContainerDCF2()
elif data_coding_format == 3:
container = WaterLevelContainerDCF3()
else:
raise S104Exception(f"DCF {data_coding_format} not supported")
return container

def __init__(self, *args, **kywrds):
super().__init__(*args, root=S104Root, **kywrds)
# when reading from a file we need to look inside the DataCodingFormat to know what type of object to create
try:
container_key = f'/{WATER_LEVEL}'
dcf = self[container_key].attrs['dataCodingFormat']
except KeyError: # must not be reading an existing file or doesn't have data for some reason
pass
else:
self.root.water_level = self.make_container_for_dcf(dcf)
self.root.water_level.read(self[container_key])

10 changes: 6 additions & 4 deletions s100py/s104/v1_0/utils.py
Expand Up @@ -29,14 +29,16 @@ def _get_S104File(output_file):
return data_file


def create_s104(output_file) -> S104File:
def create_s104(output_file, dcf) -> S104File:
""" Creates or updates an S104File object.
Default values are set for any data that doesn't have options or are mandatory to be filled in the S104 spec.
Parameters
----------
output_file
S104File object
dcf
S100 Data Coding Format (Int)
Returns
-------
Expand All @@ -47,7 +49,8 @@ def create_s104(output_file) -> S104File:
"""
data_file = _get_S104File(output_file)
root = data_file.root
root.water_level_create()
root.water_level = data_file.make_container_for_dcf(dcf)
root.water_level.water_level_create()

root.feature_information_create()
group_f = root.feature_information
Expand Down Expand Up @@ -204,9 +207,8 @@ def add_metadata(metadata: dict, data_file) -> S104File:
"""
root = data_file.root

water_level_feature = root.water_level
water_level_feature.water_level_create()

water_level_feature_instance_01 = water_level_feature.water_level.append_new_item()

water_level_feature_instance_01.water_level_group_create()
Expand Down
4 changes: 2 additions & 2 deletions s100py/s104/v1_1/__init__.py
Expand Up @@ -3,8 +3,8 @@
Tools for converting hydrodynamic models to S-104.
"""
from .api import WaterLevelUncertaintyInformation, WaterLevelUncertaintyDataset, GeometryValuesDataset, WaterLevelValues, \
WaterLevelGroup, WaterLevelGroupList, WaterLevelFeatureInstance, WaterLevelList, WaterLevelContainer, \
from .api import WaterLevelUncertaintyInformation, WaterLevelUncertaintyDataset, WaterLevelValues, \
WaterLevelGroup, WaterLevelGroupList, WaterLevelFeatureInstanceBase, WaterLevelListBase, WaterLevelContainerBase, \
WaterLevelFeatureDataset, GroupF, S104Root, S104File, S104Exception, PRODUCT_SPECIFICATION, EDITION

from .utils import create_s104, add_metadata, write_data_file
Expand Down
101 changes: 85 additions & 16 deletions s100py/s104/v1_1/api.py
Expand Up @@ -5,8 +5,9 @@
import h5py

from s100py.s1xx import s1xx_sequence, S1xxObject, S1xxCollection, S1xxDatasetBase, S1xxGridsBase, S1XXFile, h5py_string_dtype
from ...s100.v5_0.api import S100File, S100Root, S100Exception, GeometryValuesDataset, FeatureContainerDCF2, FeatureInstanceDCF2, \
FeatureInformation, FeatureInformationDataset, GroupFBase, VERTICAL_CS, VERTICAL_DATUM_REFERENCE, VERTICAL_DATUM
from ...s100.v5_0.api import S100File, S100Root, S100Exception, FeatureContainerDCF2, FeatureInstanceDCF2, \
FeatureContainerDCF3, FeatureInstanceDCF3, FeatureContainerDCF7, FeatureInstanceDCF7, FeatureInformation, \
FeatureInformationDataset, GroupFBase, VERTICAL_CS, VERTICAL_DATUM_REFERENCE, VERTICAL_DATUM

WATER_LEVEL = "WaterLevel"

Expand Down Expand Up @@ -51,6 +52,10 @@ class S104Exception(S100Exception):
pass


class S104UnspecifiedClassException(S100Exception):
pass


class DATA_DYNAMICITY(Enum):
"""
S104 v1.1 Table 12.10 - Classification of data according to the
Expand Down Expand Up @@ -157,10 +162,6 @@ def metadata_type(self) -> Type[WaterLevelUncertaintyInformation]:
return WaterLevelUncertaintyInformation






class WaterLevelValues(S1xxGridsBase):
"""NNN Group Datasets"""
__water_level_height_hdf_name__ = "waterLevelHeight"
Expand Down Expand Up @@ -304,7 +305,7 @@ def metadata_type(self) -> type:
return WaterLevelGroup


class WaterLevelFeatureInstance(FeatureInstanceDCF2):
class WaterLevelFeatureInstanceBase:
""" Basic template for the name of the attribute.
Attribute name will be automatically determined based on the array position
of the S104_MetadataList
Expand Down Expand Up @@ -377,7 +378,23 @@ def __time_record_interval_type__(self) -> Type[int]:
return numpy.uint16


class WaterLevelList(S104MetadataListBase):
class WaterLevelFeatureInstanceDCF2(FeatureInstanceDCF2, WaterLevelFeatureInstanceBase):
pass


class WaterLevelFeatureInstanceDCF3(FeatureInstanceDCF3, WaterLevelFeatureInstanceBase):
@property
def __number_of_nodes_type__(self) -> Type[int]:
return numpy.uint32


class WaterLevelFeatureInstanceDCF7(FeatureInstanceDCF7, WaterLevelFeatureInstanceBase):
@property
def __number_of_nodes_type__(self) -> Type[int]:
return numpy.uint32


class WaterLevelListBase(S104MetadataListBase):
"""
This is the set of WaterLevel.NN that act like a list here.
They will contain a list of Groups.NNN as well as other attributes etc.
Expand All @@ -391,12 +408,26 @@ def __version__(self) -> int:
def metadata_name(self) -> str:
return WATER_LEVEL


class WaterLevelListDCF2(WaterLevelListBase):
@property
def metadata_type(self) -> Type[WaterLevelFeatureInstance]:
return WaterLevelFeatureInstance
def metadata_type(self) -> Type[WaterLevelFeatureInstanceBase]:
return WaterLevelFeatureInstanceDCF2


class WaterLevelContainer(FeatureContainerDCF2):
class WaterLevelListDCF3(WaterLevelListBase):
@property
def metadata_type(self) -> Type[WaterLevelFeatureInstanceBase]:
return WaterLevelFeatureInstanceDCF3


class WaterLevelListDCF7(WaterLevelListBase):
@property
def metadata_type(self) -> Type[WaterLevelFeatureInstanceBase]:
return WaterLevelFeatureInstanceDCF7


class WaterLevelContainerBase:
""" This is the WaterLevel right off the root of the HDF5 which has possible attributes from S100 spec table 10c-10
This will hold child groups named WaterLevel.NN
"""
Expand All @@ -412,10 +443,6 @@ class WaterLevelContainer(FeatureContainerDCF2):
def __version__(self) -> int:
return 1

@property
def __water_level_type__(self):
return WaterLevelList

def water_level_create(self):
# noinspection PyAttributeOutsideInit
# pylint: disable=attribute-defined-outside-init
Expand Down Expand Up @@ -494,6 +521,24 @@ def method_water_level_product_create(self):
self.method_water_level_product = self.__method_water_level_product_type__()


class WaterLevelContainerDCF2(FeatureContainerDCF2, WaterLevelContainerBase):
@property
def __water_level_type__(self):
return WaterLevelListDCF2


class WaterLevelContainerDCF3(FeatureContainerDCF3, WaterLevelContainerBase):
@property
def __water_level_type__(self):
return WaterLevelListDCF3


class WaterLevelContainerDCF7(FeatureContainerDCF7, WaterLevelContainerBase):
@property
def __water_level_type__(self):
return WaterLevelListDCF7


class WaterLevelFeatureDataset(FeatureInformationDataset):
"""Create group_f feature dataset"""

Expand Down Expand Up @@ -571,11 +616,14 @@ def water_level(self) -> S1xxObject:

@property
def __water_level_type__(self):
return WaterLevelContainer
return WaterLevelContainerBase

def water_level_create(self):
# noinspection PyAttributeOutsideInit
# pylint: disable=attribute-defined-outside-init
print("You should not create a generic surface_current object but manually create"
"a WaterLevelContainerDCF2 or WaterLevelContainerDCF3")
raise S104UnspecifiedClassException("You should create WaterLevelContainerDCFx (x=2,3,8 etc)")
self.water_level = self.__water_level_type__()

@water_level.setter
Expand Down Expand Up @@ -658,5 +706,26 @@ class S104File(S100File):
""" HDF5 file object"""
PRODUCT_SPECIFICATION = 'INT.IHO.S-104.1.1'

@staticmethod
def make_container_for_dcf(data_coding_format):
if data_coding_format == 2:
container = WaterLevelContainerDCF2()
elif data_coding_format == 3:
container = WaterLevelContainerDCF3()
elif data_coding_format == 7:
container = WaterLevelContainerDCF7()
else:
raise S104Exception(f"DCF {data_coding_format} not supported")
return container

def __init__(self, *args, **kywrds):
super().__init__(*args, root=S104Root, **kywrds)
# when reading from a file we need to look inside the DataCodingFormat to know what type of object to create
try:
container_key = f'/{WATER_LEVEL}'
dcf = self[container_key].attrs['dataCodingFormat']
except KeyError: # must not be reading an existing file or doesn't have data for some reason
pass
else:
self.root.water_level = self.make_container_for_dcf(dcf)
self.root.water_level.read(self[container_key])

0 comments on commit b1ae39c

Please sign in to comment.