"""Getters for the API's own vocabularies.
Reference tables and per-collection queryables -- the parameter codes, statistic
codes, and filterable properties the other getters accept. These describe the
service rather than the water, so they are the one family whose results are
mostly stable between calls.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, get_args
import pandas as pd
from dataretrieval.ogc.schema import queryables_frame
from dataretrieval.waterdata.types import (
METADATA_COLLECTIONS,
)
from dataretrieval.waterdata.utils import (
OGC_API_URL,
get_ogc_data,
)
if TYPE_CHECKING:
from dataretrieval._response_metadata import BaseMetadata
[docs]
def get_reference_table(
collection: str,
limit: int | None = None,
query: dict[str, Any] | None = None,
max_rows: int | None = None,
) -> tuple[pd.DataFrame, BaseMetadata]:
"""Get metadata reference tables for the USGS Water Data API.
Reference tables provide the range of allowable values for parameter
arguments in the waterdata module.
Parameters
----------
collection : string
One of the following options: "agency-codes", "altitude-datums",
"aquifer-codes", "aquifer-types", "coordinate-accuracy-codes",
"coordinate-datum-codes", "coordinate-method-codes", "counties",
"hydrologic-unit-codes", "medium-codes", "national-aquifer-codes",
"parameter-codes", "reliability-codes", "site-types", "states",
"statistic-codes", "topographic-codes", "time-zone-codes"
limit : int, optional
The number of features returned in each page. The maximum allowable
limit is 50000; the default (None) requests that maximum. Set a lower
number if your internet connection is spotty.
query: dictionary, optional
A dictionary of extra query parameters to pass to the collection API
call.
max_rows : int, optional
Cap the total number of rows returned, stopping pagination early
instead of downloading the whole table. Useful for cheaply
previewing large tables (e.g. ``hydrologic-unit-codes`` has ~125k
rows). Unlike ``limit`` (the per-page size), this bounds the total
result. The default (None) downloads every page.
Returns
-------
df : ``pandas.DataFrame`` or ``geopandas.GeoDataFrame``
Formatted data returned from the API query. The primary metadata
of each reference table will show up in the first column, where
the name of the column is the singular form of the collection name,
separated by underscores (e.g. the "medium-codes" reference table
has a column called "medium_code", which contains all possible
medium code values).
md: :obj:`dataretrieval.utils.BaseMetadata`
A custom metadata object including the URL request and query time.
Raises
------
ChunkInterrupted
A transient failure (429 / 5xx / timeout) interrupted the request
after the built-in retries. Completed work is preserved; resume
with ``exc.call.resume()`` (see :doc:`/userguide/errors`).
Examples
--------
.. code::
>>> # Get table of USGS parameter codes
>>> ref, md = dataretrieval.waterdata.get_reference_table(
... collection="parameter-codes"
... )
>>> # Get table of selected USGS parameter codes
>>> ref, md = dataretrieval.waterdata.get_reference_table(
... collection="parameter-codes",
... query={"id": "00001,00002"},
... )
"""
valid_code_services = get_args(METADATA_COLLECTIONS)
if collection not in valid_code_services:
raise ValueError(
f"Invalid code service: '{collection}'. "
f"Valid options are: {valid_code_services}."
)
# Give the ID column the collection name, singularized and underscored.
if collection == "counties":
output_id = "county"
elif collection.endswith("s"):
output_id = collection[:-1].replace("-", "_")
else:
output_id = collection.replace("-", "_")
query_args = dict(query) if query else {}
if limit is not None:
query_args["limit"] = limit
return get_ogc_data(
args=query_args, output_id=output_id, collection=collection, max_rows=max_rows
)
[docs]
def get_queryables(collection: str) -> tuple[pd.DataFrame, BaseMetadata]:
"""List the queryable properties of a Water Data API collection.
Every OGC collection (``daily``, ``continuous``, ``monitoring-locations``,
...) advertises the set of properties that can be filtered on -- exposed as
the typed keyword arguments of the matching ``get_*`` function, and usable
directly in a CQL2 ``filter``. This function returns that set, so you can
discover the available filters programmatically and monitor them for
upstream additions.
Parameters
----------
collection : string
The collection id, e.g. ``"daily"``, ``"continuous"``,
``"monitoring-locations"``, or ``"time-series-metadata"``. See
:data:`dataretrieval.waterdata.types.WATERDATA_SERVICES` for the data
collections; reference collections (e.g. ``"parameter-codes"``) work
too.
Returns
-------
df : ``pandas.DataFrame``
One row per queryable, sorted by name, with columns ``queryable`` (the
property name), ``type``, ``title``, and ``description``.
md : :class:`dataretrieval.utils.BaseMetadata`
Metadata describing the request (URL, query time, response headers).
Raises
------
DataRetrievalError
On an HTTP error response (e.g. an unknown ``collection`` yields a 404),
the typed subclass for the status.
Examples
--------
.. doctest::
:skipif: True # network
>>> from dataretrieval import waterdata
>>> df, md = waterdata.get_queryables("daily")
>>> df.set_index("queryable").loc["state_name", "type"]
'string'
"""
# Reading the queryables document is OGC protocol work; this getter only
# names the API to ask.
return queryables_frame(collection, base_url=OGC_API_URL)
__all__ = ["get_reference_table", "get_queryables"]