From 2859d595f3a5e3d840ded4fed50b1adf060f7cc6 Mon Sep 17 00:00:00 2001 From: Chris Jellen Date: Thu, 19 Dec 2024 18:21:19 -0800 Subject: [PATCH 1/6] updates to API surface and flags --- ndbc_api/ndbc_api.py | 68 +++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/ndbc_api/ndbc_api.py b/ndbc_api/ndbc_api.py index 8720852..dee2d3d 100644 --- a/ndbc_api/ndbc_api.py +++ b/ndbc_api/ndbc_api.py @@ -30,7 +30,7 @@ import warnings from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime, timedelta -from typing import Any, List, Sequence, Tuple, Union, Dict +from typing import Any, List, Sequence, Tuple, Union, Dict, Optional import xarray import pandas as pd @@ -382,8 +382,10 @@ def station(self, def available_realtime(self, station_id: Union[str, int], - as_df: bool = False) -> Union[pd.DataFrame, dict]: - """Get the available realtime measurements for a station. + as_df: bool = False, + full_response: bool = False, + ) -> Union[List[str], pd.DataFrame, dict]: + """Get the available realtime modalities for a station. While most data buoy (station) measurements are available over multi-year time ranges, some measurements depreciate or become @@ -394,6 +396,10 @@ def available_realtime(self, Args: station_id: The NDBC station ID (e.g. `'tplm2'` or `41001`) for the station of interest. + full_response: Whether to return the full response from the NDBC + API, defaults to `False` and a list of modes from `get_modes()` + is returned. If `True`, the full URL for each data mode is + included in the returned `dict` or `pandas.DataFrame`. as_df: Whether to return station-level data as a `pandas.DataFrame`, defaults to `False`, and a `dict` is returned. @@ -408,9 +414,23 @@ def available_realtime(self, """ station_id = self._parse_station_id(station_id) try: - data = self._stations_api.realtime(handler=self._handler, + full_response = self._stations_api.realtime(handler=self._handler, station_id=station_id) - return self._handle_data(data, as_df, cols=None) + full_data = {} + if full_response: + full_data = self._handle_data(full_response, as_df, cols=None) + return full_data + else: + full_data = self._handle_data(full_response, as_df=False, cols=None) + + # Parse the modes from the full response + _modes = self.get_modes() + station_modes = set() + for k in full_data: + for m in _modes: + if m in full_data[k]['description']: + station_modes.add(m) + return list(station_modes) except (ResponseException, ValueError, KeyError) as e: raise ResponseException('Failed to handle returned data.') from e @@ -456,7 +476,8 @@ def get_data( cols: List[str] = None, station_ids: Union[Sequence[Union[int, str]], None] = None, modes: Union[List[str], None] = None, - use_opendap: bool = False, + as_xarray_dataset: bool = False, + use_opendap: Optional[bool] = False, ) -> Union[pd.DataFrame, xarray.Dataset, dict]: """Execute data query against the specified NDBC station(s). @@ -487,10 +508,14 @@ def get_data( service column headers as a timestamp, and to use this timestamp as the index. as_df: Whether to return station-level data as a `pandas.DataFrame`, - defaults to `False`, and a `dict` is returned. + defaults to `True`, if `False` a `dict` is returned unless + `as_xarray_dataset` is set to `True`. + as_xarray_dataset: Whether to return tbe data as an `xarray.Dataset`, + defaults to `False`. cols: A list of columns of interest which are selected from the available data columns, such that only the desired columns are returned. All columns are returned if `None` is specified. + use_opendap: An alias for `as_xarray_dataset`. Returns: The available station(s) measurements for the specified modes, time @@ -507,6 +532,9 @@ def get_data( HandlerException: There was an error in handling the returned data as a `dict` or `pandas.DataFrame`. """ + if use_opendap is not None: + as_xarray_dataset = use_opendap + self.log(logging.DEBUG, message=f"`get_data` called with arguments: {locals()}") if station_id is None and station_ids is None: @@ -532,7 +560,7 @@ def get_data( handle_modes.extend(modes) for mode in handle_modes: - if mode not in self.get_modes(use_opendap): + if mode not in self.get_modes(use_opendap=as_xarray_dataset): raise RequestException(f"Mode {mode} is not available.") self.log(logging.INFO, @@ -559,7 +587,7 @@ def get_data( use_timestamp=use_timestamp, as_df=as_df, cols=cols, - use_opendap=use_opendap, + use_opendap=as_xarray_dataset, ) for future in as_completed(station_futures.values()): @@ -585,16 +613,20 @@ def get_data( return self._handle_accumulate_data(accumulated_data) - def get_modes(self, use_opendap: bool = False) -> List[str]: + def get_modes(self, use_opendap: bool = False, as_xarray_dataset: Optional[bool] = None) -> List[str]: """Get the list of supported modes for `get_data(...)`. Args: use_opendap (bool): Whether to return the available - modes for opendap (NetCDF) data. - + modes for opendap `xarray.Dataset` data. + as_xarray_dataset (bool): An alias for `use_opendap`. + Returns: (List[str]) the available modalities. """ + if as_xarray_dataset is not None: + use_opendap = as_xarray_dataset + if use_opendap: return [ v for v in vars(self._opendap_data_api) if not v.startswith('_') @@ -602,16 +634,20 @@ def get_modes(self, use_opendap: bool = False) -> List[str]: return [v for v in vars(self._data_api) if not v.startswith('_')] @staticmethod - def save_netcdf_dataset(dataset: xarray.Dataset, output_filepath: str): + def save_xarray_dataset(dataset: xarray.Dataset, output_filepath: str, **kwargs) -> None: """ - Saves a netCDF4 dataset from a temporary file to a user-specified file path. + Saves an `xarray.Dataset` to netCDF a user-specified file path. Args: dataset: The xarray dataset to save. output_filepath: The path to save the dataset to. + **kwargs: Additional keyword arguments to pass to `dataset.to_netcdf`. + + Returns: + None: The dataset is written to disk """ - dataset.to_netcdf(output_filepath) - + dataset.to_netcdf(output_filepath, **kwargs) + """ PRIVATE """ def _get_request_handler( From a2fe48e19ddd4c2de60dbe5fd9aec364695e3c8d Mon Sep 17 00:00:00 2001 From: Chris Jellen Date: Thu, 19 Dec 2024 18:41:06 -0800 Subject: [PATCH 2/6] JOSS suggested improvements --- ndbc_api/ndbc_api.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ndbc_api/ndbc_api.py b/ndbc_api/ndbc_api.py index dee2d3d..a0fa118 100644 --- a/ndbc_api/ndbc_api.py +++ b/ndbc_api/ndbc_api.py @@ -382,8 +382,8 @@ def station(self, def available_realtime(self, station_id: Union[str, int], - as_df: bool = False, full_response: bool = False, + as_df: Optional[bool] = None, ) -> Union[List[str], pd.DataFrame, dict]: """Get the available realtime modalities for a station. @@ -414,14 +414,16 @@ def available_realtime(self, """ station_id = self._parse_station_id(station_id) try: - full_response = self._stations_api.realtime(handler=self._handler, + station_realtime = self._stations_api.realtime(handler=self._handler, station_id=station_id) full_data = {} if full_response: - full_data = self._handle_data(full_response, as_df, cols=None) + if as_df is None: + as_df = False + full_data = self._handle_data(station_realtime, as_df, cols=None) return full_data else: - full_data = self._handle_data(full_response, as_df=False, cols=None) + full_data = self._handle_data(station_realtime, as_df=False, cols=None) # Parse the modes from the full response _modes = self.get_modes() @@ -477,7 +479,7 @@ def get_data( station_ids: Union[Sequence[Union[int, str]], None] = None, modes: Union[List[str], None] = None, as_xarray_dataset: bool = False, - use_opendap: Optional[bool] = False, + use_opendap: Optional[bool] = None, ) -> Union[pd.DataFrame, xarray.Dataset, dict]: """Execute data query against the specified NDBC station(s). @@ -535,6 +537,8 @@ def get_data( if use_opendap is not None: as_xarray_dataset = use_opendap + as_df = as_df and not as_xarray_dataset + self.log(logging.DEBUG, message=f"`get_data` called with arguments: {locals()}") if station_id is None and station_ids is None: From 8198727950b1e77b12059908f5642303fe66bab6 Mon Sep 17 00:00:00 2001 From: Chris Jellen Date: Thu, 19 Dec 2024 18:41:13 -0800 Subject: [PATCH 3/6] update documentation --- notebooks/overview.ipynb | 1120 ++++++++++++++------------------------ 1 file changed, 416 insertions(+), 704 deletions(-) diff --git a/notebooks/overview.ipynb b/notebooks/overview.ipynb index 45c4fc5..23c6ea1 100644 --- a/notebooks/overview.ipynb +++ b/notebooks/overview.ipynb @@ -192,7 +192,7 @@ " Prediction and Research Moored Array in the At...\n", " International Partners\n", " buoy\n", - " False\n", + " True\n", " False\n", " False\n", " False\n", @@ -222,7 +222,7 @@ " Prediction and Research Moored Array in the At...\n", " International Partners\n", " buoy\n", - " True\n", + " False\n", " False\n", " False\n", " False\n", @@ -243,7 +243,7 @@ " ...\n", " \n", " \n", - " 1339\n", + " 1340\n", " yata2\n", " 59.548\n", " -139.733\n", @@ -258,7 +258,7 @@ " False\n", " \n", " \n", - " 1340\n", + " 1341\n", " ygnn6\n", " 43.262\n", " -79.064\n", @@ -273,7 +273,7 @@ " False\n", " \n", " \n", - " 1341\n", + " 1342\n", " yktv2\n", " 37.227\n", " -76.479\n", @@ -288,7 +288,7 @@ " False\n", " \n", " \n", - " 1342\n", + " 1343\n", " yrsv2\n", " 37.414\n", " -76.712\n", @@ -303,7 +303,7 @@ " False\n", " \n", " \n", - " 1343\n", + " 1344\n", " zbqn7\n", " 33.955\n", " -77.935\n", @@ -319,7 +319,7 @@ " \n", " \n", "\n", - "

1344 rows × 12 columns

\n", + "

1345 rows × 12 columns

\n", "" ], "text/plain": [ @@ -330,11 +330,11 @@ "3 13008 15.000 -38.000 0.0 \n", "4 13009 8.000 -38.000 0.0 \n", "... ... ... ... ... \n", - "1339 yata2 59.548 -139.733 \n", - "1340 ygnn6 43.262 -79.064 73.0 \n", - "1341 yktv2 37.227 -76.479 3.7 \n", - "1342 yrsv2 37.414 -76.712 11.0 \n", - "1343 zbqn7 33.955 -77.935 \n", + "1340 yata2 59.548 -139.733 \n", + "1341 ygnn6 43.262 -79.064 73.0 \n", + "1342 yktv2 37.227 -76.479 3.7 \n", + "1343 yrsv2 37.414 -76.712 11.0 \n", + "1344 zbqn7 33.955 -77.935 \n", "\n", " Name \\\n", "0 Sturgeon Bay CG Station, WI \n", @@ -343,11 +343,11 @@ "3 Reggae \n", "4 Lambada \n", "... ... \n", - "1339 9453220 - Yakutat, Yakutat Bay, AK \n", - "1340 Niagara Coast Guard Station, NY \n", - "1341 8637689 - Yorktown USCG Training Center, VA \n", - "1342 Taskinas Creek, Chesapeake Bay Reserve, VA \n", - "1343 Zeke's Basin, North Carolina \n", + "1340 9453220 - Yakutat, Yakutat Bay, AK \n", + "1341 Niagara Coast Guard Station, NY \n", + "1342 8637689 - Yorktown USCG Training Center, VA \n", + "1343 Taskinas Creek, Chesapeake Bay Reserve, VA \n", + "1344 Zeke's Basin, North Carolina \n", "\n", " Owner \\\n", "0 U.S.C.G. Marine Reporting Stations \n", @@ -356,24 +356,24 @@ "3 Prediction and Research Moored Array in the At... \n", "4 Prediction and Research Moored Array in the At... \n", "... ... \n", - "1339 NOS \n", - "1340 NWS Eastern Region \n", - "1341 NOS \n", - "1342 National Estuarine Research Reserve System \n", + "1340 NOS \n", + "1341 NWS Eastern Region \n", + "1342 NOS \n", "1343 National Estuarine Research Reserve System \n", + "1344 National Estuarine Research Reserve System \n", "\n", " Program Type Includes Meteorology Includes Currents \\\n", "0 IOOS Partners fixed False False \n", "1 International Partners buoy True False \n", - "2 International Partners buoy False False \n", + "2 International Partners buoy True False \n", "3 International Partners buoy True False \n", - "4 International Partners buoy True False \n", + "4 International Partners buoy False False \n", "... ... ... ... ... \n", - "1339 NOS/CO-OPS fixed True False \n", - "1340 IOOS Partners fixed True False \n", - "1341 NOS/CO-OPS fixed True False \n", - "1342 NERRS fixed True False \n", - "1343 NERRS fixed False False \n", + "1340 NOS/CO-OPS fixed True False \n", + "1341 IOOS Partners fixed True False \n", + "1342 NOS/CO-OPS fixed True False \n", + "1343 NERRS fixed True False \n", + "1344 NERRS fixed False False \n", "\n", " Includes Water Quality DART Program \n", "0 False False \n", @@ -382,13 +382,13 @@ "3 False False \n", "4 False False \n", "... ... ... \n", - "1339 False False \n", "1340 False False \n", "1341 False False \n", "1342 False False \n", "1343 False False \n", + "1344 False False \n", "\n", - "[1344 rows x 12 columns]" + "[1345 rows x 12 columns]" ] }, "execution_count": 5, @@ -487,9 +487,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "['adcp', 'cwind', 'ocean', 'pwind', 'stdmet', 'swden', 'wlevel']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "api.get_modes(use_opendap=True)" ] @@ -539,7 +550,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -548,8 +559,9 @@ "'tplm2'" ] }, + "execution_count": 9, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ @@ -565,7 +577,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -606,7 +618,7 @@ " \n", " \n", " \n", - " 1279\n", + " 1280\n", " tplm2\n", " 38.899\n", " -76.436\n", @@ -631,14 +643,14 @@ " Chesapeake Bay Interpretive Buoy System (CBIBS)\n", " IOOS Partners\n", " buoy\n", - " True\n", - " True\n", - " True\n", + " False\n", + " False\n", + " False\n", " False\n", " 9.369909\n", " \n", " \n", - " 622\n", + " 621\n", " apam2\n", " 38.983\n", " -76.479\n", @@ -654,7 +666,7 @@ " 12.225590\n", " \n", " \n", - " 729\n", + " 730\n", " cpvm2\n", " 38.995\n", " -76.388\n", @@ -670,7 +682,7 @@ " 13.307985\n", " \n", " \n", - " 678\n", + " 676\n", " bslm2\n", " 38.781\n", " -76.708\n", @@ -702,7 +714,7 @@ " ...\n", " \n", " \n", - " 693\n", + " 692\n", " cblo1\n", " 41.981\n", " -80.556\n", @@ -727,7 +739,7 @@ " Environment and Climate Change Canada\n", " International Partners\n", " buoy\n", - " True\n", + " False\n", " False\n", " False\n", " False\n", @@ -750,7 +762,7 @@ " 495.825675\n", " \n", " \n", - " 1178\n", + " 1179\n", " rprn6\n", " 43.263\n", " -77.598\n", @@ -766,7 +778,7 @@ " 497.635930\n", " \n", " \n", - " 1167\n", + " 1168\n", " rcrn6\n", " 43.269\n", " -77.626\n", @@ -783,79 +795,79 @@ " \n", " \n", "\n", - "

126 rows × 13 columns

\n", + "

125 rows × 13 columns

\n", "" ], "text/plain": [ " Station Lat Lon Elevation \\\n", - "1279 tplm2 38.899 -76.436 0.0 \n", + "1280 tplm2 38.899 -76.436 0.0 \n", "213 44063 38.963 -76.448 0.0 \n", - "622 apam2 38.983 -76.479 1.4 \n", - "729 cpvm2 38.995 -76.388 3.4 \n", - "678 bslm2 38.781 -76.708 0.3 \n", + "621 apam2 38.983 -76.479 1.4 \n", + "730 cpvm2 38.995 -76.388 3.4 \n", + "676 bslm2 38.781 -76.708 0.3 \n", "... ... ... ... ... \n", - "693 cblo1 41.981 -80.556 177.0 \n", + "692 cblo1 41.981 -80.556 177.0 \n", "270 45142 42.740 -79.290 174.0 \n", "239 44402 39.314 -70.717 0.0 \n", - "1178 rprn6 43.263 -77.598 75.0 \n", - "1167 rcrn6 43.269 -77.626 76.7 \n", + "1179 rprn6 43.263 -77.598 75.0 \n", + "1168 rcrn6 43.269 -77.626 76.7 \n", "\n", " Name \\\n", - "1279 Thomas Point, MD \n", + "1280 Thomas Point, MD \n", "213 Annapolis, MD \n", - "622 8575512 - Annapolis, MD \n", - "729 8575437 - Chesapeake Bay Bridge Visibility, MD \n", - "678 Jug Bay, Chesapeake Bay Reserve, MD \n", + "621 8575512 - Annapolis, MD \n", + "730 8575437 - Chesapeake Bay Bridge Visibility, MD \n", + "676 Jug Bay, Chesapeake Bay Reserve, MD \n", "... ... \n", - "693 Conneaut Breakwater Light, OH \n", + "692 Conneaut Breakwater Light, OH \n", "270 Port Colborne \n", "239 SOUTHEAST BLOCK CANYON - 130 NM SE of Fire Isl... \n", - "1178 Rochester Coast Guard, NY \n", - "1167 9052058 - Rochester, NY \n", + "1179 Rochester Coast Guard, NY \n", + "1168 9052058 - Rochester, NY \n", "\n", " Owner \\\n", - "1279 NDBC \n", + "1280 NDBC \n", "213 Chesapeake Bay Interpretive Buoy System (CBIBS) \n", - "622 NOS \n", - "729 NOAA NOS PORTS \n", - "678 National Estuarine Research Reserve System \n", + "621 NOS \n", + "730 NOAA NOS PORTS \n", + "676 National Estuarine Research Reserve System \n", "... ... \n", - "693 NWS Eastern Region \n", + "692 NWS Eastern Region \n", "270 Environment and Climate Change Canada \n", "239 NDBC \n", - "1178 NWS Eastern Region \n", - "1167 NOS \n", + "1179 NWS Eastern Region \n", + "1168 NOS \n", "\n", " Program Type Includes Meteorology \\\n", - "1279 NDBC Meteorological/Ocean fixed True \n", - "213 IOOS Partners buoy True \n", - "622 NOS/CO-OPS fixed True \n", - "729 NOS/CO-OPS fixed True \n", - "678 NERRS fixed True \n", + "1280 NDBC Meteorological/Ocean fixed True \n", + "213 IOOS Partners buoy False \n", + "621 NOS/CO-OPS fixed True \n", + "730 NOS/CO-OPS fixed True \n", + "676 NERRS fixed True \n", "... ... ... ... \n", - "693 IOOS Partners fixed True \n", - "270 International Partners buoy True \n", + "692 IOOS Partners fixed True \n", + "270 International Partners buoy False \n", "239 Tsunami dart False \n", - "1178 IOOS Partners fixed True \n", - "1167 NOS/CO-OPS fixed True \n", + "1179 IOOS Partners fixed True \n", + "1168 NOS/CO-OPS fixed True \n", "\n", " Includes Currents Includes Water Quality DART Program distance \n", - "1279 False False False 2.177979 \n", - "213 True True False 9.369909 \n", - "622 False False False 12.225590 \n", - "729 False False False 13.307985 \n", - "678 False False False 26.506690 \n", + "1280 False False False 2.177979 \n", + "213 False False False 9.369909 \n", + "621 False False False 12.225590 \n", + "730 False False False 13.307985 \n", + "676 False False False 26.506690 \n", "... ... ... ... ... \n", - "693 False False False 491.192873 \n", + "692 False False False 491.192873 \n", "270 False False False 492.561301 \n", "239 False False True 495.825675 \n", - "1178 False False False 497.635930 \n", - "1167 False False False 498.756609 \n", + "1179 False False False 497.635930 \n", + "1168 False False False 498.756609 \n", "\n", - "[126 rows x 13 columns]" + "[125 rows x 13 columns]" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -1090,7 +1102,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -1112,7 +1124,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -1248,7 +1260,7 @@ "TIDE NaN NaN NaN" ] }, - "execution_count": 18, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -1259,7 +1271,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -1309,7 +1321,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -1642,7 +1654,7 @@ " Timestamp('2020-01-02 00:00:00'): nan}}]}" ] }, - "execution_count": 20, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -1666,7 +1678,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -1692,7 +1704,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -1821,7 +1833,7 @@ "TIDE NaN NaN NaN" ] }, - "execution_count": 22, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -1839,7 +1851,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -32878,7 +32890,7 @@ " ...}}]}" ] }, - "execution_count": 24, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -32906,628 +32918,305 @@ "source": [ "The NDBC API now supports retrieving data across stations and modes in the native `DODS NetCDF4` [format](https://dods.ndbc.noaa.gov/) provided by [THREDDS](https://dods.ndbc.noaa.gov/thredds/catalog/data/catalog.html). This data is retrieved from the DODS server through HTTPS, with the resulting data matching the quality-controlled records as closely as possible.\n", "\n", - "Data retrieval through THREDDS is controlled using the `use_opendap` argument (defaults to `False`).\n", + "Data retrieval through THREDDS is controlled using the `as_xarray_dataset` argument (defaults to `False`).\n", "\n", "As with the standard usage of the `get_data` method, the `start_time`, `end_time`, and `cols` arguments can be used to efficiently filter data during retrieval." ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "ds = api.get_data(station_ids=[\"tplm2\"], modes=[\"stdmet\"], start_time=\"2019-06-01\", end_time=\"2024-06-01\", use_opendap=True)" + "ds = api.get_data(station_ids=[\"tplm2\"], modes=[\"stdmet\"], start_time=\"2019-06-01\", end_time=\"2024-06-01\", as_xarray_dataset=True)" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset> Size: 3MB\n",
-       "Dimensions:                  (time: 39874, latitude: 1, longitude: 1)\n",
-       "Coordinates:\n",
-       "  * time                     (time) datetime64[ns] 319kB 2019-06-01 ... 2023-...\n",
-       "  * latitude                 (latitude) float32 4B 38.9\n",
-       "  * longitude                (longitude) float32 4B -76.44\n",
-       "Data variables: (12/14)\n",
-       "    wind_dir                 (time, latitude, longitude) float64 319kB 92.0 ....\n",
-       "    wind_spd                 (time, latitude, longitude) float32 159kB 1.8 .....\n",
-       "    gust                     (time, latitude, longitude) float32 159kB 1.9 .....\n",
-       "    wave_height              (time, latitude, longitude) float32 159kB nan .....\n",
-       "    dominant_wpd             (time, latitude, longitude) timedelta64[ns] 319kB ...\n",
-       "    average_wpd              (time, latitude, longitude) timedelta64[ns] 319kB ...\n",
-       "    ...                       ...\n",
-       "    air_temperature          (time, latitude, longitude) float32 159kB 25.5 ....\n",
-       "    sea_surface_temperature  (time, latitude, longitude) float32 159kB 23.7 ....\n",
-       "    dewpt_temperature        (time, latitude, longitude) float32 159kB 17.2 ....\n",
-       "    visibility               (time, latitude, longitude) float32 159kB nan .....\n",
-       "    water_level              (time, latitude, longitude) float32 159kB nan .....\n",
-       "    station_id               <U5 20B 'tplm2'\n",
-       "Attributes:\n",
-       "    institution:  NOAA National Data Buoy Center and Participators in Data As...\n",
-       "    url:          http://dods.ndbc.noaa.gov\n",
-       "    quality:      Automated QC checks with manual editing and comprehensive m...\n",
-       "    conventions:  COARDS\n",
-       "    station:      tplm2\n",
-       "    comment:      Thomas Point, MD\n",
-       "    location:     38.899 N 76.436 W