Skip to content

Commit 2ac7055

Browse files
committed
Simplify "bounding box" construct
I was pretty aggressive with using pydantic everywhere with this, but public-facing api should avoid needing imports of models just to pass params to functions. This is also consistent with how `icepyx` and `earthaccess` handle bounding boxes, which we should strive for.
1 parent 54f4fcc commit 2ac7055

File tree

10 files changed

+109
-217
lines changed

10 files changed

+109
-217
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
Generate an Elevation Timeseries" notebook.
88
- Filter for cloud-hosted data, avoiding duplicate granule results from
99
`fetch.find_iceflow_data`
10+
- Make "bounding box" construct consistent with `icepyx` and `earthaccess`. This
11+
simplifies the api, and means the user no longer needs to import `BoundingBox`
12+
to query for data.
1013

1114
# v0.3.0
1215

docs/getting-started.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,13 @@ To find `iceflow`-supported data for an area of interest and timeframe, use
2525
```
2626
import datetime as dt
2727
28-
from nsidc.iceflow import (
29-
find_iceflow_data,
30-
DatasetSearchParameters,
31-
BoundingBox,
32-
)
28+
from nsidc.iceflow import find_iceflow_data
3329
3430
3531
search_results = find_iceflow_data(
36-
dataset_search_params=DatasetSearchParameters(
37-
bounding_box=BoundingBox(lower_left_lon=-103.125559, lower_left_lat=-75.180563, upper_right_lon=-102.677327, upper_right_lat=-74.798063),
38-
temporal=(dt.date(2009, 11, 1), dt.date(2009, 12, 31)),
39-
),
32+
# Lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat
33+
bounding_box=(-103.125559, -75.180563, -102.677327, -74.798063),
34+
temporal=(dt.date(2009, 11, 1), dt.date(2009, 12, 31)),
4035
)
4136
```
4237

docs/notebooks/iceflow-example.ipynb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
"import matplotlib.pyplot as plt\n",
4444
"\n",
4545
"from nsidc.iceflow import (\n",
46-
" BoundingBox,\n",
4746
" DatasetSearchParameters,\n",
4847
" ILATM1BDataset,\n",
4948
" download_iceflow_results,\n",
@@ -84,11 +83,11 @@
8483
"# Define the dataset that we want to search for.\n",
8584
"atm1b_v1_dataset = ILATM1BDataset(version=\"1\")\n",
8685
"# Define a bounding box for our area of interest.\n",
87-
"BBOX = BoundingBox(\n",
88-
" lower_left_lon=-103.125559,\n",
89-
" lower_left_lat=-75.180563,\n",
90-
" upper_right_lon=-102.677327,\n",
91-
" upper_right_lat=-74.798063,\n",
86+
"BBOX = (\n",
87+
" -103.125559,\n",
88+
" -75.180563,\n",
89+
" -102.677327,\n",
90+
" -74.798063,\n",
9291
")\n",
9392
"\n",
9493
"# We will define a short date range in 2009 to search for data.\n",

docs/notebooks/iceflow-with-icepyx.ipynb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,6 @@
763763
"import xarray as xr\n",
764764
"\n",
765765
"from nsidc.iceflow import (\n",
766-
" BoundingBox,\n",
767766
" DatasetSearchParameters,\n",
768767
" IceflowDataFrame,\n",
769768
" download_iceflow_results,\n",
@@ -804,11 +803,11 @@
804803
"ICESAT2_ITRF = \"ITRF2014\"\n",
805804
"\n",
806805
"# This bounding box covers an area near Sermeq Kujalleq (Jakobshavn Isbrae)\n",
807-
"BBOX = BoundingBox(\n",
808-
" lower_left_lon=-49.149,\n",
809-
" lower_left_lat=69.186,\n",
810-
" upper_right_lon=-48.949,\n",
811-
" upper_right_lat=69.238,\n",
806+
"BBOX = (\n",
807+
" -49.149,\n",
808+
" 69.186,\n",
809+
" -48.949,\n",
810+
" 69.238,\n",
812811
")\n",
813812
"\n",
814813
"# Range of dates we want to evaluate\n",

src/nsidc/iceflow/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from nsidc.iceflow.data.models import (
2323
ALL_DATASETS,
2424
BLATM1BDataset,
25-
BoundingBox,
2625
DatasetSearchParameters,
2726
GLAH06Dataset,
2827
IceflowDataFrame,
@@ -38,7 +37,6 @@
3837
__all__ = [
3938
"ALL_DATASETS",
4039
"BLATM1BDataset",
41-
"BoundingBox",
4240
"DatasetSearchParameters",
4341
"GLAH06Dataset",
4442
"ILATM1BDataset",

src/nsidc/iceflow/api.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,20 @@
1111
from nsidc.iceflow.data.fetch import download_iceflow_results, find_iceflow_data
1212
from nsidc.iceflow.data.models import (
1313
ALL_DATASETS,
14-
DatasetSearchParameters,
14+
BoundingBoxLike,
15+
Dataset,
1516
IceflowDataFrame,
17+
TemporalRange,
1618
)
1719
from nsidc.iceflow.data.read import read_iceflow_datafiles
1820
from nsidc.iceflow.itrf.converter import transform_itrf
1921

2022

2123
def fetch_iceflow_df(
2224
*,
23-
dataset_search_params: DatasetSearchParameters,
25+
bounding_box: BoundingBoxLike,
26+
temporal: TemporalRange,
27+
datasets: list[Dataset] = ALL_DATASETS,
2428
output_dir: Path,
2529
# TODO: also add option for target epoch!!
2630
output_itrf: str | None = None,
@@ -38,7 +42,9 @@ def fetch_iceflow_df(
3842
"""
3943

4044
iceflow_search_reuslts = find_iceflow_data(
41-
dataset_search_params=dataset_search_params,
45+
bounding_box=bounding_box,
46+
temporal=temporal,
47+
datasets=datasets,
4248
)
4349

4450
downloaded_files = download_iceflow_results(

src/nsidc/iceflow/data/fetch.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,19 @@
77
from loguru import logger
88

99
from nsidc.iceflow.data.models import (
10-
BoundingBox,
10+
ALL_DATASETS,
11+
BoundingBoxLike,
1112
Dataset,
12-
DatasetSearchParameters,
1313
IceflowSearchResult,
1414
IceflowSearchResults,
15+
TemporalRange,
1516
)
1617

1718

1819
def _find_iceflow_data_for_dataset(
1920
*,
2021
dataset: Dataset,
21-
bounding_box: BoundingBox,
22+
bounding_box: BoundingBoxLike,
2223
temporal: tuple[dt.datetime | dt.date, dt.datetime | dt.date],
2324
) -> IceflowSearchResult:
2425
earthaccess.login()
@@ -35,12 +36,7 @@ def _find_iceflow_data_for_dataset(
3536
# non-cloud, we may get duplicate granules as long as the ECS copy
3637
# remains.
3738
cloud_hosted=True,
38-
bounding_box=(
39-
bounding_box.lower_left_lon,
40-
bounding_box.lower_left_lat,
41-
bounding_box.upper_right_lon,
42-
bounding_box.upper_right_lat,
43-
),
39+
bounding_box=bounding_box,
4440
temporal=temporal,
4541
)
4642
except IndexError:
@@ -88,14 +84,17 @@ def _download_iceflow_search_result(
8884

8985
def find_iceflow_data(
9086
*,
91-
dataset_search_params: DatasetSearchParameters,
87+
bounding_box: BoundingBoxLike,
88+
temporal: TemporalRange,
89+
datasets: list[Dataset] = ALL_DATASETS,
9290
) -> IceflowSearchResults:
91+
"""Find iceflow-compatible data using spatial and temporal constraints."""
9392
iceflow_search_results = []
94-
for dataset in dataset_search_params.datasets:
93+
for dataset in datasets:
9594
iceflow_search_result = _find_iceflow_data_for_dataset(
9695
dataset=dataset,
97-
bounding_box=dataset_search_params.bounding_box,
98-
temporal=dataset_search_params.temporal,
96+
bounding_box=bounding_box,
97+
temporal=temporal,
9998
)
10099
iceflow_search_results.append(iceflow_search_result)
101100

src/nsidc/iceflow/data/models.py

Lines changed: 9 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -229,60 +229,10 @@ class GLAH06Dataset(Dataset):
229229
version: Literal["034"] = "034"
230230

231231

232-
class BoundingBox(pydantic.BaseModel):
233-
lower_left_lon: float
234-
lower_left_lat: float
235-
upper_right_lon: float
236-
upper_right_lat: float
237-
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-
)
232+
# This mirrors the bounding box construct in `earthaccess` and `icepyx`: a
233+
# list/float of len 4:
234+
# (lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat)
235+
BoundingBoxLike = list[float] | tuple[float, float, float, float]
286236

287237

288238
ALL_DATASETS: list[Dataset] = [
@@ -295,10 +245,13 @@ def __getitem__(self, idx):
295245
]
296246

297247

248+
TemporalRange = tuple[dt.datetime | dt.date, dt.datetime | dt.date]
249+
250+
298251
class DatasetSearchParameters(pydantic.BaseModel):
299252
datasets: list[Dataset] = ALL_DATASETS
300-
bounding_box: BoundingBox
301-
temporal: tuple[dt.datetime | dt.date, dt.datetime | dt.date]
253+
bounding_box: BoundingBoxLike
254+
temporal: TemporalRange
302255

303256

304257
class IceflowSearchResult(pydantic.BaseModel):

0 commit comments

Comments
 (0)