Skip to content

Commit f3bdd25

Browse files
committed
More flexible bounding box model
This is much more flexible, but classes that inherit BoundingBox as a field will still raise a validation error if BoundingBox is not initialized with a dict or model instance (e.g. `DatasetSearchParameters`). This is the most likely point that users would define and use a bounding box. Ideally, a user could pass in a list representing a BBOX at the `DatasetSearchParameters` level
1 parent 5ceb2f2 commit f3bdd25

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

src/nsidc/iceflow/data/models.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,55 @@ class BoundingBox(pydantic.BaseModel):
235235
upper_right_lon: float
236236
upper_right_lat: float
237237

238+
def __init__(self, *args, **kwargs):
239+
"""Accept either named args, one arg for each coord, or an iterable of
240+
`(lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat)`."""
241+
if args:
242+
if len(args) == 1 and isinstance(args[0], list | tuple):
243+
# The first arg should be treated like a tuple of
244+
# (lower_left_lon, lower_left_lat, upper_right_lon,
245+
# upper_right_lat)
246+
args = tuple(args[0])
247+
# Each arg should be treated as one of (lower_left_lon,
248+
# lower_left_lat, upper_right_lon, upper_right_lat).
249+
if len(args) != 4:
250+
raise ValueError(
251+
"Expected four values for bounding box:"
252+
" (lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat)"
253+
)
254+
# Set kwargs if args are given.
255+
kwargs = {
256+
"lower_left_lon": args[0],
257+
"lower_left_lat": args[1],
258+
"upper_right_lon": args[2],
259+
"upper_right_lat": args[3],
260+
}
261+
262+
# Initialize the model.
263+
super().__init__(**kwargs)
264+
265+
def __iter__(self):
266+
"""Return bounding box as a iter (list/tuple)."""
267+
return iter(
268+
(
269+
self.lower_left_lon,
270+
self.lower_left_lat,
271+
self.upper_right_lon,
272+
self.upper_right_lat,
273+
)
274+
)
275+
276+
def __getitem__(self, idx):
277+
if isinstance(idx, int):
278+
return list(self.__iter__())[idx]
279+
elif isinstance(idx, str):
280+
return getattr(self, idx)
281+
else:
282+
raise TypeError(
283+
"Getitem on BoundingBox must be int (e.g. 0)"
284+
" or str (e.g., 'lower_left_lon')."
285+
)
286+
238287

239288
ALL_DATASETS: list[Dataset] = [
240289
ILATM1BDataset(version="1"),

tests/unit/test_data_models.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pandera as pa
55
import pytest
66

7-
from nsidc.iceflow.data.models import IceflowDataFrame
7+
from nsidc.iceflow.data.models import BoundingBox, IceflowDataFrame
88

99
_mock_bad_df = pd.DataFrame(
1010
{
@@ -45,3 +45,55 @@ def _pa_check_out(df: pd.DataFrame) -> IceflowDataFrame:
4545
def test_pa_check_out():
4646
with pytest.raises(pa.errors.SchemaError):
4747
_pa_check_out(_mock_bad_df)
48+
49+
50+
def test_bounding_box():
51+
bbox_with_kwargs = BoundingBox(
52+
lower_left_lon=-103.125559,
53+
lower_left_lat=-75.180563,
54+
upper_right_lon=-102.677327,
55+
upper_right_lat=-74.798063,
56+
)
57+
58+
bbox_with_args = BoundingBox(
59+
-103.125559,
60+
-75.180563,
61+
-102.677327,
62+
-74.798063,
63+
)
64+
65+
bbox_with_list = BoundingBox(
66+
[
67+
-103.125559,
68+
-75.180563,
69+
-102.677327,
70+
-74.798063,
71+
]
72+
)
73+
74+
assert bbox_with_kwargs == bbox_with_args
75+
assert bbox_with_kwargs == bbox_with_list
76+
77+
bbox = bbox_with_kwargs
78+
79+
# Access via named values
80+
assert bbox.lower_left_lon == -103.125559
81+
assert bbox.lower_left_lat == -75.180563
82+
assert bbox.upper_right_lon == -102.677327
83+
assert bbox.upper_right_lat == -74.798063
84+
85+
# Access as a list/tuple
86+
assert list(bbox) == [-103.125559, -75.180563, -102.677327, -74.798063]
87+
assert tuple(bbox) == (-103.125559, -75.180563, -102.677327, -74.798063)
88+
89+
# Access via index
90+
assert bbox[0] == -103.125559
91+
assert bbox[1] == -75.180563
92+
assert bbox[2] == -102.677327
93+
assert bbox[3] == -74.798063
94+
95+
# Access via name
96+
assert bbox["lower_left_lon"] == -103.125559
97+
assert bbox["lower_left_lat"] == -75.180563
98+
assert bbox["upper_right_lon"] == -102.677327
99+
assert bbox["upper_right_lat"] == -74.798063

0 commit comments

Comments
 (0)