diff --git a/edr_server/core/models/extents.py b/edr_server/core/models/extents.py index 9d356a6..3e00f62 100644 --- a/edr_server/core/models/extents.py +++ b/edr_server/core/models/extents.py @@ -211,9 +211,25 @@ class VerticalExtent(EdrModel["VerticalExtent"]): # * list of vertical levels (e.g. "2",10,"80","100"} # The value `null` is supported and indicates an open vertical interval. + def __post_init__(self): + if not isinstance(self.values, List): + raise TypeError( + f'Expected List of values, received {type(self.values)}') + if not all(isinstance((invalid_value := value), (float, str)) for value in self.values): + raise TypeError( + f"Expected all float or string values, received value '{invalid_value}' of type {type(invalid_value)}") + if not isinstance(self.vrs, CrsObject): + raise TypeError(f'Expected CrsObject, received {type(self.vrs)}') + @classmethod def _prepare_json_for_init(cls, json_dict: JsonDict) -> JsonDict: json_dict["vrs"] = CrsObject.from_wkt(json_dict["vrs"]) + + with suppress(KeyError): # Remove things not required by __init__ + # 'interval' stores the bounds, which is different from the 'intervals' argument to the __init__ method + del json_dict["interval"] + del json_dict["name"] + return json_dict @classmethod diff --git a/setup.cfg b/setup.cfg index 0a2da31..25c1ce5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,7 @@ version = 0.1.0 # We're affected by https://bugs.python.org/issue43923, so can only use 3.7 & 3.8 python_requires = >=3.7,<3.9 install_requires = + numpy shapely pyproj tornado>=6.1 diff --git a/tests/unit/core/models/test_extents.py b/tests/unit/core/models/test_extents.py index 2d7e8b3..954fae4 100644 --- a/tests/unit/core/models/test_extents.py +++ b/tests/unit/core/models/test_extents.py @@ -1,9 +1,11 @@ import unittest from datetime import datetime, timedelta from shapely.geometry import Polygon +import numpy as np -from edr_server.core.models.extents import TemporalExtent, SpatialExtent +from edr_server.core.models.extents import TemporalExtent, SpatialExtent, VerticalExtent from edr_server.core.models.time import DateTimeInterval, Duration +from edr_server.core.models.crs import CrsObject class TemporalExtentTest(unittest.TestCase): @@ -443,3 +445,58 @@ def test_init_type_checking_crs(self): with self.assertRaisesRegex(TypeError, "Expected CrsObject, received "): SpatialExtent(poly, input) + + +class VerticalExtentTest(unittest.TestCase): + + def test_from_json(self): + """ + GIVEN a typical json_dict + WHEN from_json is called + THEN a VerticalExtent is returned + """ + expected = VerticalExtent([1.65]) + + json_dict = {'interval': ['ScalarBounds(lower=1.65, upper=1.65)'], + 'values': [1.65], + 'vrs': 'VERTCRS["WGS_1984",VDATUM["World Geodetic System 1984"],CS[vertical,1],AXIS["ellipsoidal height (h)",up,LENGTHUNIT["metre",1,ID["EPSG",9001]]]]', + 'name': 'WGS_1984'} + + actual = VerticalExtent.from_json(json_dict) + + self.assertEqual(actual, expected) + + def test_init_type_checking_values(self): + """ + GIVEN a non-list + WHEN passed to VerticalExtent.values + THEN a TypeError is returned + """ + heights = {"value": 4} + + with self.assertRaisesRegex(TypeError, "Expected List of values, received "): + VerticalExtent(values=heights) + + def test_init_type_checking_values_entry(self): + """ + GIVEN a list of floats with one bad entry + WHEN passed to VerticalExtent.values + THEN a TypeError is returned with value and type + """ + vals = list(np.arange(0, 1, 0.2)) + vals.append([1]) + + with self.assertRaisesRegex(TypeError, r"Expected all float or string values, received value '\[1\]' of type "): + VerticalExtent(values=vals) + + def test_init_type_checking_vrs(self): + """ + GIVEN a non-CrsObject input + WHEN passed to VerticalExtent + THEN a TypeError is returned + """ + vals = list(np.arange(0, 1, 0.2)) + input = "bad input" + + with self.assertRaisesRegex(TypeError, "Expected CrsObject, received "): + VerticalExtent(values=vals, vrs=input)