Skip to content

Commit 41ba93a

Browse files
authored
Gridded files (#264)
* stream from from get_gridded_data() * stream for get_gridded_data * add optional file_path arg to get_gridded_data_from_api * implement get_gridded_file() running locally * support remote get_gridded_file() * update docstrings * gridded_file() for remote * deprecate get_gridded_files() with a warning * fix unit tests * change test to get_gridded_file() and pass from option when getting huc maps from internal function * update Docstring comments * fix error message when no network connection * add warning for deprecation
1 parent 8512020 commit 41ba93a

8 files changed

Lines changed: 556 additions & 196 deletions

File tree

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "hf_hydrodata"
3-
version = "1.4.6"
3+
version = "1.4.7"
44
description = "hydroframe tools and utilities"
55
authors = ["William M. Hasling", "Laura Condon", "Reed Maxwell", "George Artavanis", "Amy M. Johnson", "Amy C. Defnet"]
66
license = "MIT"

‎src/hf_hydrodata/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from hf_hydrodata.gridded import (
2424
get_gridded_data,
2525
get_gridded_files,
26+
get_gridded_file,
2627
get_raw_file,
2728
get_date_range,
2829
get_huc_from_latlon,

‎src/hf_hydrodata/data_model_access.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,13 @@ def _query_data_catalog(self, options: dict):
129129
response = requests.get(url, timeout=120, headers=headers)
130130
if response.status_code == 200:
131131
response_json = json.loads(response.text)
132+
elif response.status_code == 403:
133+
raise ValueError(
134+
f"Unable to connect to '{HYDRODATA_URL}' to get data catalog information."
135+
)
132136
else:
133137
raise ValueError(
134-
f"Unable to connect to '{HYDRODATA_URL}' code = '{response.status_code}' to get data catalog information."
138+
f"Error ({response.status_code}) from server while trying to get data catalog information."
135139
)
136140
return response_json
137141

@@ -229,7 +233,7 @@ def _get_api_headers(required=True) -> dict:
229233
response = requests.get(url_security, timeout=(10, 100))
230234
except:
231235
raise ValueError(
232-
f"Unable to authenticate with your email/pin with '{HYDRODATA_URL}' server."
236+
f"Unable to connect to the server '{HYDRODATA_URL}' server."
233237
)
234238
if not response.status_code == 200:
235239
if not required:

‎src/hf_hydrodata/gridded.py‎

Lines changed: 412 additions & 183 deletions
Large diffs are not rendered by default.

‎src/hf_hydrodata/netcdf_util.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def generate_netcdf_file(
142142

143143
# Add metadata attributes to the NC file
144144
for key, value in metadata.items():
145-
setattr(nc, key, value)
145+
setattr(nc, key, str(value))
146146

147147
nc.close()
148148

@@ -157,7 +157,9 @@ def create_time_variable(
157157

158158
if temporal_resolution in ["daily", "hourly", "monthly", "weekly"]:
159159
date_start_str = get_date_start(query_parameters)
160-
if ":" in date_start_str:
160+
if isinstance(date_start_str, datetime.datetime):
161+
date_start = date_start_str
162+
elif ":" in date_start_str:
161163
date_start = datetime.datetime.strptime(date_start_str, "%Y-%m-%d %H:%M:%S")
162164
else:
163165
date_start = datetime.datetime.strptime(date_start_str, "%Y-%m-%d")
@@ -176,6 +178,8 @@ def create_time_variable(
176178
delta = datetime.timedelta(weeks=1)
177179
elif temporal_resolution == "monthly":
178180
delta = dateutil.relativedelta.relativedelta(months=1)
181+
else:
182+
delta = datetime.timedelta(days=1)
179183
dt = date_start
180184
for _ in range(0, time_steps):
181185
dates.append(dt)

‎src/hf_hydrodata/point.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,10 @@ def _validate_user():
12741274
response = requests.get(url_security, headers=None, timeout=(60, 60))
12751275
if response.status_code == 502:
12761276
raise ValueError("Server is unavailable. Try again later.")
1277+
if response.status_code == 403:
1278+
raise ValueError(
1279+
f"Unable connect to '{HYDRODATA_URL}' server to validate user."
1280+
)
12771281
if not response.status_code == 200:
12781282
raise ValueError(
12791283
f"Unable to authenticate with your email/pin with '{HYDRODATA_URL}' server."

‎tests/hf_hydrodata/test_gridded.py‎

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1700,7 +1700,7 @@ def test_topographic_index(tmp_path):
17001700
"grid_bounds": bounds,
17011701
"variable": "topographic_index",
17021702
}
1703-
hf.get_gridded_files(options, filename_template="foo.nc")
1703+
hf.get_gridded_file("foo.nc", options)
17041704
ds = xr.open_dataset("foo.nc")
17051705
da = ds["topographic_index"]
17061706
assert da.shape == (5, 5)
@@ -2580,3 +2580,121 @@ def test_get_bounds_limits():
25802580
with pytest.raises(ValueError) as info:
25812581
hf.get_gridded_data(options)
25822582
assert "is outside the grid" in str(info.value)
2583+
2584+
2585+
def test_gridded_file_2wy():
2586+
"""Test downloading subset in 2 waters years using get_gridded_file."""
2587+
2588+
with tempfile.TemporaryDirectory() as tempdirname:
2589+
bounds = [1399, 1784, 1447, 1803]
2590+
date_start = "1990-09-28"
2591+
date_end = "1990-10-04"
2592+
options = {
2593+
"dataset": "CW3E",
2594+
"variable": "air_temp",
2595+
"grid_bounds": bounds,
2596+
"temporal_resolution": "daily",
2597+
"date_start": date_start,
2598+
"date_end": date_end,
2599+
}
2600+
2601+
# Download to 2 different files for each water year
2602+
file_path = tempdirname + "/" + "{dataset}_{variable}_{wy}.nc"
2603+
hf.get_gridded_file(file_path, options)
2604+
path = f"{tempdirname}/CW3E_air_temp_1990.nc"
2605+
if not os.path.exists(path):
2606+
raise ValueError(f"File not created: '{path}'")
2607+
ds = xr.open_dataset(path)
2608+
assert ds["time"].shape[0] == 3
2609+
assert str(ds["time"].values[0]).startswith("1990-09-28")
2610+
assert ds["x"].shape[0] == 48
2611+
assert ds["y"].shape[0] == 19
2612+
2613+
# Download to one file for both water years
2614+
file_path = tempdirname + "/" + "{dataset}_{variable}.nc"
2615+
hf.get_gridded_file(file_path, options)
2616+
path = f"{tempdirname}/CW3E_air_temp.nc"
2617+
if not os.path.exists(path):
2618+
raise ValueError(f"File not created: '{path}'")
2619+
ds = xr.open_dataset(path)
2620+
assert ds["time"].shape[0] == 6
2621+
assert str(ds["time"].values[0]).startswith("1990-09-28")
2622+
assert ds["x"].shape[0] == 48
2623+
assert ds["y"].shape[0] == 19
2624+
2625+
# Download as one tiff (this should be an error)
2626+
with pytest.raises(ValueError):
2627+
file_path = tempdirname + "/" + "{dataset}_{variable}.tif"
2628+
hf.get_gridded_file(file_path, options)
2629+
path = f"{tempdirname}/CW3E_air_temp.nc"
2630+
2631+
2632+
@pytest.mark.private_dataset
2633+
def test_gridded_file_wtd():
2634+
"""Test downloading subset of 30m WTD usign get_gridded_file."""
2635+
2636+
with tempfile.TemporaryDirectory() as tempdirname:
2637+
tempdirname = "."
2638+
bounds = [3000, 3000, 3010, 3020]
2639+
options = {
2640+
"dataset": "ma_2025",
2641+
"variable": "water_table_depth",
2642+
"grid_bounds": bounds,
2643+
"grid": "conus2_wtd",
2644+
}
2645+
2646+
# Download it as a tif
2647+
file_path = tempdirname + "/" + "{dataset}_{variable}.tif"
2648+
hf.get_gridded_file(file_path, options)
2649+
path = f"{tempdirname}/ma_2025_water_table_depth.tif"
2650+
if not os.path.exists(path):
2651+
raise ValueError(f"File not created: '{path}'")
2652+
ds = xr.open_dataset(path)
2653+
assert ds["x"].shape[0] == 10
2654+
assert ds["y"].shape[0] == 20
2655+
2656+
# Download it as netcdf
2657+
file_path = tempdirname + "/" + "{dataset}_{variable}.nc"
2658+
hf.get_gridded_file(file_path, options)
2659+
path = f"{tempdirname}/ma_2025_water_table_depth.nc"
2660+
if not os.path.exists(path):
2661+
raise ValueError(f"File not created: '{path}'")
2662+
ds = xr.open_dataset(path)
2663+
assert ds["x"].shape[0] == 10
2664+
assert ds["y"].shape[0] == 20
2665+
2666+
2667+
def test_gridded_file_pfb():
2668+
"""Test downloading subset in 2 waters years using get_gridded_file to pfb."""
2669+
2670+
with tempfile.TemporaryDirectory() as tempdirname:
2671+
bounds = [1399, 1784, 1447, 1803]
2672+
date_start = "1990-09-28"
2673+
date_end = "1990-10-04"
2674+
options = {
2675+
"dataset": "CW3E",
2676+
"variable": "air_temp",
2677+
"grid_bounds": bounds,
2678+
"temporal_resolution": "hourly",
2679+
"date_start": date_start,
2680+
"date_end": date_end,
2681+
}
2682+
2683+
# Download to 6 files for each day in separate water year directories
2684+
file_path = (
2685+
tempdirname
2686+
+ "/"
2687+
+ "WY{wy}/{dataset}_{variable}.{wy_start_24hr:06d}_to_{wy_end_24hr:06}.pfb"
2688+
)
2689+
hf.get_gridded_file(file_path, options)
2690+
path = f"{tempdirname}/WY1990/CW3E_air_temp.008689_to_008712.pfb"
2691+
if not os.path.exists(path):
2692+
raise ValueError(f"File not created: '{path}'")
2693+
data = hf.gridded.read_fast_pfb(path)
2694+
assert data.shape == (1, 24, 19, 48)
2695+
2696+
# Download to one file
2697+
file_path = tempdirname + "precip.pfb"
2698+
hf.get_gridded_file(file_path, options)
2699+
data = hf.gridded.read_fast_pfb(file_path)
2700+
assert data.shape == (1, 144, 19, 48)

‎tests/hf_hydrodata/test_netcdf_util.py‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def test_hourly():
9797
"start_time": "2006-01-01 00:00:00",
9898
"end_time": "2006-01-01 01:00:00",
9999
"file_type": "pfb",
100-
"huc_id": "14010002",
100+
"grid_bounds": [1000, 1000, 1020, 1010],
101101
"dataset": "CW3E",
102102
}
103103
with tempfile.TemporaryDirectory() as temp_dir:
@@ -109,13 +109,13 @@ def test_hourly():
109109
)
110110
ds = xr.open_dataset(file_name)
111111
assert os.path.exists(file_name)
112-
assert len(ds["x"].values) == 52
113-
assert len(ds["y"].values) == 76
112+
assert len(ds["x"].values) == 20
113+
assert len(ds["y"].values) == 10
114114
assert len(ds["time"].values) == 1
115-
assert len(ds.coords["x"]) == 52
116-
assert ds.coords["x"][0] == pytest.approx(-779500.3088117298)
115+
assert len(ds.coords["x"]) == 20
116+
assert ds.coords["x"].values[0] == pytest.approx(-1207500.3088117298)
117117
da = ds["air_temp"]
118-
assert da.shape == (1, 76, 52)
119-
assert da.values[0, 20, 20] == pytest.approx(273.6231)
118+
assert da.shape == (1, 10, 20)
119+
assert da.values[0, 5, 5] == pytest.approx(287.357452)
120120
assert ds.attrs["variable"] == "air_temp"
121121
assert ds.attrs["temporal_resolution"] == "hourly"

0 commit comments

Comments
 (0)