|
4 | 4 | This module provides functions to fetch, process, and read NOAA (National |
5 | 5 | Oceanic and Atmospheric Administration) current data directly from the |
6 | 6 | 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 |
8 | 8 | JSON formats, and writing data to a JSON file. |
9 | 9 |
|
10 | 10 | Functions: |
11 | 11 | ---------- |
12 | | -request_noaa_data(station, parameter, start_date, end_date, proxy=None, |
| 12 | +request_noaa_data(station, parameter, start_date, end_date, proxy=None, |
13 | 13 | 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, |
15 | 15 | with optional support for proxy settings and writing data to a JSON |
16 | 16 | file. |
17 | 17 |
|
@@ -56,16 +56,18 @@ def request_noaa_data( |
56 | 56 | Parameters |
57 | 57 | ---------- |
58 | 58 | station : str |
59 | | - NOAA current station number (e.g. 'cp0101') |
| 59 | + NOAA current station number (e.g. 'cp0101', "s08010", "9446484") |
60 | 60 | 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/ |
62 | 64 | start_date : str |
63 | 65 | Start date in the format yyyyMMdd |
64 | 66 | end_date : str |
65 | 67 | End date in the format yyyyMMdd |
66 | 68 | 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'} |
69 | 71 | write_json : str or None |
70 | 72 | Name of json file to write data |
71 | 73 | clear_cache : bool |
@@ -158,26 +160,42 @@ def request_noaa_data( |
158 | 160 | end_date = date_list[i + 1].strftime("%Y%m%d") |
159 | 161 |
|
160 | 162 | 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" |
161 | 166 | data_url = f"https://tidesandcurrents.noaa.gov/api/datagetter?{api_query}" |
162 | 167 |
|
163 | | - print("Data request URL: ", data_url) |
| 168 | + print(f"Data request URL: {data_url}\n") |
164 | 169 |
|
165 | 170 | # Get response |
166 | 171 | try: |
167 | 172 | response = requests.get(url=data_url, proxies=proxy) |
168 | 173 | 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 | + |
175 | 190 | # Convert to DataFrame and save in data_frames list |
176 | 191 | df, metadata = _xml_to_dataframe(response) |
177 | 192 | data_frames.append(df) |
178 | 193 |
|
179 | 194 | # 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.") |
181 | 199 |
|
182 | 200 | # Remove duplicated date values |
183 | 201 | data = data.loc[~data.index.duplicated()] |
@@ -236,7 +254,12 @@ def _xml_to_dataframe(response): |
236 | 254 | df.drop_duplicates(inplace=True) |
237 | 255 |
|
238 | 256 | # 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 |
240 | 263 |
|
241 | 264 | return df, metadata |
242 | 265 |
|
|
0 commit comments