Skip to content

Commit f843cd1

Browse files
jmcvey3ssolson
authored andcommitted
Update NOAA Request function (#332)
Fixes #223
1 parent ecbdfae commit f843cd1

File tree

4 files changed

+47
-24
lines changed

4 files changed

+47
-24
lines changed

mhkit/river/graphics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def plot_velocity_duration_curve(V, F, label=None, ax=None):
118118
"""
119119
# Sort by F
120120
temp = xr.Dataset(data_vars={"V": V, "F": F})
121-
temp.sortby("F", ascending=False)
121+
temp = temp.sortby("F", ascending=False)
122122

123123
ax = _xy_plot(
124124
temp["V"],

mhkit/river/io/usgs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def request_usgs_data(
8282
station : str
8383
USGS station number (e.g. '08313000')
8484
parameter : str
85-
USGS paramter ID (e.g. '00060' for Discharge, cubic feet per second)
85+
USGS parameter ID (e.g. '00060' for Discharge, cubic feet per second)
8686
start_date : str
8787
Start date in the format 'YYYY-MM-DD' (e.g. '2018-01-01')
8888
end_date : str
@@ -91,8 +91,8 @@ def request_usgs_data(
9191
Data type, options include 'Daily' (return the mean daily value) and
9292
'Instantaneous'.
9393
proxy : dict or None
94-
To request data from behind a firewall, define a dictionary of proxy settings,
95-
for example {"http": 'localhost:8080'}
94+
To request data from behind a firewall, define a dictionary of proxy settings,
95+
for example {"http": 'localhost:8080'}
9696
write_json : str or None
9797
Name of json file to write data
9898
clear_cache : bool
@@ -106,7 +106,7 @@ def request_usgs_data(
106106
Data indexed by datetime with columns named according to the parameter's
107107
variable description
108108
"""
109-
if not data_type in ["Daily", "Instantaneous"]:
109+
if data_type not in ["Daily", "Instantaneous"]:
110110
raise ValueError(f"data_type must be Daily or Instantaneous. Got: {data_type}")
111111

112112
if not isinstance(to_pandas, bool):

mhkit/tidal/io/noaa.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
This module provides functions to fetch, process, and read NOAA (National
55
Oceanic and Atmospheric Administration) current data directly from the
66
NOAA Tides and Currents API (https://api.tidesandcurrents.noaa.gov/api/prod/). It
7-
supports loading data into a pandas DataFrame, handling data in XML and
7+
supports loading data into a pandas DataFrame, handling data in XML and
88
JSON formats, and writing data to a JSON file.
99
1010
Functions:
1111
----------
12-
request_noaa_data(station, parameter, start_date, end_date, proxy=None,
12+
request_noaa_data(station, parameter, start_date, end_date, proxy=None,
1313
write_json=None):
14-
Loads NOAA current data from the API into a pandas DataFrame,
14+
Loads NOAA current data from the API into a pandas DataFrame,
1515
with optional support for proxy settings and writing data to a JSON
1616
file.
1717
@@ -56,16 +56,18 @@ def request_noaa_data(
5656
Parameters
5757
----------
5858
station : str
59-
NOAA current station number (e.g. 'cp0101')
59+
NOAA current station number (e.g. 'cp0101', "s08010", "9446484")
6060
parameter : str
61-
NOAA paramter (e.g. '' for Discharge, cubic feet per second)
61+
NOAA parameter (e.g. "currents", "salinity", "water_level", "water_temperature",
62+
"air_temperature", "wind", "air_pressure")
63+
https://api.tidesandcurrents.noaa.gov/api/prod/
6264
start_date : str
6365
Start date in the format yyyyMMdd
6466
end_date : str
6567
End date in the format yyyyMMdd
6668
proxy : dict or None
67-
To request data from behind a firewall, define a dictionary of proxy
68-
settings, for example {"http": 'localhost:8080'}
69+
To request data from behind a firewall, define a dictionary of proxy
70+
settings, for example {"http": 'localhost:8080'}
6971
write_json : str or None
7072
Name of json file to write data
7173
clear_cache : bool
@@ -158,26 +160,42 @@ def request_noaa_data(
158160
end_date = date_list[i + 1].strftime("%Y%m%d")
159161

160162
api_query = f"begin_date={start_date}&end_date={end_date}&station={station}&product={parameter}&units=metric&time_zone=gmt&application=web_services&format=xml"
163+
# Add datum to water level inquiries
164+
if parameter == "water_level":
165+
api_query += "&datum=MLLW"
161166
data_url = f"https://tidesandcurrents.noaa.gov/api/datagetter?{api_query}"
162167

163-
print("Data request URL: ", data_url)
168+
print(f"Data request URL: {data_url}\n")
164169

165170
# Get response
166171
try:
167172
response = requests.get(url=data_url, proxies=proxy)
168173
response.raise_for_status()
169-
except requests.exceptions.HTTPError as err:
170-
print(f"HTTP error occurred: {err}")
171-
continue
172-
except requests.exceptions.RequestException as err:
173-
print(f"Error occurred: {err}")
174-
continue
174+
# Catch non-exception errors
175+
if "error" in response.content.decode():
176+
raise Exception(response.content.decode())
177+
except Exception as err:
178+
if err.__class__ == requests.exceptions.HTTPError:
179+
print(f"HTTP error occurred: {err}")
180+
print(f"Error message: {response.content.decode()}\n")
181+
continue
182+
elif err.__class__ == requests.exceptions.RequestException:
183+
print(f"Requests error occurred: {err}")
184+
print(f"Error message: {response.content.decode()}\n")
185+
continue
186+
else:
187+
print(f"Requests error occurred: {err}\n")
188+
continue
189+
175190
# Convert to DataFrame and save in data_frames list
176191
df, metadata = _xml_to_dataframe(response)
177192
data_frames.append(df)
178193

179194
# Concatenate all DataFrames
180-
data = pd.concat(data_frames, ignore_index=False)
195+
if data_frames:
196+
data = pd.concat(data_frames, ignore_index=False)
197+
else:
198+
raise ValueError("No data retrieved.")
181199

182200
# Remove duplicated date values
183201
data = data.loc[~data.index.duplicated()]
@@ -236,7 +254,12 @@ def _xml_to_dataframe(response):
236254
df.drop_duplicates(inplace=True)
237255

238256
# Convert data to float
239-
df[["d", "s"]] = df[["d", "s"]].apply(pd.to_numeric)
257+
cols = list(df.columns)
258+
for var in cols:
259+
try:
260+
df[var] = df[var].apply(pd.to_numeric)
261+
except ValueError:
262+
pass
240263

241264
return df, metadata
242265

mhkit/wave/io/ndbc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,10 +329,10 @@ def request_data(parameter, filenames, proxy=None, clear_cache=False, to_pandas=
329329
'cwind' : 'Continuous Winds Current Year Historical Data'
330330
331331
filenames: pandas Series, pandas DataFrame, xarray DataArray, or xarray Dataset
332-
Data filenames on https://www.ndbc.noaa.gov/data/historical/{parameter}/
332+
Data filenames on https://www.ndbc.noaa.gov/data/historical/{parameter}/
333333
334334
proxy: dict
335-
Proxy dict passed to python requests,
335+
Proxy dict passed to python requests,
336336
(e.g. proxy_dict= {"http": 'http:wwwproxy.yourProxy:80/'})
337337
338338
to_pandas: bool (optional)
@@ -631,7 +631,7 @@ def parameter_units(parameter=""):
631631
If no parameter is passed then an ordered dictionary of all NDBC
632632
parameterz specified unites is returned. If a parameter is specified
633633
then only the units associated with that parameter are returned.
634-
Note that many NDBC paramters report multiple measurements and in
634+
Note that many NDBC parameters report multiple measurements and in
635635
that case the returned dictionary will contain the NDBC measurement
636636
name and associated unit for all the measurements associated with
637637
the specified parameter. Optional parameter values are given below.

0 commit comments

Comments
 (0)