From c450c404d22b823af629935ff3954226e53d5fb4 Mon Sep 17 00:00:00 2001 From: Timothy Hodson <34148978+thodson-usgs@users.noreply.github.com> Date: Tue, 26 May 2026 13:50:54 -0500 Subject: [PATCH 1/8] feat(http): Migrate from requests to httpx (#289) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap the package's HTTP client: `requests.Session` → `httpx.Client`, `requests.Response` → `httpx.Response`. Same call patterns, same response shape; only the underlying transport changes. Headers, timeout, and redirect defaults are centralized in `dataretrieval.utils.HTTPX_DEFAULTS` (`httpx.Timeout(60.0, connect=10.0)`, `follow_redirects=True`). The chunker, paginated-loop helpers, and OGC waterdata fetchers route through one `httpx.Client` that the chunker publishes on the `_chunked_client` ContextVar so paginated sub-requests reuse the connection pool across a single chunked call. Tests migrate from `requests_mock` to native `pytest-httpx`. The new `tests/conftest.py` is ~30 lines configuring pytest-httpx strict-mode relaxations. Co-authored-by: Claude Opus 4.7 --- dataretrieval/nadp.py | 6 +- dataretrieval/nldi.py | 10 +- dataretrieval/nwis.py | 18 +- dataretrieval/streamstats.py | 8 +- dataretrieval/utils.py | 78 ++-- dataretrieval/waterdata/api.py | 36 +- dataretrieval/waterdata/chunking.py | 589 +++++++++++++--------------- dataretrieval/waterdata/ratings.py | 12 +- dataretrieval/waterdata/utils.py | 274 +++++++------ dataretrieval/wqp.py | 4 +- pyproject.toml | 4 +- tests/conftest.py | 32 ++ tests/nldi_test.py | 170 +++++--- tests/nwis_test.py | 35 +- tests/utils_test.py | 4 +- tests/waterdata_chunking_test.py | 377 ++++++++---------- tests/waterdata_filters_test.py | 2 +- tests/waterdata_progress_test.py | 14 +- tests/waterdata_ratings_test.py | 61 ++- tests/waterdata_test.py | 39 +- tests/waterdata_utils_test.py | 86 ++-- tests/waterservices_test.py | 85 ++-- tests/wqp_test.py | 77 ++-- 23 files changed, 1093 insertions(+), 928 deletions(-) create mode 100644 tests/conftest.py diff --git a/dataretrieval/nadp.py b/dataretrieval/nadp.py index 370b4927..3d1ee442 100644 --- a/dataretrieval/nadp.py +++ b/dataretrieval/nadp.py @@ -34,7 +34,9 @@ import warnings import zipfile -import requests +import httpx + +from dataretrieval.utils import HTTPX_DEFAULTS _DEPRECATION_MESSAGE = ( "The `nadp` module is deprecated and will be removed from `dataretrieval` " @@ -213,7 +215,7 @@ def get_zip(url, filename): """ _warn_deprecated() - req = requests.get(url + filename) + req = httpx.get(url + filename, **HTTPX_DEFAULTS) req.raise_for_status() # z = zipfile.ZipFile(io.BytesIO(req.content)) diff --git a/dataretrieval/nldi.py b/dataretrieval/nldi.py index 8304c397..e54ceb85 100644 --- a/dataretrieval/nldi.py +++ b/dataretrieval/nldi.py @@ -20,7 +20,7 @@ def _query_nldi(url, query_params, error_message): # A helper function to query the NLDI API response = query(url, payload=query_params) if response.status_code != 200: - raise ValueError(f"{error_message}. Error reason: {response.reason}") + raise ValueError(f"{error_message}. Error reason: {response.reason_phrase}") response_data = {} try: @@ -453,6 +453,14 @@ def _validate_data_source(data_source: str): available_data_sources = _query_nldi( url, {}, "Error getting available data sources" ) + if not isinstance(available_data_sources, list) or not all( + isinstance(ds, dict) and "source" in ds for ds in available_data_sources + ): + raise ValueError( + "NLDI data-source catalog returned an unexpected shape; " + "expected a list of {'source': ..., ...} objects, got: " + f"{available_data_sources!r}" + ) _AVAILABLE_DATA_SOURCES = [ds["source"] for ds in available_data_sources] if data_source not in _AVAILABLE_DATA_SOURCES: diff --git a/dataretrieval/nwis.py b/dataretrieval/nwis.py index 25384d5e..cfcdc64e 100644 --- a/dataretrieval/nwis.py +++ b/dataretrieval/nwis.py @@ -11,8 +11,8 @@ import warnings from json import JSONDecodeError +import httpx import pandas as pd -import requests from dataretrieval.rdb import read_rdb from dataretrieval.utils import BaseMetadata @@ -110,7 +110,7 @@ def wrapper(*args, **kwargs): return wrapper -def _parse_json_or_raise(response: requests.Response) -> pd.DataFrame: +def _parse_json_or_raise(response: httpx.Response) -> pd.DataFrame: """Parse a JSON NWIS response, raising a helpful error on HTML responses.""" try: return _read_json(response.json()) @@ -364,9 +364,7 @@ def get_stats( @_deprecated -def query_waterdata( - service: str, ssl_check: bool = True, **kwargs -) -> requests.models.Response: +def query_waterdata(service: str, ssl_check: bool = True, **kwargs) -> httpx.Response: """ Queries waterdata. @@ -382,7 +380,7 @@ def query_waterdata( Returns ------- - request: ``requests.models.Response`` + request: ``httpx.Response`` The response object from the API request to the web service """ major_params = ["site_no", "state_cd"] @@ -412,7 +410,7 @@ def query_waterdata( @_deprecated def query_waterservices( service: str, ssl_check: bool = True, **kwargs -) -> requests.models.Response: +) -> httpx.Response: """ Queries waterservices.usgs.gov @@ -451,7 +449,7 @@ def query_waterservices( Returns ------- - request: ``requests.models.Response`` + request: ``httpx.Response`` The response object from the API request to the web service """ @@ -1123,7 +1121,7 @@ class NWIS_Metadata(BaseMetadata): Response url query_time: datetme.timedelta Response elapsed time - header: requests.structures.CaseInsensitiveDict + header: httpx.Headers Response headers comments: str | None Metadata comments, if any @@ -1143,7 +1141,7 @@ def __init__(self, response, **parameters) -> None: Parameters ---------- response: Response - Response object from requests module + Response object from httpx module parameters: unpacked dictionary Unpacked dictionary of the parameters supplied in the request diff --git a/dataretrieval/streamstats.py b/dataretrieval/streamstats.py index 7cddabaa..9a27f936 100644 --- a/dataretrieval/streamstats.py +++ b/dataretrieval/streamstats.py @@ -7,7 +7,9 @@ import json -import requests +import httpx + +from dataretrieval.utils import HTTPX_DEFAULTS def download_workspace(workspaceID, format=""): @@ -32,7 +34,7 @@ def download_workspace(workspaceID, format=""): payload = {"workspaceID": workspaceID, "format": format} url = "https://streamstats.usgs.gov/streamstatsservices/download" - r = requests.get(url, params=payload) + r = httpx.get(url, params=payload, **HTTPX_DEFAULTS) r.raise_for_status() return r @@ -125,7 +127,7 @@ def get_watershed( } url = "https://streamstats.usgs.gov/streamstatsservices/watershed.geojson" - r = requests.get(url, params=payload) + r = httpx.get(url, params=payload, **HTTPX_DEFAULTS) r.raise_for_status() diff --git a/dataretrieval/utils.py b/dataretrieval/utils.py index 76bbb6ad..7bb03a69 100644 --- a/dataretrieval/utils.py +++ b/dataretrieval/utils.py @@ -5,12 +5,17 @@ import warnings from collections.abc import Iterable +import httpx import pandas as pd -import requests import dataretrieval from dataretrieval.codes import tz +HTTPX_DEFAULTS = { + "follow_redirects": True, + "timeout": httpx.Timeout(60.0, connect=10.0), +} + def to_str(listlike, delimiter=","): """Translates list-like objects into strings. @@ -205,7 +210,7 @@ class BaseMetadata: Response url query_time: datetme.timedelta Response elapsed time - header: requests.structures.CaseInsensitiveDict + header: httpx.Headers Response headers """ @@ -216,7 +221,7 @@ def __init__(self, response) -> None: Parameters ---------- response: Response - Response object from requests module + Response object from httpx module Returns ------- @@ -225,8 +230,8 @@ def __init__(self, response) -> None: """ - # These are built from the API response - self.url = response.url + # Coerce httpx.URL -> str: BaseMetadata.url has always been str. + self.url = str(response.url) self.query_time = response.elapsed self.header = response.headers self.comment = None @@ -254,10 +259,29 @@ def __repr__(self) -> str: return f"{type(self).__name__}(url={self.url})" +_URL_TOO_LONG_EXAMPLE = """ + # n is the number of chunks to divide the query into \n + split_list = np.array_split(site_list, n) + data_list = [] # list to store chunk results in \n + # loop through chunks and make requests \n + for site_list in split_list: \n + data = nwis.get_record(sites=site_list, service='dv', \n + start=start, end=end) \n + data_list.append(data) # append results to list""" + + +def _url_too_long_error(detail: str) -> ValueError: + return ValueError( + "Request URL too long. Modify your query to use fewer sites. " + f"{detail}. Pseudo-code example of how to split your query: " + f"\n {_URL_TOO_LONG_EXAMPLE}" + ) + + def query(url, payload, delimiter=",", ssl_check=True): """Send a query. - Wrapper for requests.get that handles errors, converts listed + Wrapper for httpx.get that handles errors, converts listed query parameters to comma separated strings, and returns response. Parameters @@ -265,7 +289,7 @@ def query(url, payload, delimiter=",", ssl_check=True): url: string URL to query payload: dict - query parameters passed to ``requests.get`` + query parameters passed to ``httpx.get`` delimiter: string delimiter to use with lists ssl_check: bool @@ -275,19 +299,27 @@ def query(url, payload, delimiter=",", ssl_check=True): Returns ------- string: query response - The response from the API query ``requests.get`` function call. + The response from the API query ``httpx.get`` function call. """ for key, value in payload.items(): payload[key] = to_str(value, delimiter) - # for index in range(len(payload)): - # key, value = payload[index] - # payload[index] = (key, to_str(value)) + # httpx serializes None params as ``foo=``; USGS rejects with 400. + # Drop them. (``to_str`` returns None for non-iterable scalars like bools.) + payload = {k: v for k, v in payload.items() if v is not None} - # define the user agent for the query user_agent = {"user-agent": f"python-dataretrieval/{dataretrieval.__version__}"} - response = requests.get(url, params=payload, headers=user_agent, verify=ssl_check) + try: + response = httpx.get( + url, + params=payload, + headers=user_agent, + verify=ssl_check, + **HTTPX_DEFAULTS, + ) + except httpx.InvalidURL as exc: + raise _url_too_long_error(f"httpx rejected the URL client-side: {exc}") from exc if response.status_code == 400: raise ValueError( @@ -299,24 +331,10 @@ def query(url, payload, delimiter=",", ssl_check=True): + f"URL: {response.url}" ) elif response.status_code == 414: - _reason = response.reason - _example = """ - # n is the number of chunks to divide the query into \n - split_list = np.array_split(site_list, n) - data_list = [] # list to store chunk results in \n - # loop through chunks and make requests \n - for site_list in split_list: \n - data = nwis.get_record(sites=site_list, service='dv', \n - start=start, end=end) \n - data_list.append(data) # append results to list""" - raise ValueError( - "Request URL too long. Modify your query to use fewer sites. " - + f"API response reason: {_reason}. Pseudo-code example of how to " - + f"split your query: \n {_example}" - ) - elif response.status_code in [500, 502, 503]: + raise _url_too_long_error(f"API response reason: {response.reason_phrase}") + elif 500 <= response.status_code < 600: raise ValueError( - f"Service Unavailable: {response.status_code} {response.reason}. " + f"Service Unavailable: {response.status_code} {response.reason_phrase}. " + f"The service at {response.url} may be down or experiencing issues." ) diff --git a/dataretrieval/waterdata/api.py b/dataretrieval/waterdata/api.py index 6f24d80f..57fffc88 100644 --- a/dataretrieval/waterdata/api.py +++ b/dataretrieval/waterdata/api.py @@ -13,11 +13,15 @@ from typing import get_args from urllib.parse import quote +import httpx import pandas as pd -import requests -from requests.models import PreparedRequest -from dataretrieval.utils import BaseMetadata, _attach_datetime_columns, to_str +from dataretrieval.utils import ( + HTTPX_DEFAULTS, + BaseMetadata, + _attach_datetime_columns, + to_str, +) from dataretrieval.waterdata.filters import FILTER_LANG from dataretrieval.waterdata.types import ( CODE_SERVICES, @@ -2110,7 +2114,7 @@ def get_codes(code_service: CODE_SERVICES) -> pd.DataFrame: url = f"{SAMPLES_URL}/codeservice/{code_service}?mimeType=application%2Fjson" - response = requests.get(url, headers=_default_headers()) + response = httpx.get(url, headers=_default_headers(), **HTTPX_DEFAULTS) response.raise_for_status() @@ -2336,12 +2340,14 @@ def get_samples( url = f"{SAMPLES_URL}/{service}/{profile}" - req = PreparedRequest() - req.prepare_url(url, params=params) - logger.debug("Request: %s", req.url) + logger.debug("Request: %s", httpx.URL(url).copy_merge_params(params)) - response = requests.get( - url, params=params, verify=ssl_check, headers=_default_headers() + response = httpx.get( + url, + params=params, + verify=ssl_check, + headers=_default_headers(), + **HTTPX_DEFAULTS, ) response.raise_for_status() @@ -2408,12 +2414,14 @@ def get_samples_summary( url = f"{SAMPLES_URL}/summary/{quote(monitoringLocationIdentifier, safe='')}" params = {"mimeType": "text/csv"} - req = PreparedRequest() - req.prepare_url(url, params=params) - logger.debug("Request: %s", req.url) + logger.debug("Request: %s", httpx.URL(url).copy_merge_params(params)) - response = requests.get( - url, params=params, verify=ssl_check, headers=_default_headers() + response = httpx.get( + url, + params=params, + verify=ssl_check, + headers=_default_headers(), + **HTTPX_DEFAULTS, ) response.raise_for_status() diff --git a/dataretrieval/waterdata/chunking.py b/dataretrieval/waterdata/chunking.py index a6fee155..36ee24fd 100644 --- a/dataretrieval/waterdata/chunking.py +++ b/dataretrieval/waterdata/chunking.py @@ -9,18 +9,12 @@ sub-request URL fits. Requests that already fit get a trivial single-step plan — ``ChunkedCall`` has one code path either way. -Quota: after the first sub-request ``ChunkedCall`` reads -``x-ratelimit-remaining``; if the rest of the plan won't fit, it -raises ``RequestExceedsQuota`` before burning more budget. Set -``API_USGS_LIMIT=0`` to skip this pre-emptive check and attempt the -full plan anyway. - Interruption: any mid-stream transient failure (429, 5xx) surfaces as a ``ChunkInterrupted`` subclass — ``QuotaExhausted`` for 429, ``ServiceInterrupted`` for 5xx. The exception carries ``.call``, a ``ChunkedCall`` handle that owns the already-completed sub-request -state. Call ``.call.resume()`` once the underlying condition clears -to resume; only the still-pending sub-requests are re-issued. +state. Call ``.call.resume()`` once the underlying condition +clears; only the still-pending sub-requests are re-issued. ``Retry-After`` (when the server sets it) is surfaced on the exception as ``.retry_after``. @@ -37,17 +31,18 @@ import functools import itertools import math -import os from collections.abc import Callable, Iterator -from contextlib import contextmanager +from contextlib import contextmanager, suppress from contextvars import ContextVar from dataclasses import dataclass +from datetime import timedelta from typing import Any, ClassVar from urllib.parse import quote_plus +import httpx import pandas as pd -import requests -from requests.structures import CaseInsensitiveDict + +from dataretrieval.utils import HTTPX_DEFAULTS from . import _progress from .filters import ( @@ -98,45 +93,45 @@ # Response header USGS uses to advertise remaining hourly quota. _QUOTA_HEADER = "x-ratelimit-remaining" -# Session shared across all sub-requests of a single chunked call so +# Client shared across all sub-requests of a single chunked call so # paginated-loop helpers downstream (``_walk_pages``) reuse one -# connection pool across the whole fan-out. ``None`` when not inside a +# connection pool across the whole call. ``None`` when not inside a # chunked call — paginated helpers fall back to their own short-lived -# session in that case. -_chunked_session: ContextVar[requests.Session | None] = ContextVar( - "_chunked_session", default=None +# client in that case. +_chunked_client: ContextVar[httpx.Client | None] = ContextVar( + "_chunked_client", default=None ) @contextmanager -def _publish_session(session: requests.Session) -> Iterator[None]: +def _publish_client(client: httpx.Client) -> Iterator[None]: """ - Make ``session`` visible to :func:`get_active_session` for the - duration of the ``with`` block via the ``_chunked_session`` + Make ``client`` visible to :func:`get_active_client` for the + duration of the ``with`` block via the ``_chunked_client`` ContextVar. Wraps the set/reset token dance so callers don't have to. """ - token = _chunked_session.set(session) + token = _chunked_client.set(client) try: yield finally: - _chunked_session.reset(token) + _chunked_client.reset(token) -def get_active_session() -> requests.Session | None: +def get_active_client() -> httpx.Client | None: """ - Return the chunker's currently-published session, or ``None``. + Return the chunker's currently-published sync client, or ``None``. - Public accessor for the ``_chunked_session`` ContextVar so - sibling modules (notably :func:`dataretrieval.waterdata.utils._session`) + Public accessor for the ``_chunked_client`` ContextVar so + sibling modules (notably :func:`dataretrieval.waterdata.utils._client_for`) don't have to reach into the private ContextVar directly. Returns ------- - requests.Session or None - The session published by :func:`_publish_session` if currently + httpx.Client or None + The client published by :func:`_publish_client` if currently inside a :class:`ChunkedCall` ``resume`` block; ``None`` otherwise. """ - return _chunked_session.get() + return _chunked_client.get() # Separators the two axis kinds use to join their atoms back into @@ -145,7 +140,7 @@ def get_active_session() -> requests.Session | None: _LIST_SEP = "," _OR_SEP = " OR " -_FetchOnce = Callable[[dict[str, Any]], tuple[pd.DataFrame, requests.Response]] +_FetchOnce = Callable[[dict[str, Any]], tuple[pd.DataFrame, httpx.Response]] class _RetryableTransportError(RuntimeError): @@ -211,60 +206,6 @@ class RequestTooLarge(ValueError): """ -class RequestExceedsQuota(ValueError): - """ - Remaining rate-limit window can't cover the rest of the chunked plan. - - Raised after a sub-request when ``x-ratelimit-remaining`` in the - response shows the rest of the plan can't fit in the current per-key - rate-limit window. The chunks completed so far have already been - issued and consumed quota; ``ChunkedCall`` stops here rather than - burn more quota on a call that will fail mid-way. The completed - work is preserved on ``.call`` (the originating ``ChunkedCall``) - so callers can recover its ``partial_frame`` / ``partial_response`` - and, once the rate-limit window resets, call ``.call.resume()`` - to continue. - - Attributes - ---------- - planned_chunks : int - Total sub-requests the joint plan would issue. - available : int - Sub-requests this caller can still issue in the current window - (``x-ratelimit-remaining`` + chunks already completed). - deficit : int - ``planned_chunks - available`` — how far over budget the call - would run if it continued. - call : ChunkedCall or None - The originating call handle. ``None`` on hand-constructed - exceptions (test fixtures); otherwise the live handle whose - ``partial_frame`` / ``partial_response`` expose the work - completed before the check fired and whose ``resume()`` can be - called once the rate-limit window rolls over. - """ - - def __init__( - self, - *, - planned_chunks: int, - available: int, - deficit: int, - call: ChunkedCall | None = None, - ) -> None: - super().__init__( - f"Request would issue {planned_chunks} sub-requests but only " - f"{available} fit in the current rate-limit window (short by " - f"{deficit}). Wait for the window to reset, request a higher " - f"per-key quota, narrow the query, or set " - f"API_USGS_LIMIT=0 to bypass this check and risk a " - f"mid-stream 429 (recoverable via QuotaExhausted.resume())." - ) - self.planned_chunks = planned_chunks - self.available = available - self.deficit = deficit - self.call = call - - class ChunkInterrupted(RuntimeError): """ Base class for mid-stream chunk failures whose completed work is @@ -302,7 +243,7 @@ class ChunkInterrupted(RuntimeError): was raised. Snapshot at raise time — does NOT advance on a later ``call.resume()`` (use ``exc.call.partial_frame`` for the live view). - partial_response : requests.Response or None + partial_response : httpx.Response or None Aggregated response covering the completed sub-requests at raise time; ``None`` if nothing had completed yet. Same snapshot semantics as ``partial_frame``. @@ -346,12 +287,15 @@ def __init__( total_chunks: int, call: ChunkedCall | None = None, retry_after: float | None = None, + cause: BaseException | None = None, ) -> None: - super().__init__( - self._MESSAGE_TEMPLATE.format( - completed_chunks=completed_chunks, total_chunks=total_chunks - ) + message = self._MESSAGE_TEMPLATE.format( + completed_chunks=completed_chunks, total_chunks=total_chunks ) + if cause is not None: + cause_msg = str(cause) or type(cause).__name__ + message = f"{message} Cause: {type(cause).__name__}: {cause_msg}" + super().__init__(message) self.completed_chunks = completed_chunks self.total_chunks = total_chunks self.call = call @@ -365,7 +309,7 @@ def __init__( # already comes via ``copy.copy`` from ``_combine_chunk_responses``. if call is None: self.partial_frame: pd.DataFrame = pd.DataFrame() - self.partial_response: requests.Response | None = None + self.partial_response: httpx.Response | None = None else: self.partial_frame = call.partial_frame.copy() self.partial_response = call.partial_response @@ -376,17 +320,10 @@ class QuotaExhausted(ChunkInterrupted): A sub-request returned HTTP 429 — the per-key rate-limit window is exhausted. Subclass of :class:`ChunkInterrupted`. - For a chunked call (``total_chunks > 1``) reached past chunk 0, - the post-first-chunk :class:`RequestExceedsQuota` check normally - short-circuits before burning quota on a plan that won't fit; - arrival here typically means a concurrent caller drained the - window faster than predicted. ``partial_frame`` holds what - completed first. - - For a single-shot call (``total_chunks == 1``) or a 429 on the - very first chunk, ``partial_frame`` is empty and - ``partial_response`` is ``None``; the original ``RateLimited`` is - on ``__cause__``. + The completed sub-requests are preserved on ``.call``; once the + rate-limit window resets, ``.call.resume()`` re-issues only the + still-pending work. ``partial_frame`` holds what completed + before the 429. """ _MESSAGE_TEMPLATE = ( @@ -414,47 +351,112 @@ class ServiceInterrupted(ChunkInterrupted): ) -def _request_bytes(req: requests.PreparedRequest) -> int: +def _request_bytes(req: httpx.Request) -> int: + """ + Return the total bytes of an httpx request: URL + body. + + GET routes have empty ``.content`` and reduce to URL length. POST + routes (CQL2 JSON body) need body bytes — the URL stays short + regardless of payload, so URL-only sizing would underestimate the + request and skip chunking when it's needed. + + Parameters + ---------- + req : httpx.Request + The request to size. + + Returns + ------- + int + ``len(str(req.url)) + len(req.content)``. ``httpx.URL`` doesn't + support ``len()`` directly, so the str-coercion is required. + """ + return len(str(req.url)) + len(req.content) + + +def _safe_request_bytes( + build_request: Callable[..., httpx.Request], + args: dict[str, Any], + url_limit: int, +) -> int: """ - Total bytes of a prepared request: URL + body. + Size a candidate sub-request, treating ``httpx.InvalidURL`` as + "still too large". - GET routes have ``body=None`` and reduce to URL length. POST routes - (CQL2 JSON body) need body bytes — the URL stays short regardless - of payload, so URL-only sizing would underestimate the request and - skip chunking when it's needed. + ``httpx.URL`` enforces a hard 64 KB cap per URL component + (``MAX_URL_LENGTH``) and raises ``httpx.InvalidURL`` for anything + bigger. We report ``url_limit + 1`` on overflow so the greedy + halving loop in :meth:`ChunkPlan._plan` keeps shrinking the + largest axis until ``httpx.Request`` can be constructed at all. Parameters ---------- - req : requests.PreparedRequest - The prepared request to size. + build_request : Callable[..., httpx.Request] + Factory that turns a kwargs dict into a sized request. + args : dict[str, Any] + Per-sub-request kwargs to pass through to ``build_request``. + url_limit : int + The chunker's byte budget; returned + 1 on overflow. Returns ------- int - ``len(req.url) + len(req.body)`` where ``req.body`` is treated - as 0 bytes when ``None`` and UTF-8 encoded when ``str``. + Real byte count when the request builds, otherwise + ``url_limit + 1`` so the planner's "too large" branch keeps + halving. + """ + try: + req = build_request(**args) + except httpx.InvalidURL: + return url_limit + 1 + return _request_bytes(req) - Raises - ------ - TypeError - If ``req.body`` is not ``None``, ``bytes``/``bytearray``, or - ``str``. Size-based planning needs a deterministic byte count, - so generators and file-like streams are rejected up front - rather than silently treated as zero bytes. + +def _safe_elapsed(response: httpx.Response) -> timedelta: """ - body = req.body - if body is None: - body_len = 0 - elif isinstance(body, (bytes, bytearray)): - body_len = len(body) - elif isinstance(body, str): - body_len = len(body.encode("utf-8")) - else: - raise TypeError( - f"multi_value_chunked cannot size a request body of type " - f"{type(body).__name__!r}; pass str, bytes, or None." + Read ``response.elapsed``, falling back to ``timedelta(0)`` when + the attribute hasn't been populated. + + httpx only writes ``.elapsed`` when a response is closed through + its normal transport path. ``MockTransport`` (used by + ``pytest-httpx``) and hand-constructed ``httpx.Response`` objects + leave the attribute unset, so accessing it raises ``RuntimeError``. + Combining responses across chunks needs a defined duration, so we + treat the missing attribute as zero elapsed. + """ + try: + return response.elapsed + except RuntimeError: + return timedelta(0) + + +def _set_response_url(response: httpx.Response, url: str | httpx.URL) -> None: + """ + Overwrite the URL surfaced by a response without back-propagating + the change into any aliased original. + + On real ``httpx.Response`` instances ``.url`` is a read-only + property that resolves through the bound request; rather than + mutate the existing request's URL (which would be visible through + any shallow copy that shares the same ``.request``), we replace + the response's request with a fresh :class:`httpx.Request` carrying + the new URL. On lightweight test mocks ``.url`` is a plain + writable attribute — that path is tried first. + """ + try: + response.url = url # type: ignore[misc] + except AttributeError: + target = httpx.URL(str(url)) + try: + old = response.request + except RuntimeError: + # No request bound (some hand-built httpx.Response fixtures); + # synthesize a minimal one to hold the URL. + response.request = httpx.Request("GET", target) + return + response.request = httpx.Request( + method=old.method, url=target, headers=old.headers ) - return len(req.url) + body_len @dataclass(frozen=True) @@ -489,7 +491,8 @@ class _Axis: def chunk_bytes(self, chunk: list[str]) -> int: """ - URL-encoded bytes a chunk contributes when substituted. + Return the URL-encoded byte count this chunk contributes when + substituted into the request. ``quote_plus`` is faithful to what the real URL builder produces, so values containing characters that expand under URL @@ -588,11 +591,11 @@ class ChunkPlan: ---------- args : dict[str, Any] The user-level request kwargs. - build_request : Callable[..., requests.PreparedRequest] - Factory that turns a kwargs dict into a sized prepared - request, e.g. ``_construct_api_requests``. + build_request : Callable[..., httpx.Request] + Factory that turns a kwargs dict into a sized httpx request, + e.g. ``_construct_api_requests``. url_limit : int - Byte budget for the prepared request (URL + body). + Byte budget for the request (URL + body). Attributes ---------- @@ -607,12 +610,10 @@ class ChunkPlan: Per-axis partition: ``chunks[axis.arg_key]`` is the list of atom-sublists this axis is split into. Empty in passthrough. canonical_url : str or None - URL of the full original request, used to overwrite the first - chunk's ``response.url`` so ``BaseMetadata`` reflects the - user's full query. ``None`` on the nothing-to-chunk passthrough - path — ``fetch_once``'s response already carries the canonical - URL there, so ``ChunkedCall`` skips the override to avoid an - extra ``build_request`` call on the hot path. + URL of the user's original (un-chunked) request, used to + overwrite a chunked response's ``.url`` so ``BaseMetadata`` + reflects the full query. ``None`` on the passthrough path + and when no buildable URL exists. Raises ------ @@ -624,7 +625,7 @@ class ChunkPlan: def __init__( self, args: dict[str, Any], - build_request: Callable[..., requests.PreparedRequest], + build_request: Callable[..., httpx.Request], url_limit: int, ) -> None: self.args = args @@ -635,22 +636,45 @@ def __init__( axes = _extract_axes(args) # No chunkable axes → skip ``build_request`` entirely; the # common Water Data call shape shouldn't pay for an unused - # request prep on the passthrough hot path. + # request prep on the passthrough hot path. ``fetch_once`` + # will run with the user's args verbatim; if that produces + # an over-budget URL, the server (or httpx itself) rejects. if not axes: return - initial_request = build_request(**args) - self.canonical_url = initial_request.url - if _request_bytes(initial_request) <= url_limit: - return + # Constructing the initial request can itself trip + # ``httpx.InvalidURL`` (URL > 64 KB) — that's the canonical + # "needs chunking" signal, so swallow it and proceed to plan. + # When the unchunked URL does build, preserve it as + # ``canonical_url`` so ``BaseMetadata.url`` echoes the user's + # original query verbatim; only fall back to a worst-case + # sub-request URL when the URL itself can't be constructed. + try: + initial_request = build_request(**args) + except httpx.InvalidURL: + initial_request = None + + if initial_request is not None: + self.canonical_url = str(initial_request.url) + if _request_bytes(initial_request) <= url_limit: + return self.axes = axes self.chunks = {axis.arg_key: [list(axis.atoms)] for axis in axes} self._plan(build_request, url_limit) + if self.canonical_url is None: + # Original URL was un-constructable (httpx.InvalidURL); fall + # back to the worst-case sub-request URL so + # ``BaseMetadata.url`` still surfaces something + # informative. If even that overflows, leave canonical_url + # as None (set above) and let the response's own URL stand. + with suppress(httpx.InvalidURL): + self.canonical_url = str(build_request(**self._worst_case_args()).url) + def _plan( self, - build_request: Callable[..., requests.PreparedRequest], + build_request: Callable[..., httpx.Request], url_limit: int, ) -> None: """ @@ -668,7 +692,7 @@ def _plan( """ while True: worst = self._worst_case_args() - if _request_bytes(build_request(**worst)) <= url_limit: + if _safe_request_bytes(build_request, worst, url_limit) <= url_limit: return biggest_axis: _Axis | None = None @@ -743,7 +767,7 @@ def iter_sub_args(self) -> Iterator[dict[str, Any]]: sub_args[axis.arg_key] = axis.render(chunk) yield sub_args - def execute(self, fetch_once: _FetchOnce) -> tuple[pd.DataFrame, requests.Response]: + def execute(self, fetch_once: _FetchOnce) -> tuple[pd.DataFrame, httpx.Response]: """ Run the plan and return the combined ``(frame, response)``. @@ -760,7 +784,7 @@ def execute(self, fetch_once: _FetchOnce) -> tuple[pd.DataFrame, requests.Respon ------- df : pandas.DataFrame Combined data from every successful sub-request. - response : requests.Response + response : httpx.Response Aggregated response (canonical URL, last page's headers, cumulative elapsed time). @@ -771,54 +795,10 @@ def execute(self, fetch_once: _FetchOnce) -> tuple[pd.DataFrame, requests.Respon (:class:`QuotaExhausted` for 429, :class:`ServiceInterrupted` for 5xx). The resumable handle is on ``exc.call``. - RequestExceedsQuota - When the rate-limit window can't cover the remaining plan. """ return ChunkedCall(self, fetch_once).resume() -def _quota_check_disabled() -> bool: - """ - Check whether the pre-emptive quota check is disabled. - - Read at call time (not import time) so test patches via - ``monkeypatch.setenv`` take effect. - - Returns - ------- - bool - ``True`` when the environment variable ``API_USGS_LIMIT`` is - set to ``"0"`` (stripped), bypassing the post-first-chunk - :class:`RequestExceedsQuota` check. - """ - return os.environ.get("API_USGS_LIMIT", "").strip() == "0" - - -def _read_remaining(response: requests.Response) -> int | None: - """ - Parse the ``x-ratelimit-remaining`` header from a response. - - Parameters - ---------- - response : requests.Response - A response that may or may not carry the quota header. - - Returns - ------- - int or None - The parsed integer, or ``None`` when the header is missing or - unparseable. ``ChunkedCall`` treats ``None`` as "no quota - signal" and skips the post-first-chunk plan check. - """ - raw = response.headers.get(_QUOTA_HEADER) - if raw is None: - return None - try: - return int(raw) - except (TypeError, ValueError): - return None - - def _classify_chunk_error( exc: BaseException, ) -> tuple[type[ChunkInterrupted], float | None] | None: @@ -850,11 +830,13 @@ def _classify_chunk_error( ``__cause__``, so this function must walk the chain rather than just ``isinstance`` the top-level exception. - Bare ``requests.exceptions.RequestException`` (ConnectionError, - Timeout, SSLError, …) is also treated as a transient transport - failure and wrapped as :class:`ServiceInterrupted` — these don't - inherit from ``RuntimeError`` and would otherwise escape the - chunker's catch with no resumable handle. + Bare ``httpx.HTTPError`` (``ConnectError``, ``TimeoutException``, + etc.) and ``httpx.InvalidURL`` (server-supplied cursor URL too + long, oversize follow-up) are also treated as transport failures + and wrapped as :class:`ServiceInterrupted` — these don't inherit + from ``RuntimeError`` (and ``InvalidURL`` doesn't even inherit + from ``HTTPError``), so without explicit handling they would + escape the chunker's catch with no resumable handle. """ cur: BaseException | None = exc while cur is not None: @@ -862,7 +844,7 @@ def _classify_chunk_error( return QuotaExhausted, cur.retry_after if isinstance(cur, ServiceUnavailable): return ServiceInterrupted, cur.retry_after - if isinstance(cur, requests.exceptions.RequestException): + if isinstance(cur, (httpx.HTTPError, httpx.InvalidURL)): return ServiceInterrupted, None cur = cur.__cause__ return None @@ -930,59 +912,63 @@ def _combine_chunk_frames(frames: list[pd.DataFrame]) -> pd.DataFrame: def _combine_chunk_responses( - responses: list[requests.Response], canonical_url: str | None -) -> requests.Response: + responses: list[httpx.Response], canonical_url: str | None +) -> httpx.Response: """ Fold per-sub-request responses into a single aggregated response. - Returns a shallow copy of ``responses[0]`` with ``.headers`` set to - the last response's (so ``x-ratelimit-remaining`` reflects current - state), ``.elapsed`` set to total wall-clock across every response, - and ``.url`` set to the canonical original-query URL so - ``BaseMetadata`` reflects the user's full request rather than the - first chunk. + For a multi-response input, returns a shallow copy of + ``responses[0]`` with ``.headers`` set to the last response's (so + ``x-ratelimit-remaining`` reflects current state), ``.elapsed`` set + to total wall-clock across every response, and ``.url`` set to the + canonical original-query URL (when supplied) so ``BaseMetadata`` + reflects the user's full request rather than the first chunk. + + For a single-response input with no canonical-URL override, + ``responses[0]`` is returned unchanged to skip the copy on the + passthrough hot path. Parameters ---------- - responses : list[requests.Response] + responses : list[httpx.Response] One response per completed sub-request, in execution order. canonical_url : str or None URL of the unchunked original request. ``None`` skips the URL - override — used by the trivial-passthrough path where - ``fetch_once`` already returns a response whose ``.url`` is - the original-query URL. + override — used by the passthrough path (``fetch_once``'s + response already carries the original-query URL) and by the + worst-case overflow path (no buildable canonical URL exists). Returns ------- - requests.Response + httpx.Response A shallow copy of the first response with aggregated ``headers``, ``elapsed``, and ``url``. The function is idempotent (the input responses' ``headers`` / ``elapsed`` / ``url`` are never mutated), so it's safe to call repeatedly via :attr:`ChunkedCall.partial_response` during error inspection or resume retries. ``headers`` on the returned - object is a fresh ``CaseInsensitiveDict``, so mutations there - don't back-propagate into any chunk's underlying response. - Note that other ``Response`` fields (``_content``, ``raw``, - ``cookies``, ``request``) are still aliased to the first - chunk by the shallow copy — callers that mutate those will - affect the underlying chunk response. + object is a fresh ``httpx.Headers``, so mutations there don't + back-propagate into any chunk's underlying response. """ + if len(responses) == 1 and canonical_url is None: + return responses[0] + # ``copy.copy`` lets repeated calls re-sum elapsed from scratch # rather than re-mutating ``responses[0]`` in place. The headers - # dict is then rewrapped in a fresh ``CaseInsensitiveDict`` so the + # dict is then rewrapped in a fresh ``httpx.Headers`` so the # aggregate's headers don't share identity with — or leak mutations # back into — any underlying response on ``ChunkedCall._chunks``. head = copy.copy(responses[0]) if len(responses) > 1: - head.headers = CaseInsensitiveDict(responses[-1].headers) + head.headers = httpx.Headers(responses[-1].headers) head.elapsed = sum( - (r.elapsed for r in responses[1:]), start=responses[0].elapsed + (_safe_elapsed(r) for r in responses[1:]), + start=_safe_elapsed(responses[0]), ) else: - head.headers = CaseInsensitiveDict(responses[0].headers) + head.headers = httpx.Headers(responses[0].headers) if canonical_url is not None: - head.url = canonical_url + _set_response_url(head, canonical_url) return head @@ -1000,12 +986,12 @@ class ChunkedCall: executes; callers reach it via :attr:`ChunkInterrupted.call` on the exception raised by a mid-stream failure. - :meth:`resume` is idempotent: it skips sub-requests already - completed (``self.completed_chunks`` is the cursor) and re-issues - only the still-pending ones. The sub-request - ordering matches :meth:`ChunkPlan.iter_sub_args`, which is - deterministic, so each call picks up exactly where the previous - one stopped. + :meth:`resume` is idempotent: it iterates + :meth:`ChunkPlan.iter_sub_args` (deterministic order) and skips + any index whose result is already in ``self._chunks``. The + completion set is a ``dict[int, (df, response)]`` keyed by + sub-args index; a subsequent ``resume`` only re-issues + sub-requests whose index isn't already present. Parameters ---------- @@ -1021,14 +1007,10 @@ class ChunkedCall: The plan being driven (read-only after construction). fetch_once : Callable The per-sub-request fetch function. - completed_chunks : int - Number of sub-requests successfully completed so far. - total_chunks : int - Total sub-requests in ``plan`` (``== plan.total``). partial_frame : pandas.DataFrame Combined frame of completed sub-requests (live; recomputed per access). - partial_response : requests.Response or None + partial_response : httpx.Response or None Aggregated response with canonical URL restored, or ``None`` when nothing has completed yet (live; recomputed per access). """ @@ -1036,19 +1018,12 @@ class ChunkedCall: def __init__(self, plan: ChunkPlan, fetch_once: _FetchOnce) -> None: self.plan = plan self.fetch_once = fetch_once - # One entry per completed sub-request, in execution order. - # A single list keeps the (frame, response) pair atomic so the - # ``len(_chunks)`` cursor can't ever drift between two parallel - # lists. - self._chunks: list[tuple[pd.DataFrame, requests.Response]] = [] + # Completed (frame, response) pairs keyed by sub-args index; + # ``resume()`` skips indices already present. + self._chunks: dict[int, tuple[pd.DataFrame, httpx.Response]] = {} - @property - def completed_chunks(self) -> int: - return len(self._chunks) - - @property - def total_chunks(self) -> int: - return self.plan.total + def _ordered_chunks(self) -> list[tuple[pd.DataFrame, httpx.Response]]: + return [self._chunks[i] for i in sorted(self._chunks)] @property def partial_frame(self) -> pd.DataFrame: @@ -1067,10 +1042,10 @@ def partial_frame(self) -> pd.DataFrame: """ if not self._chunks: return pd.DataFrame() - return _combine_chunk_frames([frame for frame, _ in self._chunks]) + return _combine_chunk_frames([frame for frame, _ in self._ordered_chunks()]) @property - def partial_response(self) -> requests.Response | None: + def partial_response(self) -> httpx.Response | None: """ Aggregated response with the canonical URL restored to the user's full original query. @@ -1079,38 +1054,37 @@ def partial_response(self) -> requests.Response | None: Returns ------- - requests.Response or None + httpx.Response or None Aggregated response when at least one sub-request has completed, ``None`` otherwise. """ if not self._chunks: return None return _combine_chunk_responses( - [resp for _, resp in self._chunks], self.plan.canonical_url + [resp for _, resp in self._ordered_chunks()], self.plan.canonical_url ) - def resume(self) -> tuple[pd.DataFrame, requests.Response]: + def resume(self) -> tuple[pd.DataFrame, httpx.Response]: """ - Drive the chunked call to completion. + Drive the chunked call to completion via the sync ``fetch_once``. - Opens one ``requests.Session`` for the run and publishes it on - the ``_chunked_session`` ``ContextVar`` so paginated-loop + Opens one ``httpx.Client`` for the run and publishes it on + the ``_chunked_client`` ``ContextVar`` so paginated-loop helpers downstream (``_walk_pages``) reuse the same connection pool across every sub-request instead of handshaking fresh on - each. The session is closed when ``resume`` returns or raises; + each. The client is closed when ``resume`` returns or raises; a follow-up ``resume`` call (after a ``ChunkInterrupted``) opens a new one. - Idempotent: starts from chunk 0 on the first call, then from - the cursor (``self.completed_chunks``) on every subsequent - call. Re-issues only sub-requests that haven't already - completed. + Idempotent: only sub-requests whose index isn't already in + ``self._chunks`` are re-issued. Sub-args order matches + :meth:`ChunkPlan.iter_sub_args` and is deterministic. Returns ------- df : pandas.DataFrame Combined data from every successful sub-request. - response : requests.Response + response : httpx.Response Aggregated response (canonical URL, last page's headers, cumulative elapsed time). @@ -1122,41 +1096,44 @@ def resume(self) -> tuple[pd.DataFrame, requests.Response]: :class:`ServiceInterrupted` for 5xx). The resumable handle is on ``exc.call`` — wait for the underlying condition to clear and call ``exc.call.resume()`` again. - RequestExceedsQuota - When the rate-limit window can't cover the remaining plan - (checked after the first sub-request). """ - with requests.Session() as session, _publish_session(session): + with httpx.Client(**HTTPX_DEFAULTS) as client, _publish_client(client): reporter = _progress.current() if reporter is not None: reporter.set_chunks(self.plan.total) - completed = len(self._chunks) for i, sub_args in enumerate(self.plan.iter_sub_args()): - if i < completed: + if i in self._chunks: continue if reporter is not None: reporter.start_chunk(i + 1) - self._issue(sub_args) - frames = [frame for frame, _ in self._chunks] - responses = [resp for _, resp in self._chunks] + self._issue(i, sub_args) + ordered = self._ordered_chunks() + frames = [frame for frame, _ in ordered] + responses = [resp for _, resp in ordered] return ( _combine_chunk_frames(frames), _combine_chunk_responses(responses, self.plan.canonical_url), ) - def _issue(self, sub_args: dict[str, Any]) -> None: - # Catch both ``RuntimeError`` (the layer's typed contract: - # ``RateLimited`` / ``ServiceUnavailable`` / mid-pagination - # wrapper) and ``requests.exceptions.RequestException`` - # (transport-level failures like ConnectionError / Timeout / - # SSLError that bubble up unmodified from - # ``sess.send(initial_req)`` and don't inherit from - # RuntimeError). Both routes go through ``_classify_chunk_error`` - # so transient failures become resumable ``ChunkInterrupted`` - # subclasses; unknown failures re-raise to preserve their type. + def _issue(self, index: int, sub_args: dict[str, Any]) -> None: + """ + Issue one sub-request and record its ``(frame, response)`` pair + under ``index``. + + On failure, classify the exception and either wrap it as a + resumable :class:`ChunkInterrupted` carrying this call, or + re-raise it unchanged to preserve its type. Catches + ``RuntimeError`` (the layer's typed contract: + :class:`RateLimited`, :class:`ServiceUnavailable`, or the + mid-pagination wrapper), :class:`httpx.HTTPError` + (transport-level failures like ``ConnectError`` / + ``TimeoutException``), and :class:`httpx.InvalidURL` (which + inherits directly from ``Exception``, not ``HTTPError``); all + three feed :func:`_classify_chunk_error`. + """ try: - chunk = self.fetch_once(sub_args) - except (RuntimeError, requests.exceptions.RequestException) as exc: + self._chunks[index] = self.fetch_once(sub_args) + except (RuntimeError, httpx.HTTPError, httpx.InvalidURL) as exc: classification = _classify_chunk_error(exc) if classification is None: raise @@ -1166,31 +1143,13 @@ def _issue(self, sub_args: dict[str, Any]) -> None: total_chunks=self.plan.total, call=self, retry_after=retry_after, + cause=exc, ) from exc - self._chunks.append(chunk) - if len(self._chunks) < self.plan.total: - self._check_quota_remaining() - - def _check_quota_remaining(self) -> None: - if _quota_check_disabled(): - return - _, last_response = self._chunks[-1] - remaining = _read_remaining(last_response) - completed = len(self._chunks) - pending = self.plan.total - completed - if remaining is None or remaining >= pending: - return - raise RequestExceedsQuota( - planned_chunks=self.plan.total, - available=remaining + completed, - deficit=pending - remaining, - call=self, - ) def multi_value_chunked( *, - build_request: Callable[..., requests.PreparedRequest], + build_request: Callable[..., httpx.Request], url_limit: int | None = None, ) -> Callable[[_FetchOnce], _FetchOnce]: """ @@ -1204,15 +1163,15 @@ def multi_value_chunked( Parameters ---------- - build_request : Callable[..., requests.PreparedRequest] - Factory that turns a kwargs dict into a sized prepared - request, e.g. ``_construct_api_requests``. Called during - planning to measure each candidate plan. + build_request : Callable[..., httpx.Request] + Factory that turns a kwargs dict into a sized httpx request, + e.g. ``_construct_api_requests``. Called during planning to + measure each candidate plan. url_limit : int, optional - Byte budget for the prepared request (URL + body). When - ``None`` (default), the module-level - ``_WATERDATA_URL_BYTE_LIMIT`` is resolved at call time so test - patches via ``monkeypatch.setattr`` take effect. + Byte budget for the request (URL + body). When ``None`` + (default), the module-level ``_WATERDATA_URL_BYTE_LIMIT`` is + resolved at call time so test patches via + ``monkeypatch.setattr`` take effect. Returns ------- @@ -1225,9 +1184,6 @@ def multi_value_chunked( ------ RequestTooLarge If no plan can fit ``url_limit``. - RequestExceedsQuota - After the first sub-request, if the remaining plan can't fit - the current rate-limit window. ChunkInterrupted On a mid-execution 429 (:class:`QuotaExhausted`) or 5xx (:class:`ServiceInterrupted`). See :class:`ChunkedCall` for @@ -1243,9 +1199,10 @@ def decorator(fetch_once: _FetchOnce) -> _FetchOnce: @functools.wraps(fetch_once) def wrapper( args: dict[str, Any], - ) -> tuple[pd.DataFrame, requests.Response]: + ) -> tuple[pd.DataFrame, httpx.Response]: limit = _WATERDATA_URL_BYTE_LIMIT if url_limit is None else url_limit - return ChunkPlan(args, build_request, limit).execute(fetch_once) + plan = ChunkPlan(args, build_request, limit) + return plan.execute(fetch_once) return wrapper diff --git a/dataretrieval/waterdata/ratings.py b/dataretrieval/waterdata/ratings.py index a37c88b5..0e1b503d 100644 --- a/dataretrieval/waterdata/ratings.py +++ b/dataretrieval/waterdata/ratings.py @@ -17,10 +17,11 @@ from collections.abc import Iterable from typing import Any, Literal, get_args +import httpx import pandas as pd -import requests from dataretrieval.rdb import extract_rdb_comment, read_rdb +from dataretrieval.utils import HTTPX_DEFAULTS from .utils import ( _DURATION_RE, @@ -186,7 +187,7 @@ def get_ratings( fid = feature["id"] try: out[fid] = _download_and_parse(feature, file_path, ssl_check) - except (requests.RequestException, ValueError, OSError) as e: + except (httpx.HTTPError, ValueError, OSError) as e: logger.warning("Failed to download / parse %s: %s", fid, e) return out @@ -240,11 +241,12 @@ def _search( if bbox is not None: params["bbox"] = ",".join(map(str, bbox)) - response = requests.get( + response = httpx.get( f"{STAC_URL}/search", params=params, headers=_default_headers(), verify=ssl_check, + **HTTPX_DEFAULTS, ) response.raise_for_status() return response.json().get("features", []) @@ -257,7 +259,9 @@ def _download_and_parse( ) -> pd.DataFrame: """Fetch the feature's data asset, parse RDB, optionally persist to disk.""" url = feature["assets"]["data"]["href"] - response = requests.get(url, headers=_default_headers(), verify=ssl_check) + response = httpx.get( + url, headers=_default_headers(), verify=ssl_check, **HTTPX_DEFAULTS + ) response.raise_for_status() if file_path is not None: diff --git a/dataretrieval/waterdata/utils.py b/dataretrieval/waterdata/utils.py index dd908143..66ed1723 100644 --- a/dataretrieval/waterdata/utils.py +++ b/dataretrieval/waterdata/utils.py @@ -1,28 +1,34 @@ from __future__ import annotations import copy +import functools import json import logging import os import re -from collections.abc import Callable, Iterable, Iterator, Mapping +from collections.abc import ( + Callable, + Iterable, + Iterator, + Mapping, +) from contextlib import contextmanager from datetime import datetime, timedelta from typing import Any, TypeVar, get_args from zoneinfo import ZoneInfo +import httpx import pandas as pd -import requests -from requests.structures import CaseInsensitiveDict from dataretrieval import __version__ -from dataretrieval.utils import BaseMetadata +from dataretrieval.utils import HTTPX_DEFAULTS, BaseMetadata from dataretrieval.waterdata import _progress, chunking from dataretrieval.waterdata.chunking import ( _QUOTA_HEADER, RateLimited, ServiceUnavailable, - get_active_session, + _safe_elapsed, + get_active_client, ) from dataretrieval.waterdata.types import ( PROFILE_LOOKUP, @@ -366,24 +372,26 @@ def _check_ogc_requests(endpoint: str = "daily", req_type: str = "queryables"): ------ ValueError If req_type is not "queryables" or "schema". - requests.HTTPError - If the HTTP request returns an unsuccessful status code. + RateLimited, ServiceUnavailable, RuntimeError + From :func:`_raise_for_non_200` on any non-200 — same typed + contract as the main data path so callers can use one + ``except`` clause everywhere. """ if req_type not in ("queryables", "schema"): raise ValueError(f"req_type must be 'queryables' or 'schema', got {req_type!r}") url = f"{OGC_API_URL}/collections/{endpoint}/{req_type}" - resp = requests.get(url, headers=_default_headers()) - resp.raise_for_status() + resp = httpx.get(url, headers=_default_headers(), **HTTPX_DEFAULTS) + _raise_for_non_200(resp) return resp.json() -def _error_body(resp: requests.Response): +def _error_body(resp: httpx.Response): """ Build an informative error message from an HTTP response. Parameters ---------- - resp : requests.Response + resp : httpx.Response The HTTP response object to extract the error message from. Returns @@ -418,7 +426,7 @@ def _error_body(resp: requests.Response): j_txt = resp.json() except ValueError: snippet = (resp.text or "").strip()[:200] - reason = resp.reason or "Error" + reason = resp.reason_phrase or "Error" if snippet: return f"{status}: {reason}. {snippet}" return f"{status}: {reason}." @@ -459,18 +467,18 @@ def _parse_retry_after(value: str | None) -> float | None: return None -def _raise_for_non_200(resp: requests.Response) -> None: +def _raise_for_non_200(resp: httpx.Response) -> None: """ Raise a typed exception for any non-200 response. Routes through :func:`_error_body` (USGS-API-aware: handles 429/403 specially, extracts ``code``/``description`` from JSON error bodies) rather than ``Response.raise_for_status``, which - raises ``HTTPError`` with a generic message. + raises ``HTTPStatusError`` with a generic message. Parameters ---------- - resp : requests.Response + resp : httpx.Response The HTTP response to inspect. Raises @@ -520,7 +528,7 @@ def _paginated_failure_message(pages_collected: int, cause: BaseException) -> st and ``get_stats_data`` raise from the original exception. """ cause_str = str(cause).removesuffix(".") - # Some ``requests`` exceptions (e.g. ``Timeout()`` with no args) + # Some ``httpx`` exceptions (e.g. ``TimeoutException()`` with no args) # stringify to empty; fall back to the class name so the # returned message is always informative. if not cause_str.strip(): @@ -544,7 +552,7 @@ def _construct_api_requests( limit: int | None = None, skip_geometry: bool = False, **kwargs, -): +) -> httpx.Request: """ Constructs an HTTP request object for the specified water data API service. @@ -572,7 +580,7 @@ def _construct_api_requests( Returns ------- - requests.PreparedRequest + httpx.Request The constructed HTTP request object ready to be sent. Notes @@ -626,25 +634,23 @@ def _construct_api_requests( if post_params: headers["Content-Type"] = "application/query-cql-json" - request = requests.Request( + return httpx.Request( method="POST", url=service_url, headers=headers, - data=_cql2_param(post_params), - params=params, - ) - else: - request = requests.Request( - method="GET", - url=service_url, - headers=headers, + content=_cql2_param(post_params), params=params, ) - return request.prepare() + return httpx.Request( + method="GET", + url=service_url, + headers=headers, + params=params, + ) def _next_req_url( - resp: requests.Response, *, body: dict[str, Any] | None = None + resp: httpx.Response, *, body: dict[str, Any] | None = None ) -> str | None: """ Extracts the URL for the next page of results from an HTTP response from a @@ -652,7 +658,7 @@ def _next_req_url( Parameters ---------- - resp : requests.Response + resp : httpx.Response The HTTP response object containing JSON data and headers. body : dict, optional Pre-parsed JSON body for ``resp``. When provided, skips the @@ -676,13 +682,38 @@ def _next_req_url( if not body.get("numberReturned"): return None for link in body.get("links", []): - if link.get("rel") == "next": - return link.get("href") + if link.get("rel") != "next": + continue + href = link.get("href") + if not href: + return href + # Refuse to follow a next-page link to a different host — + # the request's headers/auth were minted for the original + # host and shouldn't leak to whatever a poisoned response + # body might supply. Guarded against mock-shaped ``resp.url`` + # attributes (tests sometimes set strings or ``MagicMock``) + # by falling open when host extraction isn't reliable. + try: + next_host = httpx.URL(href).host + resp_url = ( + resp.url + if isinstance(resp.url, httpx.URL) + else httpx.URL(str(resp.url)) + ) + cur_host = resp_url.host + except (httpx.InvalidURL, TypeError): + next_host = cur_host = None + if next_host and cur_host and next_host != cur_host: + raise RuntimeError( + f"Refusing to follow cross-host next-page URL: " + f"{next_host} != {cur_host}" + ) + return href return None def _get_resp_data( - resp: requests.Response, + resp: httpx.Response, geopd: bool, *, body: dict[str, Any] | None = None, @@ -692,7 +723,7 @@ def _get_resp_data( Parameters ---------- - resp : requests.Response + resp : httpx.Response The HTTP response object expected to contain a JSON body with a "features" key. geopd : bool @@ -769,47 +800,48 @@ def _get_resp_data( @contextmanager -def _session(client: requests.Session | None) -> Iterator[requests.Session]: +def _client_for(client: httpx.Client | None) -> Iterator[httpx.Client]: """ - Yield a usable session, picking the best available source. + Yield a usable client, picking the best available source. Resolution order: 1. ``client`` if the caller supplied one (borrowed; not closed here — the caller owns its lifecycle). - 2. The chunker's shared session if we're inside a ``ChunkedCall`` - fan-out (per :func:`chunking.get_active_session`). Borrowed; + 2. The chunker's shared client if we're inside a + ``ChunkedCall.resume()`` block (per + :func:`chunking.get_active_client`). Borrowed; ``ChunkedCall.resume`` closes it on exit. - 3. A fresh short-lived ``requests.Session`` opened here and closed + 3. A fresh short-lived ``httpx.Client`` opened here and closed on context exit. Parameters ---------- - client : requests.Session or None - A caller-owned session to borrow, or ``None`` to defer to the - chunker's shared session or a temporary one. + client : httpx.Client or None + A caller-owned client to borrow, or ``None`` to defer to the + chunker's shared client or a temporary one. Yields ------ - requests.Session - The chosen session. + httpx.Client + The chosen client. """ if client is not None: yield client return - shared = get_active_session() + shared = get_active_client() if shared is not None: yield shared return - with requests.Session() as new: + with httpx.Client(**HTTPX_DEFAULTS) as new: yield new def _aggregate_paginated_response( - initial: requests.Response, - last: requests.Response, + initial: httpx.Response, + last: httpx.Response, total_elapsed: timedelta, -) -> requests.Response: +) -> httpx.Response: """ Build a single response covering a paginated call. @@ -823,27 +855,24 @@ def _aggregate_paginated_response( Parameters ---------- - initial : requests.Response + initial : httpx.Response First-page response (the canonical one for ``md.url``). - last : requests.Response + last : httpx.Response Last-page response — supplies the headers to copy over. total_elapsed : datetime.timedelta Cumulative wall-clock across every page, including ``initial``. Returns ------- - requests.Response + httpx.Response A shallow copy of ``initial`` with ``.headers`` set to a fresh - ``CaseInsensitiveDict`` and ``.elapsed`` set to the - cumulative wall-clock. ``initial.headers`` / ``initial.elapsed`` - are never mutated, so callers holding a pre-pagination - reference still see the original first-page values. Other - ``Response`` fields (``_content``, ``raw``, ``cookies``, - ``request``) are still aliased to ``initial`` by the shallow - copy — callers that mutate those will affect ``initial``. + ``httpx.Headers`` and ``.elapsed`` set to the cumulative + wall-clock. ``initial.headers`` / ``initial.elapsed`` are + never mutated, so callers holding a pre-pagination reference + still see the original first-page values. """ final = copy.copy(initial) - final.headers = CaseInsensitiveDict(last.headers) + final.headers = httpx.Headers(last.headers) final.elapsed = total_elapsed return final @@ -852,12 +881,12 @@ def _aggregate_paginated_response( def _paginate( - initial_req: requests.PreparedRequest, + initial_req: httpx.Request, *, - parse_response: Callable[[requests.Response], tuple[pd.DataFrame, _Cursor | None]], - follow_up: Callable[[_Cursor, requests.Session], requests.Response], - client: requests.Session | None = None, -) -> tuple[pd.DataFrame, requests.Response]: + parse_response: Callable[[httpx.Response], tuple[pd.DataFrame, _Cursor | None]], + follow_up: Callable[[_Cursor, httpx.Client], httpx.Response], + client: httpx.Client | None = None, +) -> tuple[pd.DataFrame, httpx.Response]: """ Drive a paginated request to completion. @@ -870,27 +899,27 @@ def _paginate( Parameters ---------- - initial_req : requests.PreparedRequest + initial_req : httpx.Request First-page request to send. parse_response : callable ``resp -> (df, next_cursor_or_None)``. Returns the page's DataFrame and the cursor (URL, token, …) used to drive ``follow_up`` for the next page; ``None`` terminates the loop. follow_up : callable - ``(cursor, session) -> requests.Response``. Builds and sends + ``(cursor, client) -> httpx.Response``. Builds and sends the next-page request. - client : requests.Session, optional - Caller-borrowed session. ``None`` (default) means use the - chunker's shared session (if inside a chunked call) or open + client : httpx.Client, optional + Caller-borrowed client. ``None`` (default) means use the + chunker's shared client (if inside a chunked call) or open a temporary one. Returns ------- df : pandas.DataFrame Concatenation of every page's parsed frame. - response : requests.Response + response : httpx.Response A shallow copy of the first-page response, with ``.headers`` - rebuilt as a fresh ``CaseInsensitiveDict`` reflecting the last + rebuilt as a fresh ``httpx.Headers`` reflecting the last page and ``.elapsed`` set to cumulative wall-clock. The canonical URL is preserved from the first page. The original first-page response is not mutated. @@ -906,22 +935,22 @@ def _paginate( (wrapped via :func:`_paginated_failure_message` with the original exception on ``__cause__``), or any failure on a subsequent page (same wrapping). - requests.exceptions.RequestException + httpx.HTTPError Network-level failures on the *initial* request (e.g. - ``ConnectionError``, ``Timeout``) propagate unmodified so - callers can branch on the specific type; equivalent failures - on subsequent pages are wrapped per above. + ``ConnectError``, ``TimeoutException``) propagate unmodified + so callers can branch on the specific type; equivalent + failures on subsequent pages are wrapped per above. """ logger.debug("Requesting: %s", initial_req.url) reporter = _progress.current() - with _session(client) as sess: - resp = sess.send(initial_req) + with _client_for(client) as client: + resp = client.send(initial_req) _raise_for_non_200(resp) # Keep the original-request response as the "canonical" one for # ``md.url`` reproducibility; ``.headers`` and ``.elapsed`` get # overwritten with latest/cumulative values below. initial_response = resp - total_elapsed = resp.elapsed + total_elapsed = _safe_elapsed(resp) try: df, cursor = parse_response(resp) @@ -941,11 +970,11 @@ def _paginate( reporter.add_page(rows=len(df)) while cursor is not None: try: - resp = follow_up(cursor, sess) + resp = follow_up(cursor, client) _raise_for_non_200(resp) df, cursor = parse_response(resp) dfs.append(df) - total_elapsed += resp.elapsed + total_elapsed += _safe_elapsed(resp) if reporter is not None: reporter.set_rate_remaining( resp.headers.get(_QUOTA_HEADER), @@ -969,11 +998,27 @@ def _paginate( return pd.concat(dfs, ignore_index=True), final_response +def _ogc_parse_response( + resp: httpx.Response, *, geopd: bool +) -> tuple[pd.DataFrame, str | None]: + """Parse one OGC API page: extract the DataFrame and the next-page URL. + + Coerces falsy cursors (empty href, etc.) to ``None`` so the + paginate loop's ``while cursor is not None`` terminates instead + of spinning on a meaningless value. + """ + body = resp.json() + return ( + _get_resp_data(resp, geopd=geopd, body=body), + _next_req_url(resp, body=body) or None, + ) + + def _walk_pages( geopd: bool, - req: requests.PreparedRequest, - client: requests.Session | None = None, -) -> tuple[pd.DataFrame, requests.Response]: + req: httpx.Request, + client: httpx.Client | None = None, +) -> tuple[pd.DataFrame, httpx.Response]: """ Iterate through paginated OGC API responses and aggregate into one DataFrame. @@ -987,17 +1032,17 @@ def _walk_pages( ---------- geopd : bool Whether geopandas is installed (drives geometry handling). - req : requests.PreparedRequest + req : httpx.Request The initial HTTP request to send. - client : requests.Session, optional - Caller-borrowed session; ``None`` defers session management to + client : httpx.Client, optional + Caller-borrowed client; ``None`` defers client management to :func:`_paginate`. Returns ------- pd.DataFrame A DataFrame containing the aggregated results from all pages. - requests.Response + httpx.Response Aggregated response — initial-request URL (for query identity), final page's headers (so downstream sees current rate-limit state), and cumulative ``elapsed`` summed across pages. @@ -1006,29 +1051,19 @@ def _walk_pages( ------ RuntimeError See :func:`_paginate`. - requests.exceptions.RequestException + httpx.HTTPError See :func:`_paginate`. """ - method = req.method # ``PreparedRequest.method`` is already upper-cased. - headers = dict(req.headers) - content = req.body if method == "POST" else None + method = req.method # ``httpx.Request.method`` is already upper-cased. + headers = req.headers + content = req.content if method == "POST" else None - def parse_response(resp: requests.Response) -> tuple[pd.DataFrame, str | None]: - body = resp.json() - # Coerce falsy cursors (empty href, etc.) to None so - # _paginate's `while cursor is not None` terminates instead of - # spinning on a meaningless value. - return ( - _get_resp_data(resp, geopd=geopd, body=body), - _next_req_url(resp, body=body) or None, - ) - - def follow_up(cursor: str, sess: requests.Session) -> requests.Response: - return sess.request(method, cursor, headers=headers, data=content) + def follow_up(cursor: str, client: httpx.Client) -> httpx.Response: + return client.request(method, cursor, headers=headers, content=content) return _paginate( req, - parse_response=parse_response, + parse_response=functools.partial(_ogc_parse_response, geopd=geopd), follow_up=follow_up, client=client, ) @@ -1255,18 +1290,20 @@ def get_ogc_data( return return_list, BaseMetadata(response) -@chunking.multi_value_chunked(build_request=_construct_api_requests) +@chunking.multi_value_chunked( + build_request=_construct_api_requests, +) def _fetch_once( args: dict[str, Any], -) -> tuple[pd.DataFrame, requests.Response]: +) -> tuple[pd.DataFrame, httpx.Response]: """Send one prepared-args OGC request; return the frame + response. ``@chunking.multi_value_chunked`` models every multi-value list parameter and the cql-text filter as a chunkable axis, greedy-halves the biggest chunk across all axes until each sub-request URL fits, and iterates the cartesian product. With no chunkable inputs the - decorator passes args through unchanged. Either way the return - shape is ``(frame, response)``. + decorator passes args through unchanged. The return shape + is ``(frame, response)``. """ req = _construct_api_requests(**args) return _walk_pages(geopd=GEOPANDAS, req=req) @@ -1432,7 +1469,7 @@ def get_stats_data( args: dict[str, Any], service: str, expand_percentiles: bool, - client: requests.Session | None = None, + client: httpx.Client | None = None, ) -> tuple[pd.DataFrame, BaseMetadata]: """ Retrieves statistical data from a specified endpoint and returns it @@ -1464,27 +1501,26 @@ def get_stats_data( """ url = f"{STATISTICS_API_URL}/{service}" - request = requests.Request( + req = httpx.Request( method="GET", url=url, headers=_default_headers(), params=args, ) - req = request.prepare() - method = req.method # ``PreparedRequest.method`` is already upper-cased. - headers = dict(req.headers) + method = req.method + headers = req.headers - def parse_response(resp: requests.Response) -> tuple[pd.DataFrame, str | None]: + def parse_response(resp: httpx.Response) -> tuple[pd.DataFrame, str | None]: body = resp.json() # Coerce falsy cursors ("", 0) to None so _paginate terminates. # USGS uses "next": null at end-of-stream, but defensive coerce # protects against any "" sentinel a future schema might use. return _handle_stats_nesting(body, geopd=GEOPANDAS), body.get("next") or None - def follow_up(cursor: str, sess: requests.Session) -> requests.Response: - # Build a fresh params dict per page so the caller's ``args`` is - # never mutated. - return sess.request( + def follow_up(cursor: str, client: httpx.Client) -> httpx.Response: + # Build a fresh params dict per page so the caller's ``args`` + # is never mutated. + return client.request( method, url=url, params={**args, "next_token": cursor}, headers=headers ) diff --git a/dataretrieval/wqp.py b/dataretrieval/wqp.py index 8cfc6ca1..e874f0be 100644 --- a/dataretrieval/wqp.py +++ b/dataretrieval/wqp.py @@ -625,7 +625,7 @@ class WQP_Metadata(BaseMetadata): Response url query_time : datetme.timedelta Response elapsed time - header : requests.structures.CaseInsensitiveDict + header : httpx.Headers Response headers comments : None Metadata comments. WQP does not return comments. @@ -640,7 +640,7 @@ def __init__(self, response, **parameters) -> None: Parameters ---------- response : Response - Response object from requests module + Response object from httpx module parameters : dict Unpacked dictionary of the parameters supplied in the request diff --git a/pyproject.toml b/pyproject.toml index 5c9fbda0..65b1ae68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ "Programming Language :: Python :: 3", ] dependencies = [ - "requests", + "httpx", "pandas>=2.0.0,<4.0.0", ] dynamic = ["version"] @@ -37,7 +37,7 @@ test = [ "pytest-cov[all]", "pytest-rerunfailures", "coverage", - "requests-mock", + "pytest-httpx", "ruff", ] doc = [ diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..afbdfec2 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,32 @@ +""" +Test scaffolding for the dataretrieval test suite. + +Relaxes ``pytest-httpx``'s strict-mode flags so unconsumed mocks and +unmatched real requests don't fail the suite (matches the historical +``requests-mock``-style permissiveness the test code was written +against, and keeps mocked-URL setup terse). +""" + +from __future__ import annotations + +import pytest + + +def pytest_collection_modifyitems(config, items): + """Apply relaxed ``pytest-httpx`` strict-mode settings to every + test in the suite — matches the permissive defaults the historical + tests were written against.""" + marker = pytest.mark.httpx_mock( + assert_all_responses_were_requested=False, + assert_all_requests_were_expected=False, + can_send_already_matched_responses=True, + ) + for item in items: + item.add_marker(marker) + + +@pytest.fixture +def non_mocked_hosts() -> list[str]: + """No hosts are exempted from mocking; every HTTP call must hit + a mock registered through the ``httpx_mock`` fixture.""" + return [] diff --git a/tests/nldi_test.py b/tests/nldi_test.py index 91be5026..988d9672 100644 --- a/tests/nldi_test.py +++ b/tests/nldi_test.py @@ -18,7 +18,7 @@ def _reset_data_source_cache(monkeypatch): monkeypatch.setattr(nldi, "_AVAILABLE_DATA_SOURCES", None) -def mock_request_data_sources(requests_mock): +def mock_request_data_sources(httpx_mock): request_url = f"{NLDI_API_BASE_URL}/" available_data_sources = [ {"source": "ca_gages"}, @@ -38,42 +38,48 @@ def mock_request_data_sources(requests_mock): {"source": "WQP"}, {"source": "comid"}, ] - requests_mock.get( - request_url, json=available_data_sources, headers={"mock_header": "value"} + httpx_mock.add_response( + method="GET", + url=request_url, + json=available_data_sources, + headers={"mock_header": "value"}, ) -def mock_request(requests_mock, request_url, file_path): +def mock_request(httpx_mock, request_url, file_path): with open(file_path) as text: - requests_mock.get( - request_url, text=text.read(), headers={"mock_header": "value"} + httpx_mock.add_response( + method="GET", + url=request_url, + text=text.read(), + headers={"mock_header": "value"}, ) -def test_get_basin(requests_mock): +def test_get_basin(httpx_mock): """Tests NLDI get basin query""" request_url = ( f"{NLDI_API_BASE_URL}/WQP/USGS-054279485/basin" f"?simplified=true&splitCatchment=false" ) response_file_path = "tests/data/nldi_get_basin.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) gdf = get_basin(feature_source="WQP", feature_id="USGS-054279485") assert isinstance(gdf, GeoDataFrame) assert gdf.size == 1 -def test_get_flowlines(requests_mock): +def test_get_flowlines(httpx_mock): """Tests NLDI get flowlines query using feature source as the origin""" request_url = ( f"{NLDI_API_BASE_URL}/WQP/USGS-054279485/navigation/UM/flowlines" f"?distance=5&trimStart=false" ) response_file_path = "tests/data/nldi_get_flowlines.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) gdf = get_flowlines( feature_source="WQP", feature_id="USGS-054279485", navigation_mode="UM" @@ -82,21 +88,22 @@ def test_get_flowlines(requests_mock): assert gdf.size == 2 -def test_get_flowlines_by_comid(requests_mock): +def test_get_flowlines_by_comid(httpx_mock): """Tests NLDI get flowlines query using comid as the origin""" request_url = ( - f"{NLDI_API_BASE_URL}/comid/13294314/navigation/UM/flowlines?distance=50" + f"{NLDI_API_BASE_URL}/comid/13294314/navigation/UM/flowlines" + "?distance=50&trimStart=false" ) response_file_path = "tests/data/nldi_get_flowlines_by_comid.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) gdf = get_flowlines(navigation_mode="UM", comid=13294314, distance=50) assert isinstance(gdf, GeoDataFrame) assert gdf.size == 16 -def test_features_by_feature_source_with_navigation(requests_mock): +def test_features_by_feature_source_with_navigation(httpx_mock): """Tests NLDI get features query using feature source as the origin with navigation mode """ @@ -106,8 +113,8 @@ def test_features_by_feature_source_with_navigation(requests_mock): response_file_path = ( "tests/data/nldi_get_features_by_feature_source_with_nav_mode.json" ) - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) gdf = get_features( feature_source="WQP", @@ -120,7 +127,7 @@ def test_features_by_feature_source_with_navigation(requests_mock): assert gdf.size == 108 -def test_features_by_feature_source_without_navigation(requests_mock): +def test_features_by_feature_source_without_navigation(httpx_mock): """Tests NLDI get features query using feature source as the origin without navigation mode """ @@ -128,20 +135,20 @@ def test_features_by_feature_source_without_navigation(requests_mock): response_file_path = ( "tests/data/nldi_get_features_by_feature_source_without_nav_mode.json" ) - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) gdf = get_features(feature_source="WQP", feature_id="USGS-054279485") assert isinstance(gdf, GeoDataFrame) assert gdf.size == 10 -def test_get_features_by_comid(requests_mock): +def test_get_features_by_comid(httpx_mock): """Tests NLDI get features query using comid as the origin""" request_url = f"{NLDI_API_BASE_URL}/comid/13294314/navigation/UM/WQP?distance=5" response_file_path = "tests/data/nldi_get_features_by_comid.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) gdf = get_features( comid=13294314, data_source="WQP", navigation_mode="UM", distance=5 @@ -150,26 +157,29 @@ def test_get_features_by_comid(requests_mock): assert gdf.size == 405 -def test_get_features_by_lat_long(requests_mock): +def test_get_features_by_lat_long(httpx_mock): """Tests NLDI get features query using lat/long as the origin""" request_url = ( f"{NLDI_API_BASE_URL}/comid/position?coords=POINT%28-89.509%2043.087%29" ) response_file_path = "tests/data/nldi_get_features_by_lat_long.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) gdf = get_features(lat=43.087, long=-89.509) assert isinstance(gdf, GeoDataFrame) assert gdf.size == 6 -def test_search_for_basin(requests_mock): +def test_search_for_basin(httpx_mock): """Tests NLDI search query for basin""" - request_url = f"{NLDI_API_BASE_URL}/WQP/USGS-054279485/basin" + request_url = ( + f"{NLDI_API_BASE_URL}/WQP/USGS-054279485/basin" + "?simplified=true&splitCatchment=false" + ) response_file_path = "tests/data/nldi_get_basin.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) search_results = search( feature_source="WQP", feature_id="USGS-054279485", find="basin" @@ -180,12 +190,15 @@ def test_search_for_basin(requests_mock): assert len(search_results["features"][0]["geometry"]["coordinates"][0]) == 122 -def test_search_for_flowlines(requests_mock): +def test_search_for_flowlines(httpx_mock): """Tests NLDI search query for flowlines""" - request_url = f"{NLDI_API_BASE_URL}/WQP/USGS-054279485/navigation/UM/flowlines" + request_url = ( + f"{NLDI_API_BASE_URL}/WQP/USGS-054279485/navigation/UM/flowlines" + "?distance=50&trimStart=false" + ) response_file_path = "tests/data/nldi_get_flowlines.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) search_results = search( feature_source="WQP", @@ -199,12 +212,15 @@ def test_search_for_flowlines(requests_mock): assert len(search_results["features"][0]["geometry"]["coordinates"]) == 27 -def test_search_for_flowlines_by_comid(requests_mock): +def test_search_for_flowlines_by_comid(httpx_mock): """Tests NLDI search query for flowlines by comid""" - request_url = f"{NLDI_API_BASE_URL}/comid/13294314/navigation/UM/flowlines" + request_url = ( + f"{NLDI_API_BASE_URL}/comid/13294314/navigation/UM/flowlines" + "?distance=50&trimStart=false" + ) response_file_path = "tests/data/nldi_get_flowlines_by_comid.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) search_results = search(comid=13294314, navigation_mode="UM", find="flowlines") assert isinstance(search_results, dict) @@ -213,7 +229,7 @@ def test_search_for_flowlines_by_comid(requests_mock): assert len(search_results["features"][0]["geometry"]["coordinates"]) == 27 -def test_search_for_features_by_feature_source_with_navigation(requests_mock): +def test_search_for_features_by_feature_source_with_navigation(httpx_mock): """Tests NLDI search query for features by feature source""" request_url = ( f"{NLDI_API_BASE_URL}/WQP/USGS-054279485/navigation/UM/nwissite?distance=50" @@ -221,8 +237,8 @@ def test_search_for_features_by_feature_source_with_navigation(requests_mock): response_file_path = ( "tests/data/nldi_get_features_by_feature_source_with_nav_mode.json" ) - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) search_results = search( feature_source="WQP", @@ -237,14 +253,14 @@ def test_search_for_features_by_feature_source_with_navigation(requests_mock): assert len(search_results["features"]) == 9 -def test_search_for_features_by_feature_source_without_navigation(requests_mock): +def test_search_for_features_by_feature_source_without_navigation(httpx_mock): """Tests NLDI search query for features by feature source""" request_url = f"{NLDI_API_BASE_URL}/WQP/USGS-054279485" response_file_path = ( "tests/data/nldi_get_features_by_feature_source_without_nav_mode.json" ) - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) search_results = search( feature_source="WQP", feature_id="USGS-054279485", find="features" @@ -255,12 +271,12 @@ def test_search_for_features_by_feature_source_without_navigation(requests_mock) assert len(search_results["features"]) == 1 -def test_search_for_features_by_comid(requests_mock): +def test_search_for_features_by_comid(httpx_mock): """Tests NLDI search query for features by comid""" request_url = f"{NLDI_API_BASE_URL}/comid/13294314/navigation/UM/WQP?distance=5" response_file_path = "tests/data/nldi_get_features_by_comid.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) search_results = search( comid=13294314, @@ -275,14 +291,14 @@ def test_search_for_features_by_comid(requests_mock): assert len(search_results["features"]) == 45 -def test_search_for_features_by_lat_long(requests_mock): +def test_search_for_features_by_lat_long(httpx_mock): """Tests NLDI search query for features by lat/long""" request_url = ( f"{NLDI_API_BASE_URL}/comid/position?coords=POINT%28-89.509%2043.087%29" ) response_file_path = "tests/data/nldi_get_features_by_lat_long.json" - mock_request_data_sources(requests_mock) - mock_request(requests_mock, request_url, response_file_path) + mock_request_data_sources(httpx_mock) + mock_request(httpx_mock, request_url, response_file_path) search_results = search(lat=43.087, long=-89.509, find="features") assert isinstance(search_results, dict) @@ -291,13 +307,13 @@ def test_search_for_features_by_lat_long(requests_mock): assert len(search_results["features"][0]["geometry"]["coordinates"]) == 27 -def test_validate_data_source_rejects_invalid_after_cache_populated(requests_mock): +def test_validate_data_source_rejects_invalid_after_cache_populated(httpx_mock): """Once the cache is warm, invalid data sources must still raise ValueError. Regression: previously the validation check was nested inside the cache-population branch, so all calls after the first silently passed. """ - mock_request_data_sources(requests_mock) + mock_request_data_sources(httpx_mock) nldi._validate_data_source("WQP") @@ -323,3 +339,49 @@ def test_validate_navigation_mode_raises_value_error_for_invalid(): def test_validate_navigation_mode_normalizes_lowercase(): """Regression: lowercase values used to validate but be sent unchanged.""" assert _validate_navigation_mode("um") == "UM" + + +def test_query_nldi_non_200_surfaces_reason_phrase(httpx_mock): + """``_query_nldi`` must include the response's reason phrase in + the raised ``ValueError``. Pre-fix this crashed with + ``AttributeError: 'Response' object has no attribute 'reason'`` + because the migration to httpx renamed ``.reason`` → + ``.reason_phrase`` but missed this call site.""" + httpx_mock.add_response( + method="GET", + url=f"{NLDI_API_BASE_URL}/WQP/USGS-MISSING/basin" + "?simplified=true&splitCatchment=false", + status_code=429, + ) + mock_request_data_sources(httpx_mock) + with pytest.raises(ValueError, match="Error reason:"): + nldi.get_basin(feature_source="WQP", feature_id="USGS-MISSING") + + +def test_validate_data_source_rejects_malformed_catalog(httpx_mock, monkeypatch): + """``_validate_data_source`` should raise ``ValueError`` with an + informative message if the NLDI base URL returns a non-list shape + (or a list whose entries don't carry ``source`` keys), instead of + crashing with ``TypeError: string indices must be integers``.""" + monkeypatch.setattr(nldi, "_AVAILABLE_DATA_SOURCES", None) + httpx_mock.add_response( + method="GET", + url=f"{NLDI_API_BASE_URL}/", + json={"error": "upstream maintenance"}, + ) + with pytest.raises(ValueError, match="unexpected shape"): + nldi._validate_data_source("WQP") + + +def test_query_504_raises_value_error(httpx_mock): + """``utils.query`` must classify 504 Gateway Timeout as a 5xx + failure. Pre-fix: the membership check ``[500, 502, 503]`` missed + 504 and returned the response unchanged, leading downstream + callers (e.g. ``_query_nldi``) to silently swallow the failure as + an empty dict via JSONDecodeError.""" + from dataretrieval.utils import query + + url = "https://example.invalid/x" + httpx_mock.add_response(method="GET", url=f"{url}?a=1", status_code=504) + with pytest.raises(ValueError, match="Service Unavailable: 504"): + query(url, {"a": "1"}) diff --git a/tests/nwis_test.py b/tests/nwis_test.py index a3b23da6..f343f26e 100644 --- a/tests/nwis_test.py +++ b/tests/nwis_test.py @@ -1,5 +1,6 @@ import datetime import json +import re import warnings from pathlib import Path from unittest import mock @@ -37,7 +38,7 @@ def _load_mock_json(file_name): return json.load(f) -def _test_iv_service(requests_mock): +def _test_iv_service(httpx_mock): """Mocked test of instantaneous value service""" start = START_DATE end = END_DATE @@ -48,17 +49,17 @@ def _test_iv_service(requests_mock): mock_json = _load_mock_json("nwis_iv_mock.json") # Match the base URL and ensure query parameters are correct - requests_mock.get( - "https://waterservices.usgs.gov/nwis/iv", + httpx_mock.add_response( + method="GET", + url=re.compile(r"^https://waterservices\.usgs\.gov/nwis/iv(\?.*)?$"), json=mock_json, - complete_qs=False, ) return get_record(site, start, end, service=service) -def test_iv_service_answer(requests_mock): - df = _test_iv_service(requests_mock) +def test_iv_service_answer(httpx_mock): + df = _test_iv_service(httpx_mock) # check multiindex function assert df.index.names == [ SITENO_COL, @@ -152,23 +153,25 @@ def test_warn_message_includes_replacement(self, func_name, replacement_substrin assert replacement_substring in message assert _NWIS_REMOVAL_DATE in message - def test_get_iv_fires_deprecation_on_call(self, requests_mock): + def test_get_iv_fires_deprecation_on_call(self, httpx_mock): """End-to-end: a real call routes through _warn_deprecated.""" - requests_mock.get( - "https://waterservices.usgs.gov/nwis/iv", + httpx_mock.add_response( + method="GET", + url=re.compile(r"^https://waterservices\.usgs\.gov/nwis/iv(\?.*)?$"), json={"value": {"timeSeries": []}}, ) with pytest.warns(DeprecationWarning, match="get_iv.*waterdata.get_continuous"): get_iv(sites="01491000") - def test_nested_calls_emit_one_warning(self, requests_mock): + def test_nested_calls_emit_one_warning(self, httpx_mock): """get_record(service='iv') wraps get_iv -> query_waterservices. Without re-entrancy suppression the user would see 3 near-identical deprecation warnings for one call; pin the outermost-only contract. """ - requests_mock.get( - "https://waterservices.usgs.gov/nwis/iv", + httpx_mock.add_response( + method="GET", + url=re.compile(r"^https://waterservices\.usgs\.gov/nwis/iv(\?.*)?$"), json={"value": {"timeSeries": []}}, ) with warnings.catch_warnings(record=True) as caught: @@ -318,7 +321,7 @@ def test_expandedrdb_get_info(self): assert "count_nu" not in data.columns -def test_empty_timeseries(requests_mock): +def test_empty_timeseries(httpx_mock): """Test based on empty case from GitHub Issue #26.""" sites = "011277906" start = "2010-07-20" @@ -326,10 +329,10 @@ def test_empty_timeseries(requests_mock): mock_json = _load_mock_json("nwis_iv_empty_mock.json") # Match the base URL and ensure query parameters are correct - requests_mock.get( - "https://waterservices.usgs.gov/nwis/iv", + httpx_mock.add_response( + method="GET", + url=re.compile(r"^https://waterservices\.usgs\.gov/nwis/iv(\?.*)?$"), json=mock_json, - complete_qs=False, ) df = get_record(sites=sites, service="iv", start=start, end=end) diff --git a/tests/utils_test.py b/tests/utils_test.py index 2c350b2b..c25e1084 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -19,12 +19,12 @@ def test_url_too_long(self): or abruptly close the connection (ConnectionError). Both are valid responses to an excessively long URL. """ - import requests as req + import httpx # all sites in MD sites, _ = nwis.what_sites(stateCd="MD") # raise error by trying to query them all, so URL is way too long - with pytest.raises((ValueError, req.exceptions.ConnectionError)): + with pytest.raises((ValueError, httpx.ConnectError)): nwis.get_iv(sites=sites.site_no.values.tolist()) def test_header(self): diff --git a/tests/waterdata_chunking_test.py b/tests/waterdata_chunking_test.py index d9a54a7d..21b23757 100644 --- a/tests/waterdata_chunking_test.py +++ b/tests/waterdata_chunking_test.py @@ -35,30 +35,35 @@ ChunkPlan, QuotaExhausted, RateLimited, - RequestExceedsQuota, RequestTooLarge, ServiceInterrupted, ServiceUnavailable, - _chunked_session, + _chunked_client, _extract_axes, - _read_remaining, multi_value_chunked, ) from dataretrieval.waterdata.utils import _construct_api_requests class _FakeReq: - __slots__ = ("url", "body") + """Stand-in for ``httpx.Request`` whose ``_request_bytes`` shape + is ``len(str(url)) + len(content)``.""" - def __init__(self, url, body=None): + __slots__ = ("url", "content") + + def __init__(self, url, content=b""): self.url = url - self.body = body + self.content = ( + content + if isinstance(content, (bytes, bytearray)) + else (content.encode("utf-8") if isinstance(content, str) else b"") + ) def _fake_build(*, base=200, **kwargs): """Fake build_request: URL length deterministic in its inputs. - Mirrors the GET-routed shape: payload goes in the URL, body is None. + Mirrors the GET-routed shape: payload goes in the URL, body is empty. List/string values are URL-encoded via ``quote_plus`` so the fake's byte count matches what the real ``_construct_api_requests`` would produce; otherwise an alphanumeric test could pass against the fake @@ -234,33 +239,6 @@ def fetch(args): assert calls[0]["monitoring_location_id"] == ["A", "B"] -def test_multi_value_chunked_emits_cartesian_product(): - """Two chunkable axes, each split into 2 chunks → exactly 4 sub-requests, - each pairing one chunk from each axis.""" - calls = [] - - @multi_value_chunked(build_request=_fake_build, url_limit=240) - def fetch(args): - calls.append({k: v for k, v in args.items() if k in ("sites", "pcodes")}) - return pd.DataFrame(), mock.Mock( - elapsed=datetime.timedelta(seconds=0.1), headers={} - ) - - fetch( - { - "sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10], - "pcodes": ["P1" * 10, "P2" * 10, "P3" * 10, "P4" * 10], - } - ) - # Both heavy → planner should split both axes. Confirm a cartesian shape: - # every unique site-chunk pairs with every unique pcode-chunk. - sites_seen = {tuple(c["sites"]) for c in calls} - pcodes_seen = {tuple(c["pcodes"]) for c in calls} - assert len(calls) == len(sites_seen) * len(pcodes_seen) - assert len(sites_seen) > 1 - assert len(pcodes_seen) > 1 - - def test_multi_value_chunked_emits_3d_cartesian_product(): """Three chunkable axes, each forced to split → exhaustive cartesian product across all three. Verifies the halving loop in @@ -332,20 +310,20 @@ def fetch(args): def test_chunked_session_shared_across_sub_requests(): """Every sub-request of one chunked call sees the same - ``requests.Session`` on the ``_chunked_session`` ContextVar, so + ``httpx.Client`` on the ``_chunked_client`` ContextVar, so downstream paginated helpers (``_walk_pages``) can reuse the connection pool instead of handshaking fresh on each sub-request.""" sessions_seen = [] @multi_value_chunked(build_request=_fake_build, url_limit=240) def fetch(args): - sessions_seen.append(_chunked_session.get()) + sessions_seen.append(_chunked_client.get()) return pd.DataFrame(), mock.Mock( elapsed=datetime.timedelta(seconds=0.1), headers={} ) # Outside a chunked call: no session published. - assert _chunked_session.get() is None + assert _chunked_client.get() is None fetch({"sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10]}) @@ -357,7 +335,7 @@ def fetch(args): # And it was the same object every time. assert len({id(s) for s in sessions_seen}) == 1 # On exit the ContextVar is reset to its default. - assert _chunked_session.get() is None + assert _chunked_client.get() is None def test_chunked_session_isolated_per_resume(): @@ -385,107 +363,22 @@ def fetch(args): fetch({"sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10]}) # First resume's session is closed; ContextVar is reset. - assert _chunked_session.get() is None + assert _chunked_client.get() is None state["blow_up"] = False excinfo.value.call.resume() # Second resume's session is also cleaned up. - assert _chunked_session.get() is None + assert _chunked_client.get() is None def _quota_response(remaining: int | str | None) -> mock.Mock: - """A mock requests.Response-like object whose ``x-ratelimit-remaining`` + """A mock httpx.Response-like object whose ``x-ratelimit-remaining`` header reflects the given value (None → header absent).""" resp = mock.Mock(elapsed=datetime.timedelta(seconds=0.1)) resp.headers = {} if remaining is None else {_QUOTA_HEADER: str(remaining)} return resp -def test_read_remaining_parses_header(): - assert _read_remaining(_quota_response(42)) == 42 - - -def test_read_remaining_returns_none_when_header_missing(): - """No rate-limit header → ``None`` so ``ChunkedCall`` can branch - on ``is None`` instead of comparing against a magic sentinel.""" - assert _read_remaining(_quota_response(None)) is None - - -def test_read_remaining_returns_none_on_malformed_header(): - """Non-integer header value → ``None`` so a parse failure doesn't - trip the quota check.""" - assert _read_remaining(_quota_response("not-a-number")) is None - - -def test_request_exceeds_quota_after_first_chunk(): - """Plan totals 4 sub-requests. The first response reports - ``x-ratelimit-remaining=1`` — only 2 sub-requests fit total - (the one just issued + 1 more). The wrapper must raise - ``RequestExceedsQuota`` *before* issuing chunk 2, and the - exception must carry a ``.call`` handle so the first chunk's - already-fetched data is recoverable.""" - calls: list[dict] = [] - - def fetch(args): - calls.append(args) - return pd.DataFrame({"sites": list(args["sites"])}), _quota_response(1) - - decorated = multi_value_chunked(build_request=_fake_build, url_limit=240)(fetch) - - with pytest.raises(RequestExceedsQuota) as excinfo: - decorated({"sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10]}) - - err = excinfo.value - assert err.planned_chunks == 4 - assert err.available == 2 # remaining=1 + the chunk we just spent - assert err.deficit == 2 - assert len(calls) == 1, "only the first chunk should have been issued" - # The originating ChunkedCall is exposed on .call so the first - # chunk's already-fetched data is recoverable. - assert err.call is not None - assert err.call.completed_chunks == 1 - assert not err.call.partial_frame.empty - - -def test_request_exceeds_quota_message_reports_deficit(): - """The error must surface planned / available / deficit so callers - know precisely how far over budget the call is.""" - e = RequestExceedsQuota(planned_chunks=10, available=4, deficit=6) - msg = str(e) - assert "10" in msg - assert "4" in msg - assert "6" in msg - - -def test_request_exceeds_quota_not_raised_when_plan_fits(): - """If ``x-ratelimit-remaining`` is large enough to cover the rest - of the plan, ``ChunkedCall`` proceeds normally.""" - remaining_seq = iter([100, 99, 98, 97]) - - def fetch(args): - return ( - pd.DataFrame({"sites": list(args["sites"])}), - _quota_response(next(remaining_seq)), - ) - - decorated = multi_value_chunked(build_request=_fake_build, url_limit=240)(fetch) - df, _ = decorated({"sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10]}) - assert len(df) == 4 - - -def test_no_quota_check_when_header_absent(): - """Without an ``x-ratelimit-remaining`` header ``ChunkedCall`` - has no quota signal and must NOT synthesize a - ``RequestExceedsQuota``; every planned sub-request runs.""" - - def fetch(args): - return pd.DataFrame({"sites": list(args["sites"])}), _quota_response(None) - - decorated = multi_value_chunked(build_request=_fake_build, url_limit=240)(fetch) - df, _ = decorated({"sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10]}) - assert len(df) == 4 - - def test_quota_exhausted_on_mid_call_429(): """Mid-call 429 (a concurrent caller drained the window) surfaces as ``QuotaExhausted`` carrying the partial frame plus the chunk @@ -757,13 +650,13 @@ def test_chunk_interrupted_base_class_catches_both(): def test_connection_error_wrapped_as_service_interrupted(): - """A bare ``requests.exceptions.ConnectionError`` (or any other - transport-level RequestException) doesn't inherit from - ``RuntimeError``; without the widened catch in ``_issue`` it - would escape uncaught and the user would lose the resumable - handle to ``.call.resume()``. Verify ``ChunkedCall`` wraps it as - ``ServiceInterrupted`` so partial progress is preserved.""" - import requests as _requests + """A bare ``httpx.ConnectError`` (or any other transport-level + ``httpx.HTTPError``) doesn't inherit from ``RuntimeError``; + without the widened catch in ``_issue`` it would escape uncaught + and the user would lose the resumable handle to ``.call.resume()``. + Verify ``ChunkedCall`` wraps it as ``ServiceInterrupted`` so + partial progress is preserved.""" + import httpx as _httpx state = {"i": 0, "blow_up": True} @@ -771,7 +664,7 @@ def fetch(args): i = state["i"] state["i"] += 1 if i == 2 and state["blow_up"]: - raise _requests.exceptions.ConnectionError("connection reset") + raise _httpx.ConnectError("connection reset") return ( pd.DataFrame({"sites": list(args["sites"])}), _quota_response(500), @@ -785,13 +678,51 @@ def fetch(args): assert err.completed_chunks == 2 assert err.call is not None # The transport exception is on __cause__ so callers can drill in if needed. - assert isinstance(err.__cause__, _requests.exceptions.ConnectionError) + assert isinstance(err.__cause__, _httpx.ConnectError) # Resume after the upstream recovers. state["blow_up"] = False df, _ = err.call.resume() assert set(df["sites"]) == {"S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10, "S5" * 10} +def test_invalid_url_wrapped_as_service_interrupted(): + """``httpx.InvalidURL`` inherits from ``Exception``, NOT from + ``httpx.HTTPError``. Without the widened catch in ``_issue`` / + ``_classify_chunk_error`` an oversize follow-up URL escapes as + raw ``InvalidURL`` and the user loses ``.call.resume()`` access + to the partial state. Mirror the ConnectError test.""" + import httpx as _httpx + + state = {"i": 0, "blow_up": True} + + def fetch(args): + i = state["i"] + state["i"] += 1 + if i == 2 and state["blow_up"]: + raise _httpx.InvalidURL("URL is too long: 65536 bytes > 65000") + return ( + pd.DataFrame({"sites": list(args["sites"])}), + _quota_response(500), + ) + + decorated = multi_value_chunked(build_request=_fake_build, url_limit=240)(fetch) + with pytest.raises(ServiceInterrupted) as excinfo: + decorated({"sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10, "S5" * 10]}) + + err = excinfo.value + assert err.completed_chunks == 2 + assert err.call is not None + assert isinstance(err.__cause__, _httpx.InvalidURL) + # The top-level message must surface the underlying cause text so + # the user doesn't have to traverse ``__cause__`` to know what + # actually failed (previously the message was generic "Service + # error after K/N sub-requests; ... resume() once the upstream + # recovers", with the real "URL too long" only visible via + # ``.__cause__``). + assert "InvalidURL" in str(err) + assert "URL is too long" in str(err) + + def test_service_interrupted_exposes_partial_frame_and_response(): """Both ``QuotaExhausted`` AND ``ServiceInterrupted`` carry ``partial_frame`` / ``partial_response`` directly on the @@ -899,9 +830,9 @@ def fetch(args): def test_combine_chunk_responses_returns_independent_headers(): """The aggregated response's ``.headers`` must be a fresh - ``CaseInsensitiveDict`` — mutations by downstream callers - (logging hooks, metadata extensions) must not back-propagate into - the underlying chunk response's headers, which still live on + ``httpx.Headers`` — mutations by downstream callers (logging + hooks, metadata extensions) must not back-propagate into the + underlying chunk response's headers, which still live on ``ChunkedCall._chunks``.""" from dataretrieval.waterdata.chunking import _combine_chunk_responses @@ -930,7 +861,7 @@ def test_paginate_terminates_on_empty_string_cursor(): import datetime as _dt from unittest import mock as _mock - import requests as _requests + import httpx as _httpx from dataretrieval.waterdata import utils as _utils @@ -942,20 +873,20 @@ def test_paginate_terminates_on_empty_string_cursor(): "features": [{"id": "1", "properties": {"val": "a"}}], "links": [{"rel": "next", "href": ""}], } - resp = _mock.MagicMock(spec=_requests.Response) + resp = _mock.MagicMock(spec=_httpx.Response) resp.status_code = 200 resp.url = "https://example.com/items?limit=1" resp.elapsed = _dt.timedelta(seconds=0.1) resp.headers = {} resp.json.return_value = body_with_empty_next - client = _mock.MagicMock(spec=_requests.Session) + client = _mock.MagicMock(spec=_httpx.Client) client.send.return_value = resp - req = _mock.MagicMock(spec=_requests.PreparedRequest) + req = _mock.MagicMock(spec=_httpx.Request) req.method = "GET" req.headers = {} - req.body = None + req.content = b"" req.url = "https://example.com/items?limit=1" df, final = _utils._walk_pages(geopd=False, req=req, client=client) @@ -1036,35 +967,71 @@ def test_quota_exhausted_message_points_at_resume(): assert ".call.resume()" in msg -def test_request_bytes_rejects_non_sizable_body(): - """``_request_bytes`` requires a deterministic byte count up front; - silently treating an unknown body as zero would under-chunk and let - the request blow past the server's POST-body limit. Generators, - iterables, and file-like objects must surface as ``TypeError``.""" +def test_request_bytes_sums_url_and_content(): + """``_request_bytes`` returns ``len(str(url)) + len(content)``. + + ``httpx.Request`` always carries ``.content`` as ``bytes`` (the + constructor normalises ``data``/``json``/``content`` inputs), so + the chunker just needs to size that single attribute alongside + the URL. + """ + import httpx + from dataretrieval.waterdata.chunking import _request_bytes - class _FakeReqWithGenBody: - url = "https://example.com/foo" - body = (b"x" for _ in range(3)) + # GET request with no body + req = httpx.Request("GET", "https://x.example/ab") + assert _request_bytes(req) == len("https://x.example/ab") - with pytest.raises(TypeError, match="cannot size a request body"): - _request_bytes(_FakeReqWithGenBody()) + # POST request with content + req = httpx.Request("POST", "https://x.example/ab", content=b"cd") + assert _request_bytes(req) == len("https://x.example/ab") + 2 -def test_request_bytes_handles_supported_body_types(): - """Sanity-check the supported body types: None (GET), bytes (raw - POST), str (JSON-as-string POST).""" - from dataretrieval.waterdata.chunking import _request_bytes +def test_safe_request_bytes_treats_invalid_url_as_overflow(): + """``httpx.URL`` enforces a 64 KB cap per URL component and raises + ``httpx.InvalidURL`` for anything bigger — e.g. comma-joining all + California stream sites in one query. The planner's halving loop + must keep shrinking past that cap rather than crashing; the + contract is that ``_safe_request_bytes`` returns ``url_limit + 1`` + (a value strictly greater than the limit) when ``build_request`` + raises ``InvalidURL``.""" + import httpx + + from dataretrieval.waterdata.chunking import _safe_request_bytes + + def build_request(**kwargs): + raise httpx.InvalidURL("URL too long") + + url_limit = 8000 + assert _safe_request_bytes(build_request, {}, url_limit) == url_limit + 1 + + +def test_chunk_plan_handles_initial_url_overflow(): + """A user query whose unchunked URL exceeds the 64 KB + ``httpx.URL`` cap (e.g. 5000+ site IDs comma-joined) must not + crash ``ChunkPlan.__init__``; the planner falls back to a + worst-case sub-request URL for ``canonical_url`` and proceeds to + halve the over-limit axes normally.""" + import httpx + + real_build = _fake_build - class _Req: - def __init__(self, url, body): - self.url = url - self.body = body + def overflowing_build(**kwargs): + # Mimic httpx: any single sub-arg whose ``sites`` list has + # more than 2 entries fails URL construction (proxy for a + # 64 KB overflow at the worst case). + if len(kwargs.get("sites", [])) > 2: + raise httpx.InvalidURL("URL > 64 KB") + return real_build(**kwargs) - assert _request_bytes(_Req("ab", None)) == 2 - assert _request_bytes(_Req("ab", b"cd")) == 4 - assert _request_bytes(_Req("ab", "cd")) == 4 - assert _request_bytes(_Req("ab", bytearray(b"cd"))) == 4 + sites = ["S" * 10 + str(i) for i in range(8)] + plan = ChunkPlan({"sites": sites}, overflowing_build, url_limit=8000) + # Planner kept halving until every worst-case sub-arg had ≤2 sites. + assert all(len(c) <= 2 for c in plan.chunks["sites"]) + assert plan.total > 1 + # canonical_url fell back to a constructable worst-case URL. + assert plan.canonical_url is not None def test_multi_value_chunked_restores_canonical_url(): @@ -1169,7 +1136,7 @@ def test_joint_planner_url_construction_long_filter_and_long_sites(): over_limit = [] for sub_args in plan.iter_sub_args(): req = _construct_api_requests(**sub_args) - url_len = len(req.url) + (len(req.body) if req.body else 0) + url_len = len(str(req.url)) + len(req.content) if url_len > url_limit: over_limit.append((url_len, sub_args)) assert not over_limit, ( @@ -1194,11 +1161,9 @@ def test_joint_planner_url_construction_long_filter_and_long_sites(): def test_combine_chunk_frames_all_empty_preserves_geo_type(): - """Regression: when every chunk returns an empty frame, - ``_combine_chunk_frames`` must not downgrade an empty - ``GeoDataFrame`` to a plain ``DataFrame``. The whole reason the - function drops empties before concat is to prevent that downgrade - — the all-empty short-circuit was independently dropping it.""" + """An all-empty chunk list preserves the ``GeoDataFrame`` type. + Dropping empties before concat exists precisely to prevent type + downgrade; the all-empty branch must honor the same contract.""" pytest.importorskip("geopandas") import geopandas as gpd @@ -1212,10 +1177,10 @@ def test_combine_chunk_frames_all_empty_preserves_geo_type(): def test_combine_chunk_frames_single_frame_is_safe_to_mutate(): - """Regression: the single-completed-chunk fast path returned the - underlying chunk frame verbatim, so a caller mutating - ``call.partial_frame`` (documented as a live view) would mutate - ``_chunks[0][0]`` in place. The fast path now returns a copy.""" + """``_combine_chunk_frames`` returns a frame independent of its + input on the single-chunk fast path — a caller mutating + ``call.partial_frame`` (a live view) must not back-propagate into + the underlying ``_chunks[0][0]`` frame.""" from dataretrieval.waterdata.chunking import _combine_chunk_frames chunk = pd.DataFrame({"id": ["A", "B"], "value": [1, 2]}) @@ -1225,10 +1190,9 @@ def test_combine_chunk_frames_single_frame_is_safe_to_mutate(): def test_iter_sub_args_passthrough_yields_a_copy(): - """Regression: the no-axes passthrough yielded ``self.args`` - directly while the chunked branch did ``dict(self.args)``. A - ``fetch_once`` that mutated the dict it received would silently - corrupt ``ChunkPlan.args``. The passthrough now copies too.""" + """``ChunkPlan.iter_sub_args`` yields a fresh dict on every path + (passthrough and chunked), so a ``fetch_once`` that mutates the + dict it receives cannot corrupt ``ChunkPlan.args``.""" args = {"monitoring_location_id": ["USGS-A"], "limit": 100} plan = ChunkPlan(args, _fake_build, url_limit=8000) sub = next(plan.iter_sub_args()) @@ -1238,34 +1202,31 @@ def test_iter_sub_args_passthrough_yields_a_copy(): assert "new_key" not in plan.args -def test_quota_check_fires_after_every_chunk_not_just_first(): - """Regression: ``_check_quota_after_first`` was gated on - ``len(_chunks) == 1`` so it only fired after chunk 0; a concurrent - caller draining the window mid-call (or a partially-rolled-over - quota on resume) went undetected. The check now fires after every - non-final chunk.""" - # 4-chunk plan. Chunks 0 and 1 report plenty of remaining quota; - # chunk 2's response reports remaining=0 with one chunk still - # pending. The check must fire after chunk 2, NOT silently let - # chunk 3 hit a mid-stream 429. - responses = iter([500, 500, 0]) - calls: list[dict] = [] +def test_combine_chunk_responses_does_not_mutate_input_urls(): + """Regression for the _set_response_url aliasing bug. - def fetch(args): - calls.append(args) - return pd.DataFrame({"sites": list(args["sites"])}), _quota_response( - next(responses) - ) + ``_combine_chunk_responses`` shallow-copies the first response; + if the canonical-URL override is applied by mutating the bound + ``request.url``, the shallow alias back-propagates the URL change + into the underlying chunk-0 response — breaking the documented + 'input responses are not mutated' invariant. The fix is to swap + in a fresh ``httpx.Request`` rather than mutate the existing one. + """ + import httpx as _httpx - decorated = multi_value_chunked(build_request=_fake_build, url_limit=240)(fetch) - with pytest.raises(RequestExceedsQuota) as excinfo: - decorated({"sites": ["S1" * 10, "S2" * 10, "S3" * 10, "S4" * 10]}) - err = excinfo.value - assert err.planned_chunks == 4 - # 3 completed + 0 remaining = 3 available; 1 pending; deficit 1. - assert err.available == 3 - assert err.deficit == 1 - assert len(calls) == 3, "only chunks 0-2 should have been issued" - # .call carries the in-flight call so the user can recover. - assert err.call is not None - assert err.call.completed_chunks == 3 + from dataretrieval.waterdata.chunking import _combine_chunk_responses + + req1 = _httpx.Request("GET", "https://example.com/chunk0") + req2 = _httpx.Request("GET", "https://example.com/chunk1") + r1 = _httpx.Response(200, request=req1) + r2 = _httpx.Response(200, request=req2) + + out = _combine_chunk_responses( + [r1, r2], canonical_url="https://canonical.example/full" + ) + assert str(out.url) == "https://canonical.example/full" + # The inputs and their bound requests must be untouched. + assert str(r1.url) == "https://example.com/chunk0" + assert str(r2.url) == "https://example.com/chunk1" + assert str(req1.url) == "https://example.com/chunk0" + assert str(req2.url) == "https://example.com/chunk1" diff --git a/tests/waterdata_filters_test.py b/tests/waterdata_filters_test.py index 9d9d183e..32879318 100644 --- a/tests/waterdata_filters_test.py +++ b/tests/waterdata_filters_test.py @@ -14,7 +14,7 @@ def _query_params(prepared_request): - return parse_qs(urlsplit(prepared_request.url).query) + return parse_qs(urlsplit(str(prepared_request.url)).query) def _fake_prepared_request(url="https://example.test"): diff --git a/tests/waterdata_progress_test.py b/tests/waterdata_progress_test.py index 14a98839..faa61630 100644 --- a/tests/waterdata_progress_test.py +++ b/tests/waterdata_progress_test.py @@ -11,8 +11,8 @@ import types from unittest import mock +import httpx import pytest -import requests from dataretrieval.waterdata import _progress from dataretrieval.waterdata._progress import ( @@ -305,11 +305,11 @@ def test_walk_pages_reports_pages_and_rate_limit(): ) resp2 = _resp([{"id": "2", "properties": {"v": "b"}}], rate_remaining="4998") - client = mock.MagicMock(spec=requests.Session) + client = mock.MagicMock(spec=httpx.Client) client.send.return_value = resp1 client.request.return_value = resp2 - req = mock.MagicMock(spec=requests.PreparedRequest) + req = mock.MagicMock(spec=httpx.Request) req.method = "GET" req.headers = {} req.url = "https://example.com/p1" @@ -330,10 +330,10 @@ def test_walk_pages_reports_pages_and_rate_limit(): def test_walk_pages_without_context_does_not_error(): # No active reporter: pagination must still work and stay silent. resp = _resp([{"id": "1", "properties": {"v": "a"}}]) - client = mock.MagicMock(spec=requests.Session) + client = mock.MagicMock(spec=httpx.Client) client.send.return_value = resp - req = mock.MagicMock(spec=requests.PreparedRequest) + req = mock.MagicMock(spec=httpx.Request) req.method = "GET" req.headers = {} req.url = "https://example.com/p1" @@ -350,11 +350,11 @@ def test_broken_progress_stream_does_not_truncate_pagination(): [{"id": "1", "properties": {"v": "a"}}], next_url="https://example.com/p2" ) resp2 = _resp([{"id": "2", "properties": {"v": "b"}}]) - client = mock.MagicMock(spec=requests.Session) + client = mock.MagicMock(spec=httpx.Client) client.send.return_value = resp1 client.request.return_value = resp2 - req = mock.MagicMock(spec=requests.PreparedRequest) + req = mock.MagicMock(spec=httpx.Request) req.method = "GET" req.headers = {} req.url = "https://example.com/p1" diff --git a/tests/waterdata_ratings_test.py b/tests/waterdata_ratings_test.py index fcead65d..9cbb4b70 100644 --- a/tests/waterdata_ratings_test.py +++ b/tests/waterdata_ratings_test.py @@ -1,3 +1,4 @@ +import re import sys from urllib.parse import parse_qs, urlsplit @@ -10,6 +11,15 @@ from dataretrieval.waterdata import get_ratings from dataretrieval.waterdata.ratings import _build_filter +# pytest-httpx matches URL strings exactly (including query). For the +# ratings tests we want a "match this endpoint, ignore the params" +# fixture so the assertions can drill into the captured params +# afterwards without coupling the registration to the implementation's +# parameter order. ``url=STAC_SEARCH_RE`` does that. +STAC_SEARCH_RE = re.compile( + r"^https://api\.waterdata\.usgs\.gov/stac/v0/search(\?.*)?$" +) + def test_build_filter_single_site_single_type(): f = _build_filter("USGS-01104475", "exsa") @@ -77,14 +87,16 @@ def _stub_search_response(): } -def test_get_ratings_mocked_search_and_download(requests_mock, tmp_path): +def test_get_ratings_mocked_search_and_download(httpx_mock, tmp_path): """End-to-end happy path with mocked STAC search + RDB download.""" - requests_mock.get( - "https://api.waterdata.usgs.gov/stac/v0/search", + httpx_mock.add_response( + method="GET", + url=STAC_SEARCH_RE, json=_stub_search_response(), ) - requests_mock.get( - "https://api.waterdata.usgs.gov/stac-files/ratings/USGS.01104475.exsa.rdb", + httpx_mock.add_response( + method="GET", + url="https://api.waterdata.usgs.gov/stac-files/ratings/USGS.01104475.exsa.rdb", text=_SAMPLE_RDB, ) @@ -100,22 +112,23 @@ def test_get_ratings_mocked_search_and_download(requests_mock, tmp_path): assert len(df) == 3 # Server-side filter should pin the single requested file_type. - sent = requests_mock.request_history[0] - qs = parse_qs(urlsplit(sent.url).query) + sent = httpx_mock.get_requests()[0] + qs = parse_qs(urlsplit(str(sent.url)).query) assert "file_type = 'exsa'" in qs["filter"][0] assert "monitoring_location_id IN ('USGS-01104475')" in qs["filter"][0] -def test_get_ratings_attaches_rdb_comment_and_url(requests_mock, tmp_path): +def test_get_ratings_attaches_rdb_comment_and_url(httpx_mock, tmp_path): """Each parsed frame should carry its RDB header + source URL in df.attrs.""" - requests_mock.get( - "https://api.waterdata.usgs.gov/stac/v0/search", + httpx_mock.add_response( + method="GET", + url=STAC_SEARCH_RE, json=_stub_search_response(), ) asset_url = ( "https://api.waterdata.usgs.gov/stac-files/ratings/USGS.01104475.exsa.rdb" ) - requests_mock.get(asset_url, text=_SAMPLE_RDB) + httpx_mock.add_response(method="GET", url=asset_url, text=_SAMPLE_RDB) out = get_ratings( monitoring_location_id="USGS-01104475", @@ -131,9 +144,10 @@ def test_get_ratings_attaches_rdb_comment_and_url(requests_mock, tmp_path): assert df.attrs["url"] == asset_url -def test_get_ratings_download_and_parse_false_returns_features(requests_mock): - requests_mock.get( - "https://api.waterdata.usgs.gov/stac/v0/search", +def test_get_ratings_download_and_parse_false_returns_features(httpx_mock): + httpx_mock.add_response( + method="GET", + url=STAC_SEARCH_RE, json=_stub_search_response(), ) features = get_ratings( @@ -144,10 +158,11 @@ def test_get_ratings_download_and_parse_false_returns_features(requests_mock): assert features[0]["id"] == "USGS-01104475.exsa.rdb" -def test_get_ratings_multi_type_filters_via_property(requests_mock, tmp_path): +def test_get_ratings_multi_type_filters_via_property(httpx_mock, tmp_path): """File_type list: server filter omits it; local filter reads the property.""" - requests_mock.get( - "https://api.waterdata.usgs.gov/stac/v0/search", + httpx_mock.add_response( + method="GET", + url=STAC_SEARCH_RE, json={ "features": [ { @@ -169,8 +184,12 @@ def test_get_ratings_multi_type_filters_via_property(requests_mock, tmp_path): }, ) # Only mock the two URLs we expect to be downloaded. - requests_mock.get("https://x.example/X.exsa.rdb", text=_SAMPLE_RDB) - requests_mock.get("https://x.example/X.corr.rdb", text=_SAMPLE_RDB) + httpx_mock.add_response( + method="GET", url="https://x.example/X.exsa.rdb", text=_SAMPLE_RDB + ) + httpx_mock.add_response( + method="GET", url="https://x.example/X.corr.rdb", text=_SAMPLE_RDB + ) out = get_ratings( monitoring_location_id="USGS-X", @@ -180,6 +199,6 @@ def test_get_ratings_multi_type_filters_via_property(requests_mock, tmp_path): assert set(out) == {"USGS-X.exsa.rdb", "USGS-X.corr.rdb"} # Server-side filter must NOT include file_type for multi-type requests. - search_req = requests_mock.request_history[0] - qs = parse_qs(urlsplit(search_req.url).query) + search_req = httpx_mock.get_requests()[0] + qs = parse_qs(urlsplit(str(search_req.url)).query) assert "file_type" not in qs["filter"][0] diff --git a/tests/waterdata_test.py b/tests/waterdata_test.py index 24eb6eff..09f66aa5 100644 --- a/tests/waterdata_test.py +++ b/tests/waterdata_test.py @@ -44,27 +44,30 @@ # try. The marker is attached to every test in the module, but the # patterns match only traces produced by real network round-trips # (``_raise_for_non_200`` output, ``requests`` exceptions), so tests -# using ``requests_mock`` or ``mock.patch`` are no-ops for the rerun. +# using ``httpx_mock`` or ``mock.patch`` are no-ops for the rerun. pytestmark = pytest.mark.flaky( reruns=2, reruns_delay=5, only_rerun=[ r"(?:RateLimited|RuntimeError):\s*(?:429|5\d\d):", # _raise_for_non_200 output - r"ConnectionError", + r"Connect(ion)?Error", # requests' ConnectionError + httpx' ConnectError r"ReadTimeout|ConnectTimeout|Timeout", ], ) -def mock_request(requests_mock, request_url, file_path): +def mock_request(httpx_mock, request_url, file_path): """Mock request code""" with open(file_path) as text: - requests_mock.get( - request_url, text=text.read(), headers={"mock_header": "value"} + httpx_mock.add_response( + method="GET", + url=request_url, + text=text.read(), + headers={"mock_header": "value"}, ) -def test_mock_get_samples(requests_mock): +def test_mock_get_samples(httpx_mock): """Tests USGS Samples query""" request_url = ( "https://api.waterdata.usgs.gov/samples-data/results/fullphyschem?" @@ -72,7 +75,7 @@ def test_mock_get_samples(requests_mock): "&activityStartDateUpper=2024-12-31&monitoringLocationIdentifier=USGS-05406500&mimeType=text%2Fcsv" ) response_file_path = "tests/data/samples_results.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_samples( service="results", profile="fullphyschem", @@ -86,19 +89,19 @@ def test_mock_get_samples(requests_mock): assert df.shape == (67, 187) assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None assert df["Activity_StartDateTime"].notna().any() -def test_mock_get_samples_summary(requests_mock): +def test_mock_get_samples_summary(httpx_mock): """Tests USGS Samples summary query""" request_url = ( "https://api.waterdata.usgs.gov/samples-data/summary/USGS-04183500" "?mimeType=text%2Fcsv" ) response_file_path = "tests/data/samples_summary.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_samples_summary(monitoringLocationIdentifier="USGS-04183500") assert type(df) is DataFrame expected_columns = { @@ -115,7 +118,7 @@ def test_mock_get_samples_summary(requests_mock): assert (df["monitoringLocationIdentifier"] == "USGS-04183500").all() assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None @@ -141,8 +144,8 @@ def test_construct_api_requests_multivalue_get(): parameter_code=["00060", "00065"], ) assert req.method == "GET" - assert "monitoring_location_id=USGS-05427718%2CUSGS-05427719" in req.url - assert "parameter_code=00060%2C00065" in req.url + assert "monitoring_location_id=USGS-05427718%2CUSGS-05427719" in str(req.url) + assert "parameter_code=00060%2C00065" in str(req.url) def test_construct_api_requests_monitoring_locations_post(): @@ -154,7 +157,7 @@ def test_construct_api_requests_monitoring_locations_post(): assert req.method == "POST" assert req.headers["Content-Type"] == "application/query-cql-json" - body = json.loads(req.body) + body = json.loads(req.content) # Top-level shape: AND over a list of per-param predicates. assert body["op"] == "and" assert isinstance(body["args"], list) and len(body["args"]) == 1 @@ -175,8 +178,8 @@ def test_construct_api_requests_single_value_stays_get(): parameter_code="00060", ) assert req.method == "GET" - assert "monitoring_location_id=USGS-05427718" in req.url - assert "%2C" not in req.url # no comma-encoded multi-value + assert "monitoring_location_id=USGS-05427718" in str(req.url) + assert "%2C" not in str(req.url) # no comma-encoded multi-value def test_construct_api_requests_numeric_list_joins_with_str(): @@ -189,7 +192,7 @@ def test_construct_api_requests_numeric_list_joins_with_str(): water_year=[2020, 2021], ) assert req.method == "GET" - assert "water_year=2020%2C2021" in req.url + assert "water_year=2020%2C2021" in str(req.url) def test_construct_api_requests_two_element_date_list_becomes_interval(): @@ -204,7 +207,7 @@ def test_construct_api_requests_two_element_date_list_becomes_interval(): ) assert req.method == "GET" # `/` URL-encodes to %2F. Confirms _format_api_dates ran before the join. - assert "time=2024-01-01%2F2024-01-31" in req.url + assert "time=2024-01-01%2F2024-01-31" in str(req.url) def test_samples_results(): diff --git a/tests/waterdata_utils_test.py b/tests/waterdata_utils_test.py index c135115c..bb5ece10 100644 --- a/tests/waterdata_utils_test.py +++ b/tests/waterdata_utils_test.py @@ -2,9 +2,9 @@ import logging from unittest import mock +import httpx import pandas as pd import pytest -import requests import dataretrieval.waterdata.utils as _utils_module from dataretrieval.waterdata.chunking import RateLimited, ServiceUnavailable @@ -74,13 +74,13 @@ def test_walk_pages_multiple_mocked(): resp2.status_code = 200 # Mock client (Session) - mock_client = mock.MagicMock(spec=requests.Session) + mock_client = mock.MagicMock(spec=httpx.Client) # First call to send() returns resp1, then call to request() in loop returns resp2 mock_client.send.return_value = resp1 mock_client.request.return_value = resp2 # Mock request (PreparedRequest) - mock_req = mock.MagicMock(spec=requests.PreparedRequest) + mock_req = mock.MagicMock(spec=httpx.Request) mock_req.method = "GET" mock_req.headers = {} mock_req.url = "https://example.com/page1" @@ -115,14 +115,14 @@ def _walk_pages_with_failure(failure_resp_or_exc): """Run _walk_pages where page 1 succeeds and page 2 fails as given.""" resp1 = _resp_ok([{"id": "1", "properties": {"val": "a"}}]) - mock_client = mock.MagicMock(spec=requests.Session) + mock_client = mock.MagicMock(spec=httpx.Client) mock_client.send.return_value = resp1 if isinstance(failure_resp_or_exc, BaseException): mock_client.request.side_effect = failure_resp_or_exc else: mock_client.request.return_value = failure_resp_or_exc - mock_req = mock.MagicMock(spec=requests.PreparedRequest) + mock_req = mock.MagicMock(spec=httpx.Request) mock_req.method = "GET" mock_req.headers = {} mock_req.url = "https://example.com/page1" @@ -135,21 +135,21 @@ def test_walk_pages_raises_on_connection_error_mid_pagination(): chained, and the wrapper message must include recovery guidance that is NOT rate-limit-specific (no quota window involved).""" with pytest.raises(RuntimeError, match="Paginated request failed") as excinfo: - _walk_pages_with_failure(requests.ConnectionError("boom")) + _walk_pages_with_failure(httpx.ConnectError("boom")) msg = str(excinfo.value) - assert isinstance(excinfo.value.__cause__, requests.ConnectionError) + assert isinstance(excinfo.value.__cause__, httpx.ConnectError) assert "boom" in msg assert "retry the request" in msg assert "rate-limit window" not in msg def test_walk_pages_raises_with_class_name_when_cause_stringifies_empty(): - """Some ``requests`` exceptions (e.g. ``Timeout()`` with no args) + """Some ``httpx`` exceptions (e.g. ``TimeoutException("")``) stringify to ``""``. The wrapper must still produce an informative message — fall back to the exception class name.""" with pytest.raises(RuntimeError, match="Paginated request failed") as excinfo: - _walk_pages_with_failure(requests.Timeout()) + _walk_pages_with_failure(httpx.TimeoutException("")) msg = str(excinfo.value) assert "Timeout" in msg, msg @@ -206,10 +206,10 @@ def test_walk_pages_wraps_initial_page_parse_error(): # Body is unparseable JSON (gateway HTML page, truncated stream). resp.json.side_effect = json.JSONDecodeError("Expecting value", "...", 0) - mock_client = mock.MagicMock(spec=requests.Session) + mock_client = mock.MagicMock(spec=httpx.Client) mock_client.send.return_value = resp - mock_req = mock.MagicMock(spec=requests.PreparedRequest) + mock_req = mock.MagicMock(spec=httpx.Request) mock_req.method = "GET" mock_req.headers = {} mock_req.url = "https://example.com/page1" @@ -270,11 +270,11 @@ def test_walk_pages_does_not_mutate_initial_response(): "links": [], } - mock_client = mock.MagicMock(spec=requests.Session) + mock_client = mock.MagicMock(spec=httpx.Client) mock_client.send.return_value = page1 mock_client.request.return_value = page2 - mock_req = mock.MagicMock(spec=requests.PreparedRequest) + mock_req = mock.MagicMock(spec=httpx.Request) mock_req.method = "GET" mock_req.headers = {} mock_req.url = "https://example.com/page1" @@ -324,7 +324,7 @@ def _run_get_stats_data_with_failure(failure_resp_or_exc, monkeypatch): mock.MagicMock(return_value=pd.DataFrame()), ) - mock_client = mock.MagicMock(spec=requests.Session) + mock_client = mock.MagicMock(spec=httpx.Client) mock_client.send.return_value = _stats_initial_ok() if isinstance(failure_resp_or_exc, BaseException): mock_client.request.side_effect = failure_resp_or_exc @@ -347,11 +347,11 @@ def test_get_stats_data_raises_on_mid_pagination_failure(monkeypatch): follow-up callback is wired into ``_paginate`` correctly.""" with pytest.raises(RuntimeError, match="Paginated request failed") as excinfo: _run_get_stats_data_with_failure( - requests.ConnectionError("stats-boom"), + httpx.ConnectError("stats-boom"), monkeypatch, ) - assert isinstance(excinfo.value.__cause__, requests.ConnectionError) + assert isinstance(excinfo.value.__cause__, httpx.ConnectError) assert "stats-boom" in str(excinfo.value) @@ -605,12 +605,16 @@ def test_format_api_dates_rejects_mapping(): def _make_response(status, body, reason=None, content_type="text/html"): - resp = requests.Response() - resp.status_code = status - resp.reason = reason - resp._content = body.encode("utf-8") - resp.headers["Content-Type"] = content_type - return resp + headers = {"Content-Type": content_type} + extensions = {} + if reason is not None: + extensions["reason_phrase"] = reason.encode("utf-8") + return httpx.Response( + status_code=status, + content=body.encode("utf-8"), + headers=headers, + extensions=extensions, + ) def test_error_body_handles_non_json_html_response(): @@ -730,3 +734,41 @@ def test_raise_for_non_200_still_raises_bare_runtimeerror_for_other_4xx(): # ServiceUnavailable. Both subclass RuntimeError, so a plain # ``pytest.raises(RuntimeError)`` would match either. assert type(excinfo.value) is RuntimeError + + +def test_next_req_url_rejects_cross_host(): + """``_next_req_url`` must refuse to follow a next-page link to a + different host. The original request's headers (including any + auth-like artifacts) were minted for the original host; following + a server-supplied cross-host URL would leak them — and the URL + itself could be sensitive.""" + from dataretrieval.waterdata.utils import _next_req_url + + resp = mock.MagicMock() + resp.url = httpx.URL("https://api.waterdata.usgs.gov/page1") + body = { + "numberReturned": 1, + "features": [{"id": "1"}], + "links": [{"rel": "next", "href": "https://evil.example.org/secret"}], + } + with pytest.raises(RuntimeError, match="cross-host next-page"): + _next_req_url(resp, body=body) + + +def test_check_ogc_requests_raises_typed_on_5xx(httpx_mock): + """``_check_ogc_requests`` previously called ``resp.raise_for_status()``, + which leaks raw ``httpx.HTTPStatusError``. Now routes through + ``_raise_for_non_200`` so callers see ``ServiceUnavailable`` / + ``RateLimited`` / ``RuntimeError`` — the same typed contract as + the main data path.""" + from dataretrieval.waterdata.chunking import ServiceUnavailable + from dataretrieval.waterdata.utils import OGC_API_URL, _check_ogc_requests + + httpx_mock.add_response( + method="GET", + url=f"{OGC_API_URL}/collections/daily/schema", + status_code=503, + json={"code": "ServiceUnavailable", "description": "maintenance window"}, + ) + with pytest.raises(ServiceUnavailable): + _check_ogc_requests(endpoint="daily", req_type="schema") diff --git a/tests/waterservices_test.py b/tests/waterservices_test.py index 2126b4d1..874c2a0e 100644 --- a/tests/waterservices_test.py +++ b/tests/waterservices_test.py @@ -57,18 +57,20 @@ def test_query_waterservices_validation(): assert str(type_error.value) == "Service not recognized" -def test_query_validation(requests_mock): +def test_query_validation(httpx_mock): request_url = ( "https://waterservices.usgs.gov/nwis/stat?sites=bad_site_id&format=rdb" ) - requests_mock.get(request_url, status_code=400) + httpx_mock.add_response(method="GET", url=request_url, status_code=400) with pytest.raises(ValueError) as type_error: get_stats(sites="bad_site_id") assert request_url in str(type_error) request_url = "https://waterservices.usgs.gov/nwis/stat?sites=123456&format=rdb" - requests_mock.get( - request_url, text="No sites/data found using the selection criteria specified" + httpx_mock.add_response( + method="GET", + url=request_url, + text="No sites/data found using the selection criteria specified", ) with pytest.raises(NoSitesError) as no_sites_error: get_stats(sites="123456") @@ -82,7 +84,7 @@ def test_get_record_validation(): assert str(type_error.value) == "Unrecognized service: not_a_service" -def test_get_dv(requests_mock): +def test_get_dv(httpx_mock): """Verify get_dv builds the expected request URL and returns a DataFrame.""" format = "json" site = "01491000%2C01645000" @@ -91,7 +93,7 @@ def test_get_dv(requests_mock): f"&startDT=2020-02-14&endDT=2020-02-15&sites={site}" ) response_file_path = "tests/data/waterservices_dv.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_dv( sites=["01491000", "01645000"], start="2020-02-14", end="2020-02-15" ) @@ -100,11 +102,11 @@ def test_get_dv(requests_mock): raise TypeError(f"{type(df)} is not DataFrame base class type") assert df.size == 8 - assert_metadata(requests_mock, request_url, md, site, None, format) + assert_metadata(httpx_mock, request_url, md, site, None, format) @pytest.mark.parametrize("site_input_type_list", [True, False]) -def test_get_dv_site_value_types(requests_mock, site_input_type_list): +def test_get_dv_site_value_types(httpx_mock, site_input_type_list): """Tests get_dv method for valid input types for the 'sites' parameter""" _format = "json" site = "01491000" @@ -113,7 +115,7 @@ def test_get_dv_site_value_types(requests_mock, site_input_type_list): f"&startDT=2020-02-14&endDT=2020-02-15&sites={site}" ) response_file_path = "tests/data/waterservices_dv.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) if site_input_type_list: sites = [site] else: @@ -125,7 +127,7 @@ def test_get_dv_site_value_types(requests_mock, site_input_type_list): assert df.size == 8 -def test_get_iv(requests_mock): +def test_get_iv(httpx_mock): """Verify get_iv builds the expected request URL and returns a DataFrame.""" format = "json" site = "01491000%2C01645000" @@ -134,7 +136,7 @@ def test_get_iv(requests_mock): f"&startDT=2019-02-14&endDT=2020-02-15&sites={site}" ) response_file_path = "tests/data/waterservices_iv.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_iv( sites=["01491000", "01645000"], start="2019-02-14", end="2020-02-15" ) @@ -143,11 +145,11 @@ def test_get_iv(requests_mock): assert df.size == 563380 assert md.url == request_url - assert_metadata(requests_mock, request_url, md, site, None, format) + assert_metadata(httpx_mock, request_url, md, site, None, format) @pytest.mark.parametrize("site_input_type_list", [True, False]) -def test_get_iv_site_value_types(requests_mock, site_input_type_list): +def test_get_iv_site_value_types(httpx_mock, site_input_type_list): """Tests get_iv method for valid input type for the 'sites' parameter""" _format = "json" site = "01491000" @@ -156,7 +158,7 @@ def test_get_iv_site_value_types(requests_mock, site_input_type_list): f"&startDT=2019-02-14&endDT=2020-02-15&sites={site}" ) response_file_path = "tests/data/waterservices_iv.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) if site_input_type_list: sites = [site] else: @@ -168,7 +170,7 @@ def test_get_iv_site_value_types(requests_mock, site_input_type_list): assert md.url == request_url -def test_get_info(requests_mock): +def test_get_info(httpx_mock): """ Verify get_info builds the expected request URL and returns a DataFrame. Note that only sites and format are passed as query params @@ -179,7 +181,7 @@ def test_get_info(requests_mock): parameter_cd = "00618" request_url = f"https://waterservices.usgs.gov/nwis/site?sites={site}¶meterCd={parameter_cd}&siteOutput=Expanded&format={format}" response_file_path = "tests/data/waterservices_site.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_info(sites=["01491000", "01645000"], parameterCd="00618") if not isinstance(df, DataFrame): raise TypeError(f"{type(df)} is not DataFrame base class type") @@ -194,10 +196,10 @@ def test_get_info(requests_mock): assert df.size == size assert md.url == request_url - assert_metadata(requests_mock, request_url, md, site, [parameter_cd], format) + assert_metadata(httpx_mock, request_url, md, site, [parameter_cd], format) -def test_get_discharge_peaks(requests_mock): +def test_get_discharge_peaks(httpx_mock): """Verify get_discharge_peaks builds the expected URL and returns a DataFrame.""" format = "rdb" site = "01594440" @@ -206,17 +208,17 @@ def test_get_discharge_peaks(requests_mock): "&begin_date=2000-02-14&end_date=2020-02-15" ) response_file_path = "tests/data/waterservices_peaks.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_discharge_peaks(sites=[site], start="2000-02-14", end="2020-02-15") if not isinstance(df, DataFrame): raise TypeError(f"{type(df)} is not DataFrame base class type") assert df.size == 240 - assert_metadata(requests_mock, request_url, md, site, None, format) + assert_metadata(httpx_mock, request_url, md, site, None, format) @pytest.mark.parametrize("site_input_type_list", [True, False]) -def test_get_discharge_peaks_sites_value_types(requests_mock, site_input_type_list): +def test_get_discharge_peaks_sites_value_types(httpx_mock, site_input_type_list): """Tests get_discharge_peaks for valid input types of the 'sites' parameter""" _format = "rdb" @@ -226,7 +228,7 @@ def test_get_discharge_peaks_sites_value_types(requests_mock, site_input_type_li "&begin_date=2000-02-14&end_date=2020-02-15" ) response_file_path = "tests/data/waterservices_peaks.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) if site_input_type_list: sites = [site] else: @@ -249,22 +251,22 @@ def test_get_ratings_validation(): ) -def test_get_ratings(requests_mock): +def test_get_ratings(httpx_mock): """Verify get_ratings builds the expected URL and returns a DataFrame.""" format = "rdb" site = "01594440" request_url = f"https://nwis.waterdata.usgs.gov/nwisweb/get_ratings/?site_no={site}&file_type=base" response_file_path = "tests/data/waterservices_ratings.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_ratings(site_no=site) if not isinstance(df, DataFrame): raise TypeError(f"{type(df)} is not DataFrame base class type") assert df.size == 33 - assert_metadata(requests_mock, request_url, md, site, None, format) + assert_metadata(httpx_mock, request_url, md, site, None, format) -def test_what_sites(requests_mock): +def test_what_sites(httpx_mock): """Verify what_sites builds the expected URL and returns a DataFrame.""" size = 2472 format = "rdb" @@ -275,7 +277,7 @@ def test_what_sites(requests_mock): f"¶meterCd={parameter_cd}&hasDataTypeCd=dv&format={format}" ) response_file_path = "tests/data/nwis_sites.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_sites( bBox=[-83.0, 36.5, -81.0, 38.5], @@ -298,25 +300,25 @@ def test_what_sites(requests_mock): size += len(df) assert df.size == size - assert_metadata(requests_mock, request_url, md, None, parameter_cd_list, format) + assert_metadata(httpx_mock, request_url, md, None, parameter_cd_list, format) -def test_get_stats(requests_mock): +def test_get_stats(httpx_mock): """Verify get_stats builds the expected URL and returns a DataFrame.""" format = "rdb" request_url = f"https://waterservices.usgs.gov/nwis/stat?sites=01491000%2C01645000&format={format}" response_file_path = "tests/data/waterservices_stats.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_stats(sites=["01491000", "01645000"]) if not isinstance(df, DataFrame): raise TypeError(f"{type(df)} is not DataFrame base class type") assert df.size == 51936 - assert_metadata(requests_mock, request_url, md, None, None, format) + assert_metadata(httpx_mock, request_url, md, None, None, format) @pytest.mark.parametrize("site_input_type_list", [True, False]) -def test_get_stats_site_value_types(requests_mock, site_input_type_list): +def test_get_stats_site_value_types(httpx_mock, site_input_type_list): """Tests get_stats method for valid input types for the 'sites' parameter""" _format = "rdb" site = "01491000" @@ -324,7 +326,7 @@ def test_get_stats_site_value_types(requests_mock, site_input_type_list): f"https://waterservices.usgs.gov/nwis/stat?sites={site}&format={_format}" ) response_file_path = "tests/data/waterservices_stats.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) if site_input_type_list: sites = [site] else: @@ -335,23 +337,28 @@ def test_get_stats_site_value_types(requests_mock, site_input_type_list): assert df.size == 51936 -def mock_request(requests_mock, request_url, file_path): +def mock_request(httpx_mock, request_url, file_path): with open(file_path) as text: - requests_mock.get( - request_url, text=text.read(), headers={"mock_header": "value"} + httpx_mock.add_response( + method="GET", + url=request_url, + text=text.read(), + headers={"mock_header": "value"}, ) -def assert_metadata(requests_mock, request_url, md, site, parameter_cd, format): +def assert_metadata(httpx_mock, request_url, md, site, parameter_cd, format): assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" if site is not None: site_request_url = ( f"https://waterservices.usgs.gov/nwis/site?sites={site}&format=rdb" ) with open("tests/data/waterservices_site.txt") as text: - requests_mock.get(site_request_url, text=text.read()) + httpx_mock.add_response( + method="GET", url=site_request_url, text=text.read() + ) site_info, _ = md.site_info if not isinstance(site_info, DataFrame): raise AssertionError(f"{type(site_info)} is not DataFrame base class type") diff --git a/tests/wqp_test.py b/tests/wqp_test.py index f432ab26..356f7ac8 100644 --- a/tests/wqp_test.py +++ b/tests/wqp_test.py @@ -17,7 +17,7 @@ ) -def test_get_results(requests_mock): +def test_get_results(httpx_mock): """Tests water quality portal ratings query""" request_url = ( "https://www.waterqualitydata.us/data/Result/Search?siteid=WIDNR_WQX-10032762" @@ -25,7 +25,7 @@ def test_get_results(requests_mock): "&mimeType=csv" ) response_file_path = "tests/data/wqp_results.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_results( siteid="WIDNR_WQX-10032762", characteristicName="Specific conductance", @@ -36,12 +36,12 @@ def test_get_results(requests_mock): assert df.shape == (5, 65) assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None assert df["ActivityStartDateTime"].notna().all() -def test_get_results_WQX3(requests_mock): +def test_get_results_WQX3(httpx_mock): """Tests water quality portal results query with new WQX3.0 profile""" request_url = ( "https://www.waterqualitydata.us/wqx3/Result/search?siteid=WIDNR_WQX-10032762" @@ -50,7 +50,7 @@ def test_get_results_WQX3(requests_mock): "&dataProfile=fullPhysChem" ) response_file_path = "tests/data/wqp3_results.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = get_results( legacy=False, siteid="WIDNR_WQX-10032762", @@ -62,151 +62,154 @@ def test_get_results_WQX3(requests_mock): assert df.shape == (5, 186) assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None assert df["Activity_StartDateTime"].notna().all() -def test_what_sites(requests_mock): +def test_what_sites(httpx_mock): """Tests Water quality portal sites query""" request_url = ( "https://www.waterqualitydata.us/data/Station/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_sites.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_sites(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 239868 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def test_what_organizations(requests_mock): +def test_what_organizations(httpx_mock): """Tests Water quality portal organizations query""" request_url = ( "https://www.waterqualitydata.us/data/Organization/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_organizations.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_organizations(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 576 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def test_what_projects(requests_mock): +def test_what_projects(httpx_mock): """Tests Water quality portal projects query""" request_url = ( "https://www.waterqualitydata.us/data/Project/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_projects.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_projects(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 530 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def test_what_activities(requests_mock): +def test_what_activities(httpx_mock): """Tests Water quality portal activities query""" request_url = ( "https://www.waterqualitydata.us/data/Activity/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_activities.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_activities(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 5087443 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def test_what_detection_limits(requests_mock): +def test_what_detection_limits(httpx_mock): """Tests Water quality portal detection limits query""" request_url = ( "https://www.waterqualitydata.us/data/ResultDetectionQuantitationLimit/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_detection_limits.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_detection_limits(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 98770 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def test_what_habitat_metrics(requests_mock): +def test_what_habitat_metrics(httpx_mock): """Tests Water quality portal habitat metrics query""" request_url = ( "https://www.waterqualitydata.us/data/BiologicalMetric/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_habitat_metrics.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_habitat_metrics(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 48114 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def test_what_project_weights(requests_mock): +def test_what_project_weights(httpx_mock): """Tests Water quality portal project weights query""" request_url = ( "https://www.waterqualitydata.us/data/ProjectMonitoringLocationWeighting/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_project_weights.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_project_weights(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 33098 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def test_what_activity_metrics(requests_mock): +def test_what_activity_metrics(httpx_mock): """Tests Water quality portal activity metrics query""" request_url = ( "https://www.waterqualitydata.us/data/ActivityMetric/Search?statecode=US%3A34&characteristicName=Chloride" "&mimeType=csv" ) response_file_path = "tests/data/wqp_activity_metrics.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, md = what_activity_metrics(statecode="US:34", characteristicName="Chloride") assert type(df) is DataFrame assert df.size == 378 assert md.url == request_url assert isinstance(md.query_time, datetime.timedelta) - assert md.header == {"mock_header": "value"} + assert md.header.get("mock_header") == "value" assert md.comment is None -def mock_request(requests_mock, request_url, file_path): +def mock_request(httpx_mock, request_url, file_path): with open(file_path) as text: - requests_mock.get( - request_url, text=text.read(), headers={"mock_header": "value"} + httpx_mock.add_response( + method="GET", + url=request_url, + text=text.read(), + headers={"mock_header": "value"}, ) @@ -220,7 +223,7 @@ def test_check_kwargs(): kwargs = _check_kwargs(kwargs) -def test_get_results_wqx3_preserves_user_dataProfile(requests_mock): +def test_get_results_wqx3_preserves_user_dataProfile(httpx_mock): """A valid user-supplied WQX3.0 profile must not be overwritten. Regression: previously the `else` branch of the `dataProfile` validation @@ -232,11 +235,11 @@ def test_get_results_wqx3_preserves_user_dataProfile(requests_mock): "siteid=UTAHDWQ_WQX-4993795&mimeType=csv&dataProfile=narrow" ) response_file_path = "tests/data/wqp3_results.txt" - mock_request(requests_mock, request_url, response_file_path) + mock_request(httpx_mock, request_url, response_file_path) df, _md = get_results( legacy=False, siteid="UTAHDWQ_WQX-4993795", dataProfile="narrow" ) assert isinstance(df, DataFrame) - sent = requests_mock.request_history[-1] - assert sent.qs.get("dataprofile") == ["narrow"] + sent = httpx_mock.get_requests()[-1] + assert sent.url.params.get("dataProfile") == "narrow" From df904037b857dc9b5fb826eb3bd468b95d70fdf8 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 26 May 2026 10:08:00 -0500 Subject: [PATCH 2/8] docs(userguide): rewrite timeconventions onto the Water Data API Replace the NWIS get_record examples (one of which used the decommissioned gwlevels service) with waterdata.get_continuous / get_daily, and update the guide to the Water Data API datetime model: time is a column (not the index), tz-aware datetime64[us, UTC] for continuous data and tz-naive dates for daily, demonstrated with the .dt.tz_convert idiom. Example output captured from live calls. Drops the NWIS-only index / PR#58 notes. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/source/userguide/timeconventions.rst | 124 +++++++++++----------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/docs/source/userguide/timeconventions.rst b/docs/source/userguide/timeconventions.rst index 8336be51..d5a69c98 100644 --- a/docs/source/userguide/timeconventions.rst +++ b/docs/source/userguide/timeconventions.rst @@ -3,78 +3,74 @@ Datetime Information -------------------- -``dataretrieval`` attempts to normalize time data to UTC time when converting -web service data into dataframes. To do this, in-built pandas functions are -used; either :obj:`pandas.to_datetime()` during the initial datetime object -conversion, or :obj:`pandas.DataFrame.tz_localize()` if the datetime objects -exist but are not UTC-localized. In most cases (single-site and multi-site), -``dataretrieval`` assigns the datetime information as the dataframe *index*, -the exception to this is when incomplete datetime information is available, in -these cases integers are used as the dataframe index (see `PR#58`_ for more -details). - -.. _PR#58: https://github.com/DOI-USGS/dataretrieval-python/pull/58 +``dataretrieval`` normalizes time data to UTC when converting Water Data API +responses into dataframes. Timestamps are returned in the ``time`` column (the +dataframe itself uses a default integer index). For sub-daily data — such as +continuous (instantaneous) values — ``time`` is a timezone-aware +``datetime64[us, UTC]`` column. Daily values represent a whole calendar day, +so their ``time`` column is timezone-naive (dates only). Inspecting Timestamps ********************* -For single sites, the index of the returned dataframe contains pandas -timestamps. +For continuous data, the ``time`` column holds UTC-localized pandas timestamps. + +.. code:: python + + >>> from dataretrieval import waterdata + >>> df, md = waterdata.get_continuous( + ... monitoring_location_id="USGS-05427718", + ... parameter_code="00060", + ... time="2024-03-01/2024-03-02", + ... ) + >>> df["time"].head() + 0 2024-03-01 00:00:00+00:00 + 1 2024-03-01 00:15:00+00:00 + 2 2024-03-01 00:30:00+00:00 + 3 2024-03-01 00:45:00+00:00 + 4 2024-03-01 01:00:00+00:00 + Name: time, dtype: datetime64[us, UTC] + +Each timestamp has the format ``YYYY-MM-DD HH:MM:SS+HH:MM``. Because the values +are localized to UTC, the offset (``+HH:MM``) is ``+00:00``. You can convert +them to a local timezone of your choosing with the pandas ``.dt`` accessor. .. code:: python - >>> import dataretrieval.nwis as nwis - >>> site = '03339000' - >>> df = nwis.get_record(sites=site, service='peaks', - ... start='2015-01-01', end='2017-12-31') - >>> print(df) - agency_cd site_no peak_tm peak_va peak_cd gage_ht gage_ht_cd year_last_pk ag_dt ag_tm ag_gage_ht ag_gage_ht_cd - datetime - 2015-06-08 00:00:00+00:00 USGS 03339000 17:30 25100 C 22.83 NaN NaN NaN NaN NaN NaN - 2015-12-29 00:00:00+00:00 USGS 03339000 18:45 37600 C 26.66 NaN NaN NaN NaN NaN NaN - 2017-05-05 00:00:00+00:00 USGS 03339000 04:45 17000 C 18.47 NaN NaN NaN NaN NaN NaN - -Here the index of the dataframe ``df`` is a set of datetime objects. Each has -the format, ``YYYY-MM-DD HH:MM:SS+HH:MM``. Because these timestamps are -localized to be in UTC, the expected offset (``+HH:MM``) is ``+00:00``. -These values can be converted to a local timezone of your choosing using -:obj:`pandas` functionality. + >>> df["time"] = df["time"].dt.tz_convert("America/New_York") + >>> df["time"].head() + 0 2024-02-29 19:00:00-05:00 + 1 2024-02-29 19:15:00-05:00 + 2 2024-02-29 19:30:00-05:00 + 3 2024-02-29 19:45:00-05:00 + 4 2024-02-29 20:00:00-05:00 + Name: time, dtype: datetime64[us, America/New_York] + +After conversion the timestamps carry New York's offset — ``-05:00`` during +standard time, or ``-04:00`` during daylight saving time, since New York is 4 +or 5 hours behind UTC depending on the time of year. Note that the first +midnight-UTC reading rolls back to the previous calendar day (``2024-02-29``) +once shifted into New York time. + + +Daily values +************ + +Daily data summarize a whole calendar day, so the ``time`` column is +timezone-naive — no offset is applied. .. code:: python - >>> df.index = df.index.tz_convert(tz='America/New_York') - >>> print(df) - agency_cd site_no peak_tm peak_va peak_cd gage_ht gage_ht_cd year_last_pk ag_dt ag_tm ag_gage_ht ag_gage_ht_cd - datetime - 2015-06-07 20:00:00-04:00 USGS 03339000 17:30 25100 C 22.83 NaN NaN NaN NaN NaN NaN - 2015-12-28 19:00:00-05:00 USGS 03339000 18:45 37600 C 26.66 NaN NaN NaN NaN NaN NaN - 2017-05-04 20:00:00-04:00 USGS 03339000 04:45 17000 C 18.47 NaN NaN NaN NaN NaN NaN - -Above, the index was converted to localize the timestamps to New York. -In the updated dataframe index, the resulting timestamps now have offsets of -``-04:00`` and ``-05:00`` as New York is either 4 or 5 hours behind UTC -depending on the time of year (due to daylight savings). - -When information for multiple sites is requested, ``dataretrieval`` creates a -dataframe with a multi-index, with the first entry containing the site number, -and the second containing the datetime information. - -.. doctest:: - - >>> import dataretrieval.nwis as nwis - >>> sites = ['180049066381200', '290000095192602'] - >>> df = nwis.get_record(sites=sites, service='gwlevels', - ... start='2021-10-01', end='2022-01-01') - >>> df - agency_cd site_tp_cd lev_dt lev_tm lev_tz_cd ... lev_dt_acy_cd lev_acy_cd lev_src_cd lev_meth_cd lev_age_cd - site_no datetime ... - 180049066381200 2021-10-04 19:54:00+00:00 USGS GW 2021-10-04 19:54 +0000 ... m NaN S S A - 2021-11-16 14:28:00+00:00 USGS GW 2021-11-16 14:28 +0000 ... m NaN S S A - 2021-12-09 10:43:00+00:00 USGS GW 2021-12-09 10:43 +0000 ... m NaN S S A - 290000095192602 2021-12-08 19:07:00+00:00 USGS GW 2021-12-08 19:07 +0000 ... m NaN S S P - - [4 rows x 15 columns] - -Here note that the default datetime index information returned is also UTC -localized, and therefore the offset values are ``+00:00``. \ No newline at end of file + >>> df, md = waterdata.get_daily( + ... monitoring_location_id="USGS-05427718", + ... parameter_code="00060", + ... time="2024-03-01/2024-03-05", + ... ) + >>> df["time"].head() + 0 2024-03-01 + 1 2024-03-02 + 2 2024-03-03 + 3 2024-03-04 + 4 2024-03-05 + Name: time, dtype: datetime64[us] From 527963d69e064f5412332ad5e0813e1285969407 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 26 May 2026 10:30:39 -0500 Subject: [PATCH 3/8] docs(nldi): skip get_features_by_data_source example in the doctest build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The example downloads every feature for data_source="nwissite" (all NWIS sites nationwide) — an effectively unbounded slow stream that evades httpx's per-read timeout and hangs `make doctest` (it surfaced as the httpx.ReadTimeout doctest failure). Tag it `# doctest: +SKIP` so it stays a real, rendered doctest example but is not executed, and fix the comment that referenced an unused feature_id. Co-Authored-By: Claude Opus 4.7 (1M context) --- dataretrieval/nldi.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dataretrieval/nldi.py b/dataretrieval/nldi.py index e54ceb85..a483dc9e 100644 --- a/dataretrieval/nldi.py +++ b/dataretrieval/nldi.py @@ -296,8 +296,9 @@ def get_features_by_data_source(data_source: str) -> gpd.GeoDataFrame: -------- .. doctest:: - >>> # Get features for a feature wqp and feature_id USGS-01031500 - >>> gdf = dataretrieval.nldi.get_features_by_data_source( + >>> # "nwissite" returns every NWIS site nationwide, so this example is + >>> # skipped in the doctest build to avoid the (very large) download. + >>> gdf = dataretrieval.nldi.get_features_by_data_source( # doctest: +SKIP ... data_source="nwissite" ... ) """ From c03267fe042a26e4aa2690a230e0fa6a1b313c5a Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 26 May 2026 10:49:47 -0500 Subject: [PATCH 4/8] docs(examples): port readme & siteinfo examples to the Water Data API Rewrite the two getting-started example pages off the deprecated nwis module onto waterdata: get_continuous (instantaneous values), get_monitoring_locations (site metadata), and get_time_series_metadata (the data-availability catalog that replaces the legacy seriesCatalogOutput switch). Output captured from live calls. Switched to illustrative .. code:: blocks matching the waterdata docstrings, which also clears the last 4 output-drift doctest failures: the doctest suite is now green (0 failures). Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/source/examples/readme_examples.rst | 81 ++++++++++--------- docs/source/examples/siteinfo_examples.rst | 90 +++++++++++----------- 2 files changed, 89 insertions(+), 82 deletions(-) diff --git a/docs/source/examples/readme_examples.rst b/docs/source/examples/readme_examples.rst index 62cb6eb7..0d7a9099 100644 --- a/docs/source/examples/readme_examples.rst +++ b/docs/source/examples/readme_examples.rst @@ -1,40 +1,49 @@ -Examples from the Readme file on retrieving NWIS data ------------------------------------------------------ +Retrieving USGS water data with the ``waterdata`` module +-------------------------------------------------------- .. note:: - NWIS stands for the National Water Information System - - -.. doctest:: - - >>> # first import the functions for downloading data from NWIS - >>> import dataretrieval.nwis as nwis - - >>> # specify the USGS site code for which we want data. - >>> site = '03339000' - - >>> # get instantaneous values (iv) - >>> df = nwis.get_record(sites=site, service='iv', start='2017-12-31', end='2018-01-01') - - >>> df.head() - 00010 00010_cd site_no 00060 00060_cd ... 63680_ysi), [discontinued 10/5/21_cd 63680_hach 63680_hach_cd 99133 99133_cd - datetime ... - 2017-12-31 06:00:00+00:00 1.0 A 03339000 140.0 A ... A 3.6 A 4.61 A - 2017-12-31 06:15:00+00:00 1.0 A 03339000 138.0 A ... A 3.6 A 4.61 A - 2017-12-31 06:30:00+00:00 1.0 A 03339000 139.0 A ... A 3.4 A 4.61 A - 2017-12-31 06:45:00+00:00 1.0 A 03339000 139.0 A ... A 3.4 A 4.61 A - 2017-12-31 07:00:00+00:00 1.0 A 03339000 139.0 A ... A 3.5 A 4.61 A - - [5 rows x 21 columns] - - - >>> # get basic info about the site - >>> df3 = nwis.get_record(sites=site, service='site') - - >>> print(df3) - agency_cd site_no station_nm site_tp_cd lat_va long_va ... aqfr_cd aqfr_type_cd well_depth_va hole_depth_va depth_src_cd project_no - 0 USGS 03339000 VERMILION RIVER NEAR DANVILLE, IL ST 400603 873550 ... NaN NaN NaN NaN NaN 100 - - [1 rows x 42 columns] \ No newline at end of file + The ``waterdata`` module accesses the USGS `Water Data API`_ and is the + recommended way to retrieve USGS water data. The legacy ``nwis`` module + remains available but is deprecated. + +.. _Water Data API: https://api.waterdata.usgs.gov/ + +.. code:: python + + >>> # import the waterdata module + >>> from dataretrieval import waterdata + + >>> # a USGS monitoring location id joins the agency code and the site + >>> # number with a hyphen + >>> site = "USGS-05427718" + + >>> # get continuous (instantaneous) streamflow — parameter code 00060 — + >>> # over a one-day window + >>> df, md = waterdata.get_continuous( + ... monitoring_location_id=site, + ... parameter_code="00060", + ... time="2024-03-01/2024-03-02", + ... ) + + >>> df[["time", "value", "unit_of_measure", "approval_status"]].head() + time value unit_of_measure approval_status + 0 2024-03-01 00:00:00+00:00 18.7 ft^3/s Approved + 1 2024-03-01 00:15:00+00:00 18.5 ft^3/s Approved + 2 2024-03-01 00:30:00+00:00 18.5 ft^3/s Approved + 3 2024-03-01 00:45:00+00:00 18.5 ft^3/s Approved + 4 2024-03-01 01:00:00+00:00 18.3 ft^3/s Approved + + >>> # get descriptive metadata about the monitoring location itself + >>> info, md = waterdata.get_monitoring_locations( + ... monitoring_location_id=site, + ... skip_geometry=True, + ... ) + + >>> info[["monitoring_location_name", "state_name", "site_type", "drainage_area"]].T + 0 + monitoring_location_name YAHARA RIVER AT WINDSOR, WI + state_name Wisconsin + site_type Stream + drainage_area 73.6 diff --git a/docs/source/examples/siteinfo_examples.rst b/docs/source/examples/siteinfo_examples.rst index 55806721..f514d634 100644 --- a/docs/source/examples/siteinfo_examples.rst +++ b/docs/source/examples/siteinfo_examples.rst @@ -2,49 +2,47 @@ Retrieving site information --------------------------- -By default ``dataretrieval`` fetches the so-called "expanded" site date from -the NWIS web service. However there is an optional keyword parameter called -``seriesCatalogOutput`` that can be set to "True" if you wish to retrieve the -detailed period of record information for a site instead. Refer to the -`NWIS water services documentation`_ for additional information. The below -example illustrates the use of the ``seriesCatalogOutput`` switch and displays -the resulting column names for the output dataframes (example prompted by -`GitHub Issue #34`_). - -.. _NWIS water services documentation: https://waterservices.usgs.gov/docs/site-service/site-service-details/ - -.. _GitHub Issue #34: https://github.com/DOI-USGS/dataretrieval-python/issues/34 - -.. doctest:: - - # first import the functions for downloading data from NWIS - >>> import dataretrieval.nwis as nwis - - # fetch data from a major HUC basin with seriesCatalogOutput set to True - >>> df = nwis.get_record(huc='20', parameterCd='00060', - ... service='site', seriesCatalogOutput='True') - - >>> print(df.columns) - Index(['agency_cd', 'site_no', 'station_nm', 'site_tp_cd', 'dec_lat_va', - 'dec_long_va', 'coord_acy_cd', 'dec_coord_datum_cd', 'alt_va', - 'alt_acy_va', 'alt_datum_cd', 'huc_cd', 'data_type_cd', 'parm_cd', - 'stat_cd', 'ts_id', 'loc_web_ds', 'medium_grp_cd', 'parm_grp_cd', - 'srs_id', 'access_cd', 'begin_date', 'end_date', 'count_nu'], - dtype='object') - - # repeat the same query with seriesCatalogOutput set as False - >>> df = nwis.get_record(huc='20', parameterCd='00060', - ... service='site', seriesCatalogOutput='False') - - >>> print(df.columns) - Index(['agency_cd', 'site_no', 'station_nm', 'site_tp_cd', 'lat_va', 'long_va', - 'dec_lat_va', 'dec_long_va', 'coord_meth_cd', 'coord_acy_cd', - 'coord_datum_cd', 'dec_coord_datum_cd', 'district_cd', 'state_cd', - 'county_cd', 'country_cd', 'land_net_ds', 'map_nm', 'map_scale_fc', - 'alt_va', 'alt_meth_cd', 'alt_acy_va', 'alt_datum_cd', 'huc_cd', - 'basin_cd', 'topo_cd', 'instruments_cd', 'construction_dt', - 'inventory_dt', 'drain_area_va', 'contrib_drain_area_va', 'tz_cd', - 'local_time_fg', 'reliability_cd', 'gw_file_cd', 'nat_aqfr_cd', - 'aqfr_cd', 'aqfr_type_cd', 'well_depth_va', 'hole_depth_va', - 'depth_src_cd', 'project_no'], - dtype='object') +The ``waterdata`` module distinguishes a monitoring location's *descriptive* +metadata from the *catalog* of data available at it. + +Use ``get_monitoring_locations`` for descriptive metadata — name, location, +site type, drainage area, hydrologic unit, and so on. + +.. code:: python + + >>> from dataretrieval import waterdata + + >>> info, md = waterdata.get_monitoring_locations( + ... monitoring_location_id="USGS-05427718", + ... skip_geometry=True, + ... ) + + >>> info[["monitoring_location_name", "site_type", "drainage_area", "hydrologic_unit_code"]].T + 0 + monitoring_location_name YAHARA RIVER AT WINDSOR, WI + site_type Stream + drainage_area 73.6 + hydrologic_unit_code 070900020504 + +To discover *what data are available* at a location — the period-of-record +catalog that the legacy ``seriesCatalogOutput`` switch used to provide — use +``get_time_series_metadata``. Each row is one time series; the ``begin`` and +``end`` columns give its period of record. + +.. code:: python + + >>> series, md = waterdata.get_time_series_metadata( + ... monitoring_location_id="USGS-05427718", + ... skip_geometry=True, + ... ) + + >>> len(series) # number of available time series + 22 + + >>> series[["parameter_code", "parameter_name", "computation_period_identifier"]].head() + parameter_code parameter_name computation_period_identifier + 0 00045 Precipitation Points + 1 91060 Orthophosphate, diss Daily + 2 91057 NH3+orgN, wu as N Daily + 3 00060 Discharge Points + 4 80155 Suspnd sedmnt disch Daily From 54059f9e32dacb82272019341a6eddcfa46c6d43 Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 26 May 2026 12:46:19 -0500 Subject: [PATCH 5/8] docs(examples): migrate demo notebooks to the Water Data API Port the example notebooks still using the deprecated nwis module onto waterdata and re-execute them against the live API: - Statistics -> get_stats_date_range / get_stats_por - Peaks -> get_peaks - Ratings -> get_ratings - NWIS_demo_1 -> get_peaks (and get_monitoring_locations in the trend analysis) - R Python Vignette equivalents -> get_monitoring_locations / get_daily / get_continuous / get_samples / get_peaks / get_ratings / get_stats_date_range WaterUse stays on the now-defunct nwis.get_water_use with a prominent note that USGS water-use data has no Water Data API equivalent. The remaining Hydroshare notebooks were already on waterdata. Co-Authored-By: Claude Opus 4.7 (1M context) --- demos/NWIS_demo_1.ipynb | 553 +++- demos/R Python Vignette equivalents.ipynb | 452 ++- .../USGS_dataretrieval_Peaks_Examples.ipynb | 2475 ++++++++++++++++- .../USGS_dataretrieval_Ratings_Examples.ipynb | 328 ++- ...GS_dataretrieval_Statistics_Examples.ipynb | 1466 +++++++++- ...USGS_dataretrieval_WaterUse_Examples.ipynb | 88 +- 6 files changed, 4986 insertions(+), 376 deletions(-) diff --git a/demos/NWIS_demo_1.ipynb b/demos/NWIS_demo_1.ipynb index 6edaa8ab..e1e72ad4 100644 --- a/demos/NWIS_demo_1.ipynb +++ b/demos/NWIS_demo_1.ipynb @@ -8,7 +8,7 @@ "\n", "## Introduction\n", "\n", - "This notebook demonstrates a slightly more advanced application of data_retrieval.nwis to collect using a national dataset of historical peak annual streamflow measurements. The objective is to use a regression of peak annual streamflow and time to identify any trends. But, not for a singile station," + "This notebook demonstrates a slightly more advanced application of the `dataretrieval.waterdata` module: assembling a dataset of historical annual peak streamflow and regressing peak discharge against time to look for trends — not at a single station, but across many." ] }, { @@ -21,15 +21,22 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 1, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:39:18.814744Z", + "iopub.status.busy": "2026-05-26T16:39:18.814536Z", + "iopub.status.idle": "2026-05-26T16:39:44.634069Z", + "shell.execute_reply": "2026-05-26T16:39:44.633533Z" + } + }, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "from scipy import stats\n", "\n", - "from dataretrieval import nwis" + "from dataretrieval import waterdata" ] }, { @@ -37,30 +44,219 @@ "metadata": {}, "source": [ "## Basic usage\n", - "Recall that the basic way to download data from NWIS is through through the `nwis.get_record()` function, which returns a user-specified record as a `pandas` dataframe. The `nwis.get_record()` function is really a facade of sorts, that allows the user to download data from various NWIS services through a consistant interface. To get started, we require a few simple parameters: a list of site numbers or states codes, a service, and a start date." + "The `waterdata` module is the recommended interface to USGS water data and replaces the deprecated `nwis` module. Annual peak streamflow is retrieved with `waterdata.get_peaks()`, which returns a `pandas` data frame and a metadata object. To get started we need a monitoring location id, a parameter code, and (optionally) a time window." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:39:44.636678Z", + "iopub.status.busy": "2026-05-26T16:39:44.636481Z", + "iopub.status.idle": "2026-05-26T16:39:45.186976Z", + "shell.execute_reply": "2026-05-26T16:39:45.186277Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: peaks · 1 page · 56 rows · 917/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
05282b66f-ee19-43d9-b772-936da66a968dPOINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s163002026-04-28 09:40:12.927288+00:001970-04-2019701970420NaNNone
1935a7d88-6e6c-4b80-b6a2-20b909d6e67fPOINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s89102026-04-28 09:40:12.927288+00:001971-02-051971197125NaNNone
269975ecd-959a-46bc-a221-815c947a76b6POINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s92402026-04-28 09:40:12.927288+00:001972-04-2219721972422NaNNone
355fc2bdd-fc45-44d5-b291-6767e5e44ff6POINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s166002026-04-28 09:40:12.927288+00:001973-04-2319731973423NaNNone
4c202f21a-a940-418f-92d6-253598a16dd0POINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s195002026-04-28 09:40:12.927288+00:001974-06-2319741974623NaNNone
\n", + "
" + ], + "text/plain": [ + " peak_id geometry \\\n", + "0 5282b66f-ee19-43d9-b772-936da66a968d POINT (-87.59761 40.10108) \n", + "1 935a7d88-6e6c-4b80-b6a2-20b909d6e67f POINT (-87.59761 40.10108) \n", + "2 69975ecd-959a-46bc-a221-815c947a76b6 POINT (-87.59761 40.10108) \n", + "3 55fc2bdd-fc45-44d5-b291-6767e5e44ff6 POINT (-87.59761 40.10108) \n", + "4 c202f21a-a940-418f-92d6-253598a16dd0 POINT (-87.59761 40.10108) \n", + "\n", + " time_series_id monitoring_location_id parameter_code \\\n", + "0 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", + "1 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", + "2 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", + "3 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", + "4 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", + "\n", + " unit_of_measure value last_modified time \\\n", + "0 ft^3/s 16300 2026-04-28 09:40:12.927288+00:00 1970-04-20 \n", + "1 ft^3/s 8910 2026-04-28 09:40:12.927288+00:00 1971-02-05 \n", + "2 ft^3/s 9240 2026-04-28 09:40:12.927288+00:00 1972-04-22 \n", + "3 ft^3/s 16600 2026-04-28 09:40:12.927288+00:00 1973-04-23 \n", + "4 ft^3/s 19500 2026-04-28 09:40:12.927288+00:00 1974-06-23 \n", + "\n", + " water_year year month day time_of_day peak_since \n", + "0 1970 1970 4 20 NaN None \n", + "1 1971 1971 2 5 NaN None \n", + "2 1972 1972 4 22 NaN None \n", + "3 1973 1973 4 23 NaN None \n", + "4 1974 1974 6 23 NaN None " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# download annual peaks from a single site\n", - "df = nwis.get_record(sites=\"03339000\", service=\"peaks\", start=\"1970-01-01\")\n", - "df.head()\n", - "\n", - "# alternatively information for the entire state of illiois can be downloaded using\n", - "# df = nwis.get_record(state_cd='il', service='peaks', start='1970-01-01')" + "# download annual peaks (discharge, parameter 00060) from a single site\n", + "df, md = waterdata.get_peaks(\n", + " monitoring_location_id=\"USGS-03339000\",\n", + " parameter_code=\"00060\",\n", + " time=\"1970-01-01/..\",\n", + ")\n", + "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Most of the fields are empty, but no matter. All we require are date (`datetime`), site number (`site_no`), and peak streamflow (`peak_va`).\n", - "\n", - "Note that when multiple sites are specified, `nwis.get_record()` will combine `datetime` and `site_no` fields to create a multi-index dataframe." + "All we require for the trend analysis are the peak date (`time`), the monitoring location id (`monitoring_location_id`), and the peak streamflow (`value`). The Water Data API returns a flat (single-index) data frame with one row per annual peak." ] }, { @@ -68,34 +264,32 @@ "metadata": {}, "source": [ "## Preparing the regression\n", - "Next we'll define a function that applies ordinary least squares on peak discharge and time.\n", - "After grouping the dataset by `site_no`, we will apply the regression on a per-site basis. The results from each site, will be returned as a row that includes the slope, y-intercept, r$^2$, p value, and standard error of the regression." + "Next we'll define a function that applies ordinary least squares to peak discharge versus time. After grouping the dataset by `monitoring_location_id`, we apply the regression per site. Each site's result is returned as a row with the slope, y-intercept, p value, and standard error of the regression." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:39:45.202721Z", + "iopub.status.busy": "2026-05-26T16:39:45.202576Z", + "iopub.status.idle": "2026-05-26T16:39:45.205354Z", + "shell.execute_reply": "2026-05-26T16:39:45.204876Z" + } + }, "outputs": [], "source": [ "def peak_trend_regression(df):\n", - " \"\"\" \"\"\"\n", - " # convert datetimes to days for regression\n", - " peak_date = df.index\n", - " peak_date = pd.to_datetime(df.index.get_level_values(1))\n", - " df[\"peak_d\"] = (peak_date - peak_date.min()) / np.timedelta64(1, \"D\")\n", - " # df['peak_d'] = (df['peak_dt'] - df['peak_dt'].min()) / np.timedelta64(1,'D')\n", + " # convert peak dates to days since the first peak for the regression\n", + " peak_date = pd.to_datetime(df[\"time\"])\n", + " peak_d = (peak_date - peak_date.min()) / np.timedelta64(1, \"D\")\n", "\n", " # normalize the peak discharge values\n", - " df[\"peak_va\"] = (df[\"peak_va\"] - df[\"peak_va\"].mean()) / df[\"peak_va\"].std()\n", + " value = (df[\"value\"] - df[\"value\"].mean()) / df[\"value\"].std()\n", "\n", - " slope, intercept, _r_value, p_value, std_error = stats.linregress(\n", - " df[\"peak_d\"], df[\"peak_va\"]\n", - " )\n", + " slope, intercept, _r_value, p_value, std_error = stats.linregress(peak_d, value)\n", "\n", - " # df_out = pd.DataFrame({'slope':slope,'intercept':intercept,'p_value':p_value},index=df['site_no'].iloc[0])\n", - "\n", - " # return df_out\n", " return pd.Series(\n", " {\n", " \"slope\": slope,\n", @@ -115,40 +309,51 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:39:45.207186Z", + "iopub.status.busy": "2026-05-26T16:39:45.207074Z", + "iopub.status.idle": "2026-05-26T16:39:45.210272Z", + "shell.execute_reply": "2026-05-26T16:39:45.209711Z" + } + }, "outputs": [], "source": [ - "def peak_trend_analysis(states, start_date):\n", + "def peak_trend_analysis(state_names, start_date):\n", " \"\"\"\n", - " states : list\n", - " a list containing the two-letter codes for each state to include in the\n", - " analysis.\n", - "\n", + " state_names : list\n", + " state names to include in the analysis, e.g. [\"Illinois\", \"Indiana\"].\n", " start_date : string\n", - " the date to use a the beginning of the analysis.\n", + " the date to use as the beginning of the analysis (YYYY-MM-DD).\n", " \"\"\"\n", " final_df = pd.DataFrame()\n", "\n", - " for state in states:\n", - " # download annual peak discharge records\n", - " df = nwis.get_record(state_cd=state, start=start_date, service=\"peaks\")\n", + " for state in state_names:\n", + " # find stream gages in the state\n", + " sites, _ = waterdata.get_monitoring_locations(\n", + " state_name=state, site_type_code=\"ST\", skip_geometry=True\n", + " )\n", + " # download annual peak discharge for those sites\n", + " df, _ = waterdata.get_peaks(\n", + " monitoring_location_id=sites[\"monitoring_location_id\"].tolist(),\n", + " parameter_code=\"00060\",\n", + " time=f\"{start_date}/..\",\n", + " )\n", " # group the data by site and apply our regression\n", - " temp = df.groupby(\"site_no\").apply(peak_trend_regression).dropna()\n", + " temp = (\n", + " df.groupby(\"monitoring_location_id\")\n", + " .apply(peak_trend_regression)\n", + " .dropna()\n", + " )\n", " # drop any insignificant results\n", " temp = temp[temp[\"p_value\"] < 0.05]\n", "\n", - " # now download metadata for each site, which we'll use later to plot the sites\n", - " # on a map\n", - " site_df = nwis.get_record(sites=temp.index, service=\"site\")\n", - "\n", - " if final_df.empty:\n", - " final_df = pd.merge(site_df, temp, right_index=True, left_on=\"site_no\")\n", - "\n", - " else:\n", - " final_df = final_df.append(\n", - " pd.merge(site_df, temp, right_index=True, left_on=\"site_no\")\n", - " )\n", + " # join site metadata (for mapping) with the trend results\n", + " merged = pd.merge(\n", + " sites, temp, right_index=True, left_on=\"monitoring_location_id\"\n", + " )\n", + " final_df = pd.concat([final_df, merged], ignore_index=True)\n", "\n", " return final_df" ] @@ -162,31 +367,225 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:39:45.211913Z", + "iopub.status.busy": "2026-05-26T16:39:45.211814Z", + "iopub.status.idle": "2026-05-26T16:39:45.213762Z", + "shell.execute_reply": "2026-05-26T16:39:45.213237Z" + } + }, "outputs": [], "source": [ - "# Warning these lines will download a large dataset from the web and\n", - "# will take few minutes to run.\n", + "# Warning: these lines download a large dataset from the web and\n", + "# will take a few minutes to run.\n", "\n", - "# start = '1970-01-01'\n", - "# states = codes.state_codes\n", - "# final_df = peak_trend_analysis(states=states, start_date=start)\n", - "# final_df.to_csv('datasets/peak_discharge_trends.csv')" + "# start = \"1970-01-01\"\n", + "# states = [\"Illinois\", \"Indiana\", \"Ohio\"]\n", + "# final_df = peak_trend_analysis(state_names=states, start_date=start)\n", + "# final_df.to_csv(\"datasets/peak_discharge_trends.csv\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Instead, lets quickly load some predownloaded data, which I generated using the code above." + "Instead, let's quickly load some pre-generated results bundled with this notebook. (This example dataset was produced by an earlier run of the analysis and retains the column names from that run.)" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:39:45.215365Z", + "iopub.status.busy": "2026-05-26T16:39:45.215266Z", + "iopub.status.idle": "2026-05-26T16:39:45.228631Z", + "shell.execute_reply": "2026-05-26T16:39:45.228151Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Unnamed: 0agency_cdsite_nostation_nmsite_tp_cddec_lat_vadec_long_vacoord_acy_cddec_coord_datum_cdalt_vaalt_acy_vaalt_datum_cdhuc_cdinterceptp_valueslopestd_error
00USGS2343275ABBIE CREEK NEAR ABBEVILLE ALST31.561835-85.204932UNAD83NaNNaNNaN3130004.0-0.6419110.0158990.0002310.000065
11USGS2360275JUDY CREEK NEAR OZARK ALST31.463224-85.572159UNAD83NaNNaNNaN3140201.0-0.7025690.0046520.0002690.000069
22USGS2360500EAST FORK CHOCTAWHATCHEE R NEAR MIDLAND CITY ALST31.373227-85.477158UNAD83179.10.01NGVD293140201.0-1.1385520.0076980.0002110.000003
33USGS2367400YELLOW RIVER NR SANFORD, ALAST31.317391-86.355788UNAD83NaNNaNNaN3140103.0-0.6113100.0158360.0005110.000013
44USGS2367500LIGHTWOOD KNOT CREEK AT BABBIE ALST31.270725-86.313564UNAD83185.00.01NGVD293140103.0-0.7056760.0152310.0002100.000052
\n", + "
" + ], + "text/plain": [ + " Unnamed: 0 agency_cd site_no \\\n", + "0 0 USGS 2343275 \n", + "1 1 USGS 2360275 \n", + "2 2 USGS 2360500 \n", + "3 3 USGS 2367400 \n", + "4 4 USGS 2367500 \n", + "\n", + " station_nm site_tp_cd dec_lat_va \\\n", + "0 ABBIE CREEK NEAR ABBEVILLE AL ST 31.561835 \n", + "1 JUDY CREEK NEAR OZARK AL ST 31.463224 \n", + "2 EAST FORK CHOCTAWHATCHEE R NEAR MIDLAND CITY AL ST 31.373227 \n", + "3 YELLOW RIVER NR SANFORD, ALA ST 31.317391 \n", + "4 LIGHTWOOD KNOT CREEK AT BABBIE AL ST 31.270725 \n", + "\n", + " dec_long_va coord_acy_cd dec_coord_datum_cd alt_va alt_acy_va \\\n", + "0 -85.204932 U NAD83 NaN NaN \n", + "1 -85.572159 U NAD83 NaN NaN \n", + "2 -85.477158 U NAD83 179.1 0.01 \n", + "3 -86.355788 U NAD83 NaN NaN \n", + "4 -86.313564 U NAD83 185.0 0.01 \n", + "\n", + " alt_datum_cd huc_cd intercept p_value slope std_error \n", + "0 NaN 3130004.0 -0.641911 0.015899 0.000231 0.000065 \n", + "1 NaN 3140201.0 -0.702569 0.004652 0.000269 0.000069 \n", + "2 NGVD29 3140201.0 -1.138552 0.007698 0.000211 0.000003 \n", + "3 NaN 3140103.0 -0.611310 0.015836 0.000511 0.000013 \n", + "4 NGVD29 3140103.0 -0.705676 0.015231 0.000210 0.000052 " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "final_df = pd.read_csv(\"datasets/peak_discharge_trends.csv\")\n", "final_df.head()" @@ -196,7 +595,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Notice how the data has been transformed. In addition to statistics about the peak streamflow trends, we've also used the NWIS site service to add latitude and longtitude information for each station." + "Notice how the data has been transformed. In addition to statistics about the peak streamflow trends, the analysis joined monitoring-location metadata to add latitude and longitude for each station." ] }, { @@ -209,8 +608,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:39:45.230088Z", + "iopub.status.busy": "2026-05-26T16:39:45.229993Z", + "iopub.status.idle": "2026-05-26T16:39:45.232274Z", + "shell.execute_reply": "2026-05-26T16:39:45.231890Z" + } + }, "outputs": [], "source": [ "# Currently commented out as there isn't an easy way to install mpl_toolkits\n", @@ -272,7 +678,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.13.3" }, "vscode": { "interpreter": { diff --git a/demos/R Python Vignette equivalents.ipynb b/demos/R Python Vignette equivalents.ipynb index 1904e88e..2f7f8abf 100644 --- a/demos/R Python Vignette equivalents.ipynb +++ b/demos/R Python Vignette equivalents.ipynb @@ -9,8 +9,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 1, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:29.608908Z", + "iopub.status.busy": "2026-05-26T17:42:29.608564Z", + "iopub.status.idle": "2026-05-26T17:42:32.260083Z", + "shell.execute_reply": "2026-05-26T17:42:32.259231Z" + } + }, "outputs": [], "source": [ "from dataretrieval import nwis, waterdata, wqp" @@ -27,24 +34,72 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:32.263095Z", + "iopub.status.busy": "2026-05-26T17:42:32.262819Z", + "iopub.status.idle": "2026-05-26T17:42:32.981278Z", + "shell.execute_reply": "2026-05-26T17:42:32.980581Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: monitoring-locations · 1 page · 2 rows · 988/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "\"\"\"\n", "{r getSite, echo=TRUE, eval=FALSE}\n", "siteNumbers <- c(\"01491000\",\"01645000\")\n", "siteINFO <- readNWISsite(siteNumbers)\n", "\"\"\"\n", - "siteNumbers = [\"01491000\", \"01645000\"]\n", - "siteINFO, md = nwis.get_iv(sites=siteNumbers)" + "siteNumbers = [\"USGS-01491000\", \"USGS-01645000\"]\n", + "siteINFO, md = waterdata.get_monitoring_locations(\n", + " monitoring_location_id=siteNumbers, skip_geometry=True\n", + ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:33.015826Z", + "iopub.status.busy": "2026-05-26T17:42:33.015634Z", + "iopub.status.idle": "2026-05-26T17:42:33.402624Z", + "shell.execute_reply": "2026-05-26T17:42:33.401928Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: time-series-metadata · 1 page · 11 rows · 987/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "\"\"\"\n", "# Continuing from the previous example:\n", @@ -52,94 +107,166 @@ "dailyDataAvailable <- whatNWISdata(siteNumbers,\n", " service=\"dv\", statCd=\"00003\")\n", "\"\"\"\n", - "\n", - "dailyDataAvailable, md = nwis.get_dv(sites=siteNumbers, statCd=\"00003\")" + "dailyDataAvailable, md = waterdata.get_time_series_metadata(\n", + " monitoring_location_id=siteNumbers, statistic_id=\"00003\", skip_geometry=True\n", + ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:33.404611Z", + "iopub.status.busy": "2026-05-26T17:42:33.404472Z", + "iopub.status.idle": "2026-05-26T17:42:34.130988Z", + "shell.execute_reply": "2026-05-26T17:42:34.130476Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: daily · 1 page · 1,096 rows · 986/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "\"\"\"\n", "# Choptank River near Greensboro, MD:\n", "siteNumber <- \"01491000\"\n", "parameterCd <- \"00060\" # Discharge\n", - "startDate <- \"2009-10-01\" \n", - "endDate <- \"2012-09-30\" \n", + "startDate <- \"2009-10-01\"\n", + "endDate <- \"2012-09-30\"\n", "\n", - "discharge <- readNWISdv(siteNumber, \n", - " parameterCd, startDate, endDate)\n", + "discharge <- readNWISdv(siteNumber, parameterCd, startDate, endDate)\n", "\"\"\"\n", "# Choptank River near Greensboro, MD:\n", - "siteNumber = \"01491000\"\n", + "siteNumber = \"USGS-01491000\"\n", "parameterCd = \"00060\" # Discharge\n", - "startDate = \"2009-10-01\"\n", - "endDate = \"2012-09-30\"\n", "\n", - "discharge, md = nwis.get_dv(\n", - " sites=siteNumber, parameterCd=parameterCd, start=startDate, end=endDate\n", + "discharge, md = waterdata.get_daily(\n", + " monitoring_location_id=siteNumber,\n", + " parameter_code=parameterCd,\n", + " time=\"2009-10-01/2012-09-30\",\n", ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:34.132919Z", + "iopub.status.busy": "2026-05-26T17:42:34.132811Z", + "iopub.status.idle": "2026-05-26T17:42:34.877663Z", + "shell.execute_reply": "2026-05-26T17:42:34.877228Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: daily · 1 page · 364 rows · 985/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "\"\"\"\n", "siteNumber <- \"01491000\"\n", "parameterCd <- c(\"00010\",\"00060\") # Temperature and discharge\n", - "statCd <- c(\"00001\",\"00003\") # Mean and maximum\n", + "statCd <- c(\"00001\",\"00003\") # Maximum and mean\n", "startDate <- \"2012-01-01\"\n", "endDate <- \"2012-05-01\"\n", "\n", - "temperatureAndFlow <- readNWISdv(siteNumber, parameterCd, \n", - " startDate, endDate, statCd=statCd)\n", + "temperatureAndFlow <- readNWISdv(siteNumber, parameterCd, startDate, endDate, statCd=statCd)\n", "\"\"\"\n", - "siteNumber = \"01491000\"\n", + "siteNumber = \"USGS-01491000\"\n", "parameterCd = [\"00010\", \"00060\"] # Temperature and discharge\n", - "statCd = [\"00001\", \"00003\"] # Mean and maximum\n", - "startDate = \"2012-01-01\"\n", - "endDate = \"2012-05-01\"\n", + "statisticId = [\"00001\", \"00003\"] # Maximum and mean\n", "\n", - "temperatureAndFlow, md = nwis.get_dv(\n", - " sites=siteNumber,\n", - " parameterCd=parameterCd,\n", - " start=startDate,\n", - " end=endDate,\n", - " statCd=statCd,\n", + "temperatureAndFlow, md = waterdata.get_daily(\n", + " monitoring_location_id=siteNumber,\n", + " parameter_code=parameterCd,\n", + " statistic_id=statisticId,\n", + " time=\"2012-01-01/2012-05-01\",\n", ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:34.879364Z", + "iopub.status.busy": "2026-05-26T17:42:34.879265Z", + "iopub.status.idle": "2026-05-26T17:42:35.276437Z", + "shell.execute_reply": "2026-05-26T17:42:35.275840Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: continuous · 1 page · 97 rows · 986/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "\"\"\"\n", "parameterCd <- \"00060\" # Discharge\n", - "startDate <- \"2012-05-12\" \n", - "endDate <- \"2012-05-13\" \n", - "dischargeUnit <- readNWISuv(siteNumber, parameterCd, \n", - " startDate, endDate)\n", + "startDate <- \"2012-05-12\"\n", + "endDate <- \"2012-05-13\"\n", + "dischargeUnit <- readNWISuv(siteNumber, parameterCd, startDate, endDate)\n", "\"\"\"\n", - "siteNumber = \"01491000\"\n", + "siteNumber = \"USGS-01491000\"\n", "parameterCd = \"00060\" # Discharge\n", - "startDate = \"2012-05-12\"\n", - "endDate = \"2012-05-13\"\n", - "dischargeUnit, md = nwis.get_iv(\n", - " sites=siteNumber, parameterCd=parameterCd, start=startDate, end=endDate\n", + "\n", + "dischargeUnit, md = waterdata.get_continuous(\n", + " monitoring_location_id=siteNumber,\n", + " parameter_code=parameterCd,\n", + " time=\"2012-05-12/2012-05-13\",\n", ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:35.278224Z", + "iopub.status.busy": "2026-05-26T17:42:35.278100Z", + "iopub.status.idle": "2026-05-26T17:42:35.800108Z", + "shell.execute_reply": "2026-05-26T17:42:35.799574Z" + } + }, "outputs": [], "source": [ "\"\"\"\n", @@ -147,84 +274,168 @@ "parameterCd <- c(\"00618\",\"71851\")\n", "startDate <- \"1985-10-01\"\n", "endDate <- \"2012-09-30\"\n", - "dfLong <- read_USGS_samples(monitoringLocationIdentifier=sprintf(\"USGS-%s\", siteNumber), usgsPCode=parameterCd, \n", - " activityStartDateLower=startDate, activityStartDateUpper=endDate)\n", + "dfLong <- read_USGS_samples(monitoringLocationIdentifier=sprintf(\"USGS-%s\", siteNumber),\n", + " usgsPCode=parameterCd, activityStartDateLower=startDate, activityStartDateUpper=endDate)\n", "\"\"\"\n", - "siteNumber = \"01491000\"\n", + "siteNumber = \"USGS-01491000\"\n", "parameterCd = [\"00618\", \"71851\"]\n", - "startDate = \"1985-10-01\"\n", - "endDate = \"2012-09-30\"\n", + "\n", "dfLong, md = waterdata.get_samples(\n", - " monitoringLocationIdentifier=f\"USGS-{siteNumber}\",\n", + " monitoringLocationIdentifier=siteNumber,\n", " usgsPCode=parameterCd,\n", - " activityStartDateLower=startDate,\n", - " activityStartDateUpper=endDate,\n", + " activityStartDateLower=\"1985-10-01\",\n", + " activityStartDateUpper=\"2012-09-30\",\n", ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 8, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:35.801905Z", + "iopub.status.busy": "2026-05-26T17:42:35.801789Z", + "iopub.status.idle": "2026-05-26T17:42:36.965288Z", + "shell.execute_reply": "2026-05-26T17:42:36.964439Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: peaks · 1 page · 49 rows · 984/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "\"\"\"\n", "siteNumber <- '01594440'\n", "peakData <- readNWISpeak(siteNumber)\n", "\"\"\"\n", - "siteNumber = \"01594440\"\n", - "peakData, md = nwis.get_discharge_peaks(sites=siteNumber)" + "peakData, md = waterdata.get_peaks(\n", + " monitoring_location_id=\"USGS-01594440\", parameter_code=\"00060\"\n", + ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:36.967386Z", + "iopub.status.busy": "2026-05-26T17:42:36.967183Z", + "iopub.status.idle": "2026-05-26T17:42:38.184282Z", + "shell.execute_reply": "2026-05-26T17:42:38.183553Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['USGS-01594440.base.rdb']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "\"\"\"\n", "ratingData <- readNWISrating(siteNumber, \"base\")\n", "attr(ratingData, \"RATING\")\n", "\"\"\"\n", - "ratings_data, md = nwis.get_ratings(site=\"01594440\", file_type=\"base\")" + "# get_ratings returns a dict keyed by \"..rdb\"\n", + "ratings_data = waterdata.get_ratings(\n", + " monitoring_location_id=\"USGS-01594440\", file_type=\"base\"\n", + ")\n", + "list(ratings_data.keys())" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 10, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:38.186467Z", + "iopub.status.busy": "2026-05-26T17:42:38.186272Z", + "iopub.status.idle": "2026-05-26T17:42:38.545255Z", + "shell.execute_reply": "2026-05-26T17:42:38.544660Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: observationIntervals · 1 page · 344 rows · 3,981/4,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "\"\"\"\n", "discharge_stats <- readNWISstat(siteNumbers=c(\"02319394\"),\n", " parameterCd=c(\"00060\"),\n", - " statReportType=\"annual\") \n", + " statReportType=\"annual\")\n", "\"\"\"\n", - "discharge_stats, md = nwis.get_stats(\n", - " sites=\"02319394\", parameterCd=\"00060\", statReportType=\"annual\", statTypeCd=\"all\"\n", + "discharge_stats, md = waterdata.get_stats_date_range(\n", + " monitoring_location_id=\"USGS-02319394\",\n", + " parameter_code=\"00060\",\n", + " computation_type=\"arithmetic_mean\",\n", ")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 11, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:38.547164Z", + "iopub.status.busy": "2026-05-26T17:42:38.547011Z", + "iopub.status.idle": "2026-05-26T17:42:38.549668Z", + "shell.execute_reply": "2026-05-26T17:42:38.548979Z" + } + }, "outputs": [], "source": [ - "# '''\n", - "# dischargeWI <- readNWISdata(service=\"dv\",\n", - "# stateCd=\"WI\",\n", - "# parameterCd=\"00060\",\n", - "# drainAreaMin=\"50\",\n", - "# statCd=\"00003\")\n", - "# '''\n", - "# dischargeWI, md = nwis.get_dv(stateCd=\"WI\", parameterCd=\"00060\", drainAreaMin=\"50\", statCd=\"00003\")" + "# R: readNWISdata(service=\"dv\", stateCd=\"WI\", parameterCd=\"00060\",\n", + "# drainAreaMin=\"50\", statCd=\"00003\")\n", + "#\n", + "# The Water Data API serves daily values per monitoring location. To assemble a\n", + "# state-wide set, first find the locations (optionally filtering by drainage\n", + "# area) with waterdata.get_monitoring_locations(state_name=\"Wisconsin\", ...),\n", + "# then pass their ids to waterdata.get_daily(parameter_code=\"00060\",\n", + "# statistic_id=\"00003\")." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 12, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:38.551661Z", + "iopub.status.busy": "2026-05-26T17:42:38.551511Z", + "iopub.status.idle": "2026-05-26T17:42:38.553863Z", + "shell.execute_reply": "2026-05-26T17:42:38.553203Z" + } + }, "outputs": [], "source": [ "# '''\n", @@ -236,8 +447,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 13, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:38.555324Z", + "iopub.status.busy": "2026-05-26T17:42:38.555222Z", + "iopub.status.idle": "2026-05-26T17:42:38.557187Z", + "shell.execute_reply": "2026-05-26T17:42:38.556614Z" + } + }, "outputs": [], "source": [ "# '''\n", @@ -249,8 +467,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 14, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:38.558558Z", + "iopub.status.busy": "2026-05-26T17:42:38.558425Z", + "iopub.status.idle": "2026-05-26T17:42:38.560452Z", + "shell.execute_reply": "2026-05-26T17:42:38.559866Z" + } + }, "outputs": [], "source": [ "# '''\n", @@ -263,8 +488,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 15, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:38.561850Z", + "iopub.status.busy": "2026-05-26T17:42:38.561741Z", + "iopub.status.idle": "2026-05-26T17:42:38.664579Z", + "shell.execute_reply": "2026-05-26T17:42:38.663906Z" + } + }, "outputs": [], "source": [ "\"\"\"site <- whatWQPsamples(siteid=\"USGS-01594440\")\"\"\"\n", @@ -274,8 +506,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 16, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:42:38.666566Z", + "iopub.status.busy": "2026-05-26T17:42:38.666430Z", + "iopub.status.idle": "2026-05-26T17:42:38.737076Z", + "shell.execute_reply": "2026-05-26T17:42:38.736489Z" + } + }, "outputs": [], "source": [ "\"\"\"\n", @@ -292,21 +531,21 @@ "source": [ "# Embedded Metadata\n", "\n", - "All service methods return the DataFrame containing requested data and Metadata as a tuple. Note, a call using get_record will only return the DataFrame to remain compatible with previous usage.\n", + "Most `waterdata` and `wqp` service methods return a tuple of the requested data (a pandas DataFrame) and a metadata object.\n", "\n", + "`md` is an object with the following attributes:\n", "\n", "```\n", - "national, md = nwis.get_water_use()\n", + "Metadata\n", + " url # the URL used to query the service\n", + " query_time # how long the query took\n", + " header # the response headers\n", "```\n", "\n", - "md is an object with the following attributes\n", + "Note: USGS *water use* data has no Water Data API equivalent yet, so it remains available only through the deprecated `nwis` module:\n", "\n", "```\n", - "Metadata\n", - " url # the resulting url to query usgs\n", - " query_time # the time it took to query usgs\n", - " site_info # a method to call site_info with the site parameters supplied\n", - " header # any headers attached to the response object\n", + "national, md = nwis.get_water_use()\n", "```" ] }, @@ -331,7 +570,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.13.3" }, "vscode": { "interpreter": { diff --git a/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb index 8a045bad..1ab47d43 100644 --- a/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb @@ -4,9 +4,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# USGS dataretrieval Python Package `get_discharge_peaks()` Examples\n", + "# USGS dataretrieval Python Package Peak Streamflow Examples\n", "\n", - "This notebook provides examples of using the Python dataretrieval package to retrieve streamflow peak data for United States Geological Survey (USGS) monitoring sites. The dataretrieval package provides a collection of functions to get data from the USGS National Water Information System (NWIS) and other online sources of hydrology and water quality data, including the United States Environmental Protection Agency (USEPA)." + "This notebook provides examples of using the Python dataretrieval package to retrieve annual peak streamflow data for United States Geological Survey (USGS) monitoring locations using the **USGS Water Data API** via the `waterdata` module. The `waterdata` module is the recommended way to access USGS water data and replaces the deprecated `nwis` module." ] }, { @@ -21,7 +21,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:50.390490Z", + "iopub.status.busy": "2026-05-26T16:32:50.390354Z", + "iopub.status.idle": "2026-05-26T16:32:51.939420Z", + "shell.execute_reply": "2026-05-26T16:32:51.938709Z" + } + }, "outputs": [], "source": [ "!pip install dataretrieval" @@ -36,15 +43,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:51.942658Z", + "iopub.status.busy": "2026-05-26T16:32:51.942468Z", + "iopub.status.idle": "2026-05-26T16:32:52.517630Z", + "shell.execute_reply": "2026-05-26T16:32:52.517076Z" + } + }, "outputs": [], "source": [ "from IPython.display import display\n", "\n", - "from dataretrieval import nwis\n", - "from dataretrieval import waterdata\n", - "import dataretrieval.waterdata as waterdata\n" + "from dataretrieval import waterdata\n" ] }, { @@ -53,31 +65,61 @@ "source": [ "### Basic Usage\n", "\n", - "The dataretrieval package has several functions that allow you to retrieve data from different web services. This examples uses the `get_discharge_peaks()` function to retrieve peak streamflow data for a USGS monitoring site from NWIS. The function has the following arguments:\n", + "This example uses the `get_peaks()` function to retrieve annual peak data for a USGS monitoring location. Commonly used arguments include:\n", "\n", - "Arguments (Additional parameters, if supplied, will be used as query parameters)\n", - "\n", - "* **sites** (list of strings): A list of USGS site identifiers for which data will be retrieved. If the waterdata parameter site_no is supplied, it will overwrite the sites parameter.\n", - "* **start** (string): A beginning date for the period for which data will be retrieved. If the waterdata parameter begin_date is supplied, it will overwrite the start parameter.\n", - "* **end** (string): An ending date for the period for which data will be retrieved. If the waterdata parameter end_date is supplied, it will overwrite the end parameter." + "* **monitoring_location_id** (string or list of strings): USGS monitoring location id(s), formed as the agency code and site number joined by a hyphen (e.g. `\"USGS-01594440\"`).\n", + "* **parameter_code** (string or list of strings): 5-digit USGS parameter code(s). Peak records include both peak discharge (`00060`) and the corresponding gage height (`00065`); pass `parameter_code=\"00060\"` to retrieve peak discharge only.\n", + "* **time** (string): an ISO-8601 date or interval (e.g. `\"1953-01-01/1960-01-01\"`) restricting the period retrieved." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Example 1: Retrieve streamflow peak data for two USGS monitoring sites" + "#### Example 1: Retrieve peak discharge for two USGS monitoring locations" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:52.520431Z", + "iopub.status.busy": "2026-05-26T16:32:52.520162Z", + "iopub.status.idle": "2026-05-26T16:32:53.056653Z", + "shell.execute_reply": "2026-05-26T16:32:53.056044Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: peaks · 1 page · 57 rows · 924/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 57 peak values.\n" + ] + } + ], "source": [ - "site_ids = [\"01594440\", \"040851325\"]\n", - "peak_data = nwis.get_discharge_peaks(site_ids)\n", - "print(\"Retrieved \" + str(len(peak_data[0])) + \" data values.\")" + "site_ids = [\"USGS-01594440\", \"USGS-040851325\"]\n", + "peak_data = waterdata.get_peaks(\n", + " monitoring_location_id=site_ids, parameter_code=\"00060\"\n", + ")\n", + "print(\"Retrieved \" + str(len(peak_data[0])) + \" peak values.\")" ] }, { @@ -86,18 +128,1336 @@ "source": [ "### Interpreting the Result\n", "\n", - "The result of calling the `get_discharge_peaks()` function is an object that contains a Pandas data frame object and an associated metadata object. The Pandas data frame contains the discharge peak values for the requested site(s).\n", + "Each `waterdata` function returns a tuple of a pandas data frame and a metadata object. The data frame contains one row per annual peak, including the peak `value`, its `time`, and the `water_year`.\n", "\n", - "Once you've got the data frame, there's several useful things you can do to explore the data.\n", + "Once you've got the data frame, there are several useful things you can do to explore the data.\n", "\n", "Display the data frame as a table." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:53.058237Z", + "iopub.status.busy": "2026-05-26T16:32:53.058107Z", + "iopub.status.idle": "2026-05-26T16:32:53.074655Z", + "shell.execute_reply": "2026-05-26T16:32:53.074200Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
03ae666b2-6658-4cd5-b0d9-9435e327f30ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s311002026-05-01 19:28:03.170712+00:001972-06-2219721972622NaNNone
1c2058a02-3a57-4867-b1eb-973d552bfa54POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s106002026-05-01 19:28:03.170712+00:001978-01-2719781978127NaNNone
2513931e9-5d25-499b-9433-2e1b23f6a4a1POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s115002026-05-01 19:28:03.170712+00:001979-09-071979197997NaNNone
33c4e6c32-42f1-4515-b90c-3194394ac576POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s39402026-05-01 19:28:03.170712+00:001979-10-11198019791011NaNNone
45df555c2-e9f7-4deb-bac5-3467d4eecf5cPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s16402026-05-01 19:28:03.170712+00:001981-02-2419811981224NaNNone
55efca373-025b-4980-89ce-b5dcaa6639d8POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s33802026-05-01 19:28:03.170712+00:001982-02-041982198224NaNNone
61d58764d-52c4-4344-bd29-9ab8852d1d89POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s57502026-05-01 19:28:03.170712+00:001983-06-2119831983621NaNNone
765bedc17-bc1f-4412-897e-c9c7bc763552POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s43402026-05-01 19:28:03.170712+00:001984-03-3019841984330NaNNone
86bedc7e5-5cc8-450d-b5a1-50449b20fe22POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s47302026-05-01 19:28:03.170712+00:001985-02-1319851985213NaNNone
92b87d057-7c2b-4cb7-bf44-576545008979POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s15202026-05-01 19:28:03.170712+00:001986-04-1619861986416NaNNone
10bf952852-cee7-43bc-88c9-70b197c4e62ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s40602026-05-01 19:28:03.170712+00:001986-12-25198719861225NaNNone
11867ee68a-f8ea-4f55-a2e1-5e9df0077fcdPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s35102026-05-01 19:28:03.170712+00:001987-11-30198819871130NaNNone
1210718b58-5763-44e5-a044-812c677f0b61POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s91902026-05-01 19:28:03.170712+00:001989-05-071989198957NaNNone
133dc8ee6a-c076-43d4-977a-e7252a94cac7POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s31402026-05-01 19:28:03.170712+00:001990-05-3019901990530NaNNone
14b9294137-2fad-4074-bd86-cf52f727ede6POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s47502026-05-01 19:28:03.170712+00:001991-03-2419911991324NaNNone
15537bb753-c6d6-49a1-a4dc-2b16b29256b5POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s32002026-05-01 19:28:03.170712+00:001992-03-2719921992327NaNNone
16dddd1dbe-b92a-4739-84b8-350918ecdd7cPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s55502026-05-01 19:28:03.170712+00:001993-03-051993199335NaNNone
1729e958a5-ebdb-4290-8fd8-843019b62155POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s69602026-05-01 19:28:03.170712+00:001993-11-29199419931129NaNNone
18942af9b6-7ace-4d6a-8cb2-f359e9511af3POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s41002026-05-01 19:28:03.170712+00:001995-03-091995199539NaNNone
1909d0f0e1-c7ac-4136-b4f8-e4639b313386POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s82802026-05-01 19:28:03.170712+00:001996-01-2019961996120NaNNone
209abb8890-e9c6-45bb-a302-678dc691207ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s69002026-05-01 19:28:03.170712+00:001996-11-0919971996119NaNNone
2101766a4b-dab4-4698-9c6a-3ef8f2e11153POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s48402026-05-01 19:28:03.170712+00:001998-03-1019981998310NaNNone
229e4dd73c-afd1-4fd2-95f4-3f5b3af886c4POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s82002026-05-01 19:28:03.170712+00:001999-09-1719991999917NaNNone
2301ad0ff4-f40e-4ad1-93e4-9460f5de1353POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s36402026-05-01 19:28:03.170712+00:002000-03-2220002000322NaNNone
243ea2d208-4285-4b9a-adeb-9b17b0d67feaPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s38002026-05-01 19:28:03.170712+00:002001-06-08200120016810:30:00+00:00None
259ad09dcd-0e49-4baf-a5dd-cb3b1dfb4c1aPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s15102026-05-01 19:28:03.170712+00:002002-04-2920022002429NaNNone
26406312f8-f7ff-4e68-ade4-8b4dd426bd44POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s69902026-05-01 19:28:03.170712+00:002003-02-242003200322400:30:00+00:00None
27e4550eba-d130-48e4-9abe-f7077b130ee8POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s57902026-05-01 19:28:03.170712+00:002003-12-1220042003121215:45:00+00:00None
28c06f6cf2-8668-4f70-851a-b8d704a24807POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s52102026-05-01 19:28:03.170712+00:002005-04-03200520054323:15:00+00:00None
2990bfb29a-82c2-4bba-85cb-f58b87874e28POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s2672026-05-05 03:36:50.236554+00:002005-08-1820052005818NaNNone
30b94cec80-5c1a-4c93-ad21-a6eea949ab67POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s2882026-05-05 03:36:50.236554+00:002006-03-1320062006313NaNNone
31b5fc68ad-d404-412a-8bdf-5df5c135887fPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s127002026-05-01 19:28:03.170712+00:002006-06-272006200662703:00:00+00:00None
32ce07ea88-b517-40a9-8a79-d45d0708c4caPOINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s3012026-05-05 03:36:50.236554+00:002007-03-1420072007314NaNNone
3333973c47-235c-49a6-ad3f-d4ae6dbbe64ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s55202026-05-01 19:28:03.170712+00:002007-04-162007200741615:15:00+00:00None
34c64dc6a7-4a8d-414a-9eae-fbbb9f5faf93POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s78602026-05-01 19:28:03.170712+00:002008-05-132008200851306:00:00+00:00None
356d756602-b9e4-4063-ad59-870b56014f4dPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s41302026-05-01 19:28:03.170712+00:002009-06-192009200961909:45:00+00:00None
3629ffbe23-6ed6-4c4a-97be-062acb4a4877POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s57802026-05-01 19:28:03.170712+00:002010-03-142010201031417:00:00+00:00None
37c4324b2a-1ef8-4aed-b3f2-26fbbd57cb79POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s3862026-05-05 03:36:50.236554+00:002010-03-1420102010314NaNNone
386074a5e7-4cb5-4177-98c4-66399fb74609POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s4362026-05-05 03:36:50.236554+00:002011-03-1820112011318NaNNone
39ba30e845-6905-41ec-af84-92804d870a0ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s168002026-05-01 19:28:03.170712+00:002011-09-08201120119817:15:00+00:00None
403b9cb606-0785-4d3e-9e52-3efa0ad559d9POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s49002026-05-01 19:28:03.170712+00:002011-12-092012201112901:30:00+00:00None
413bc2c0c3-50d0-44fe-a640-32a8219364b1POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s1502026-05-05 03:36:50.236554+00:002012-03-092012201239NaNNone
427a277bcb-435f-4b1e-b67c-dd5dac818ac2POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s108002026-05-01 19:28:03.170712+00:002012-10-3120132012103103:00:00+00:00None
439c628482-896d-4a8a-b12f-091b216cef52POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s4092026-05-05 03:36:50.236554+00:002013-03-3120132013331NaNNone
44c46e2dc3-6a5c-47ce-9322-dbf417d03caaPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s156002026-05-01 19:28:03.170712+00:002014-05-01201420145121:30:00+00:00None
45bd89314d-4ba6-429f-83fa-567e5f73ae25POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s4292026-05-05 03:36:50.236554+00:002014-06-022014201462NaNNone
4684722b5e-c286-4d69-bdf1-75e05665adb0POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s66102026-05-01 19:28:03.170712+00:002015-06-282015201562822:15:00+00:00None
476caf417c-10b1-450d-957f-e5d43973afa9POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s61402026-05-01 19:28:03.170712+00:002016-08-01201620168104:30:00+00:00None
48e7d3ffaa-e6ff-486e-bbd0-5fcbb11a6268POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s49602026-05-01 19:28:03.170712+00:002017-07-302017201773005:15:00+00:00None
4999f75026-7ad8-4e9f-bec8-9da13af22530POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s83602026-05-01 19:28:03.170712+00:002018-06-04201820186422:30:00+00:00None
50f8b816ae-c4a4-4f99-aa80-470856f0c334POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s72202026-05-01 19:28:03.170712+00:002018-12-1720192018121704:30:00+00:00None
516ce447e3-ec2a-47df-bc1a-71ceff4e47fcPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s43402026-05-01 19:28:03.170712+00:002020-04-142020202041413:15:00+00:00None
52fd1f1fb2-0329-42ad-bdd1-866d46ef6461POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s40002026-05-01 19:28:03.170712+00:002020-11-1220212020111222:45:00+00:00None
53d353f7b9-eba2-41a9-baa8-fd787e199b23POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s45902026-05-01 19:28:03.170712+00:002021-10-3020222021103021:45:00+00:00None
54b68c3597-8210-4f49-8af6-05b14f003bb0POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s33302026-05-01 19:28:03.170712+00:002022-12-1620232022121619:15:00+00:00None
55850c52c4-5567-4e2f-b30a-fda4fcd0e121POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s74002026-05-01 19:28:03.170712+00:002024-01-112024202411100:15:00+00:00None
56f9895515-e652-483e-97d2-70412cafa722POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s23202026-05-01 19:28:03.170712+00:002025-06-192025202561918:45:00+00:00None
\n", + "
" + ], + "text/plain": [ + " peak_id geometry \\\n", + "0 3ae666b2-6658-4cd5-b0d9-9435e327f30e POINT (-76.69369 38.95592) \n", + "1 c2058a02-3a57-4867-b1eb-973d552bfa54 POINT (-76.69369 38.95592) \n", + "2 513931e9-5d25-499b-9433-2e1b23f6a4a1 POINT (-76.69369 38.95592) \n", + "3 3c4e6c32-42f1-4515-b90c-3194394ac576 POINT (-76.69369 38.95592) \n", + "4 5df555c2-e9f7-4deb-bac5-3467d4eecf5c POINT (-76.69369 38.95592) \n", + "5 5efca373-025b-4980-89ce-b5dcaa6639d8 POINT (-76.69369 38.95592) \n", + "6 1d58764d-52c4-4344-bd29-9ab8852d1d89 POINT (-76.69369 38.95592) \n", + "7 65bedc17-bc1f-4412-897e-c9c7bc763552 POINT (-76.69369 38.95592) \n", + "8 6bedc7e5-5cc8-450d-b5a1-50449b20fe22 POINT (-76.69369 38.95592) \n", + "9 2b87d057-7c2b-4cb7-bf44-576545008979 POINT (-76.69369 38.95592) \n", + "10 bf952852-cee7-43bc-88c9-70b197c4e62e POINT (-76.69369 38.95592) \n", + "11 867ee68a-f8ea-4f55-a2e1-5e9df0077fcd POINT (-76.69369 38.95592) \n", + "12 10718b58-5763-44e5-a044-812c677f0b61 POINT (-76.69369 38.95592) \n", + "13 3dc8ee6a-c076-43d4-977a-e7252a94cac7 POINT (-76.69369 38.95592) \n", + "14 b9294137-2fad-4074-bd86-cf52f727ede6 POINT (-76.69369 38.95592) \n", + "15 537bb753-c6d6-49a1-a4dc-2b16b29256b5 POINT (-76.69369 38.95592) \n", + "16 dddd1dbe-b92a-4739-84b8-350918ecdd7c POINT (-76.69369 38.95592) \n", + "17 29e958a5-ebdb-4290-8fd8-843019b62155 POINT (-76.69369 38.95592) \n", + "18 942af9b6-7ace-4d6a-8cb2-f359e9511af3 POINT (-76.69369 38.95592) \n", + "19 09d0f0e1-c7ac-4136-b4f8-e4639b313386 POINT (-76.69369 38.95592) \n", + "20 9abb8890-e9c6-45bb-a302-678dc691207e POINT (-76.69369 38.95592) \n", + "21 01766a4b-dab4-4698-9c6a-3ef8f2e11153 POINT (-76.69369 38.95592) \n", + "22 9e4dd73c-afd1-4fd2-95f4-3f5b3af886c4 POINT (-76.69369 38.95592) \n", + "23 01ad0ff4-f40e-4ad1-93e4-9460f5de1353 POINT (-76.69369 38.95592) \n", + "24 3ea2d208-4285-4b9a-adeb-9b17b0d67fea POINT (-76.69369 38.95592) \n", + "25 9ad09dcd-0e49-4baf-a5dd-cb3b1dfb4c1a POINT (-76.69369 38.95592) \n", + "26 406312f8-f7ff-4e68-ade4-8b4dd426bd44 POINT (-76.69369 38.95592) \n", + "27 e4550eba-d130-48e4-9abe-f7077b130ee8 POINT (-76.69369 38.95592) \n", + "28 c06f6cf2-8668-4f70-851a-b8d704a24807 POINT (-76.69369 38.95592) \n", + "29 90bfb29a-82c2-4bba-85cb-f58b87874e28 POINT (-87.93621 44.5011) \n", + "30 b94cec80-5c1a-4c93-ad21-a6eea949ab67 POINT (-87.93621 44.5011) \n", + "31 b5fc68ad-d404-412a-8bdf-5df5c135887f POINT (-76.69369 38.95592) \n", + "32 ce07ea88-b517-40a9-8a79-d45d0708c4ca POINT (-87.93621 44.5011) \n", + "33 33973c47-235c-49a6-ad3f-d4ae6dbbe64e POINT (-76.69369 38.95592) \n", + "34 c64dc6a7-4a8d-414a-9eae-fbbb9f5faf93 POINT (-76.69369 38.95592) \n", + "35 6d756602-b9e4-4063-ad59-870b56014f4d POINT (-76.69369 38.95592) \n", + "36 29ffbe23-6ed6-4c4a-97be-062acb4a4877 POINT (-76.69369 38.95592) \n", + "37 c4324b2a-1ef8-4aed-b3f2-26fbbd57cb79 POINT (-87.93621 44.5011) \n", + "38 6074a5e7-4cb5-4177-98c4-66399fb74609 POINT (-87.93621 44.5011) \n", + "39 ba30e845-6905-41ec-af84-92804d870a0e POINT (-76.69369 38.95592) \n", + "40 3b9cb606-0785-4d3e-9e52-3efa0ad559d9 POINT (-76.69369 38.95592) \n", + "41 3bc2c0c3-50d0-44fe-a640-32a8219364b1 POINT (-87.93621 44.5011) \n", + "42 7a277bcb-435f-4b1e-b67c-dd5dac818ac2 POINT (-76.69369 38.95592) \n", + "43 9c628482-896d-4a8a-b12f-091b216cef52 POINT (-87.93621 44.5011) \n", + "44 c46e2dc3-6a5c-47ce-9322-dbf417d03caa POINT (-76.69369 38.95592) \n", + "45 bd89314d-4ba6-429f-83fa-567e5f73ae25 POINT (-87.93621 44.5011) \n", + "46 84722b5e-c286-4d69-bdf1-75e05665adb0 POINT (-76.69369 38.95592) \n", + "47 6caf417c-10b1-450d-957f-e5d43973afa9 POINT (-76.69369 38.95592) \n", + "48 e7d3ffaa-e6ff-486e-bbd0-5fcbb11a6268 POINT (-76.69369 38.95592) \n", + "49 99f75026-7ad8-4e9f-bec8-9da13af22530 POINT (-76.69369 38.95592) \n", + "50 f8b816ae-c4a4-4f99-aa80-470856f0c334 POINT (-76.69369 38.95592) \n", + "51 6ce447e3-ec2a-47df-bc1a-71ceff4e47fc POINT (-76.69369 38.95592) \n", + "52 fd1f1fb2-0329-42ad-bdd1-866d46ef6461 POINT (-76.69369 38.95592) \n", + "53 d353f7b9-eba2-41a9-baa8-fd787e199b23 POINT (-76.69369 38.95592) \n", + "54 b68c3597-8210-4f49-8af6-05b14f003bb0 POINT (-76.69369 38.95592) \n", + "55 850c52c4-5567-4e2f-b30a-fda4fcd0e121 POINT (-76.69369 38.95592) \n", + "56 f9895515-e652-483e-97d2-70412cafa722 POINT (-76.69369 38.95592) \n", + "\n", + " time_series_id monitoring_location_id parameter_code \\\n", + "0 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "1 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "2 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "3 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "4 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "5 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "6 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "7 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "8 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "9 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "10 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "11 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "12 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "13 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "14 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "15 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "16 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "17 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "18 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "19 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "20 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "21 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "22 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "23 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "24 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "25 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "26 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "27 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "28 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "29 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "30 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "31 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "32 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "33 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "34 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "35 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "36 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "37 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "38 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "39 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "40 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "41 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "42 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "43 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "44 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "45 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", + "46 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "47 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "48 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "49 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "50 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "51 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "52 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "53 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "54 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "55 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "56 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", + "\n", + " unit_of_measure value last_modified time \\\n", + "0 ft^3/s 31100 2026-05-01 19:28:03.170712+00:00 1972-06-22 \n", + "1 ft^3/s 10600 2026-05-01 19:28:03.170712+00:00 1978-01-27 \n", + "2 ft^3/s 11500 2026-05-01 19:28:03.170712+00:00 1979-09-07 \n", + "3 ft^3/s 3940 2026-05-01 19:28:03.170712+00:00 1979-10-11 \n", + "4 ft^3/s 1640 2026-05-01 19:28:03.170712+00:00 1981-02-24 \n", + "5 ft^3/s 3380 2026-05-01 19:28:03.170712+00:00 1982-02-04 \n", + "6 ft^3/s 5750 2026-05-01 19:28:03.170712+00:00 1983-06-21 \n", + "7 ft^3/s 4340 2026-05-01 19:28:03.170712+00:00 1984-03-30 \n", + "8 ft^3/s 4730 2026-05-01 19:28:03.170712+00:00 1985-02-13 \n", + "9 ft^3/s 1520 2026-05-01 19:28:03.170712+00:00 1986-04-16 \n", + "10 ft^3/s 4060 2026-05-01 19:28:03.170712+00:00 1986-12-25 \n", + "11 ft^3/s 3510 2026-05-01 19:28:03.170712+00:00 1987-11-30 \n", + "12 ft^3/s 9190 2026-05-01 19:28:03.170712+00:00 1989-05-07 \n", + "13 ft^3/s 3140 2026-05-01 19:28:03.170712+00:00 1990-05-30 \n", + "14 ft^3/s 4750 2026-05-01 19:28:03.170712+00:00 1991-03-24 \n", + "15 ft^3/s 3200 2026-05-01 19:28:03.170712+00:00 1992-03-27 \n", + "16 ft^3/s 5550 2026-05-01 19:28:03.170712+00:00 1993-03-05 \n", + "17 ft^3/s 6960 2026-05-01 19:28:03.170712+00:00 1993-11-29 \n", + "18 ft^3/s 4100 2026-05-01 19:28:03.170712+00:00 1995-03-09 \n", + "19 ft^3/s 8280 2026-05-01 19:28:03.170712+00:00 1996-01-20 \n", + "20 ft^3/s 6900 2026-05-01 19:28:03.170712+00:00 1996-11-09 \n", + "21 ft^3/s 4840 2026-05-01 19:28:03.170712+00:00 1998-03-10 \n", + "22 ft^3/s 8200 2026-05-01 19:28:03.170712+00:00 1999-09-17 \n", + "23 ft^3/s 3640 2026-05-01 19:28:03.170712+00:00 2000-03-22 \n", + "24 ft^3/s 3800 2026-05-01 19:28:03.170712+00:00 2001-06-08 \n", + "25 ft^3/s 1510 2026-05-01 19:28:03.170712+00:00 2002-04-29 \n", + "26 ft^3/s 6990 2026-05-01 19:28:03.170712+00:00 2003-02-24 \n", + "27 ft^3/s 5790 2026-05-01 19:28:03.170712+00:00 2003-12-12 \n", + "28 ft^3/s 5210 2026-05-01 19:28:03.170712+00:00 2005-04-03 \n", + "29 ft^3/s 267 2026-05-05 03:36:50.236554+00:00 2005-08-18 \n", + "30 ft^3/s 288 2026-05-05 03:36:50.236554+00:00 2006-03-13 \n", + "31 ft^3/s 12700 2026-05-01 19:28:03.170712+00:00 2006-06-27 \n", + "32 ft^3/s 301 2026-05-05 03:36:50.236554+00:00 2007-03-14 \n", + "33 ft^3/s 5520 2026-05-01 19:28:03.170712+00:00 2007-04-16 \n", + "34 ft^3/s 7860 2026-05-01 19:28:03.170712+00:00 2008-05-13 \n", + "35 ft^3/s 4130 2026-05-01 19:28:03.170712+00:00 2009-06-19 \n", + "36 ft^3/s 5780 2026-05-01 19:28:03.170712+00:00 2010-03-14 \n", + "37 ft^3/s 386 2026-05-05 03:36:50.236554+00:00 2010-03-14 \n", + "38 ft^3/s 436 2026-05-05 03:36:50.236554+00:00 2011-03-18 \n", + "39 ft^3/s 16800 2026-05-01 19:28:03.170712+00:00 2011-09-08 \n", + "40 ft^3/s 4900 2026-05-01 19:28:03.170712+00:00 2011-12-09 \n", + "41 ft^3/s 150 2026-05-05 03:36:50.236554+00:00 2012-03-09 \n", + "42 ft^3/s 10800 2026-05-01 19:28:03.170712+00:00 2012-10-31 \n", + "43 ft^3/s 409 2026-05-05 03:36:50.236554+00:00 2013-03-31 \n", + "44 ft^3/s 15600 2026-05-01 19:28:03.170712+00:00 2014-05-01 \n", + "45 ft^3/s 429 2026-05-05 03:36:50.236554+00:00 2014-06-02 \n", + "46 ft^3/s 6610 2026-05-01 19:28:03.170712+00:00 2015-06-28 \n", + "47 ft^3/s 6140 2026-05-01 19:28:03.170712+00:00 2016-08-01 \n", + "48 ft^3/s 4960 2026-05-01 19:28:03.170712+00:00 2017-07-30 \n", + "49 ft^3/s 8360 2026-05-01 19:28:03.170712+00:00 2018-06-04 \n", + "50 ft^3/s 7220 2026-05-01 19:28:03.170712+00:00 2018-12-17 \n", + "51 ft^3/s 4340 2026-05-01 19:28:03.170712+00:00 2020-04-14 \n", + "52 ft^3/s 4000 2026-05-01 19:28:03.170712+00:00 2020-11-12 \n", + "53 ft^3/s 4590 2026-05-01 19:28:03.170712+00:00 2021-10-30 \n", + "54 ft^3/s 3330 2026-05-01 19:28:03.170712+00:00 2022-12-16 \n", + "55 ft^3/s 7400 2026-05-01 19:28:03.170712+00:00 2024-01-11 \n", + "56 ft^3/s 2320 2026-05-01 19:28:03.170712+00:00 2025-06-19 \n", + "\n", + " water_year year month day time_of_day peak_since \n", + "0 1972 1972 6 22 NaN None \n", + "1 1978 1978 1 27 NaN None \n", + "2 1979 1979 9 7 NaN None \n", + "3 1980 1979 10 11 NaN None \n", + "4 1981 1981 2 24 NaN None \n", + "5 1982 1982 2 4 NaN None \n", + "6 1983 1983 6 21 NaN None \n", + "7 1984 1984 3 30 NaN None \n", + "8 1985 1985 2 13 NaN None \n", + "9 1986 1986 4 16 NaN None \n", + "10 1987 1986 12 25 NaN None \n", + "11 1988 1987 11 30 NaN None \n", + "12 1989 1989 5 7 NaN None \n", + "13 1990 1990 5 30 NaN None \n", + "14 1991 1991 3 24 NaN None \n", + "15 1992 1992 3 27 NaN None \n", + "16 1993 1993 3 5 NaN None \n", + "17 1994 1993 11 29 NaN None \n", + "18 1995 1995 3 9 NaN None \n", + "19 1996 1996 1 20 NaN None \n", + "20 1997 1996 11 9 NaN None \n", + "21 1998 1998 3 10 NaN None \n", + "22 1999 1999 9 17 NaN None \n", + "23 2000 2000 3 22 NaN None \n", + "24 2001 2001 6 8 10:30:00+00:00 None \n", + "25 2002 2002 4 29 NaN None \n", + "26 2003 2003 2 24 00:30:00+00:00 None \n", + "27 2004 2003 12 12 15:45:00+00:00 None \n", + "28 2005 2005 4 3 23:15:00+00:00 None \n", + "29 2005 2005 8 18 NaN None \n", + "30 2006 2006 3 13 NaN None \n", + "31 2006 2006 6 27 03:00:00+00:00 None \n", + "32 2007 2007 3 14 NaN None \n", + "33 2007 2007 4 16 15:15:00+00:00 None \n", + "34 2008 2008 5 13 06:00:00+00:00 None \n", + "35 2009 2009 6 19 09:45:00+00:00 None \n", + "36 2010 2010 3 14 17:00:00+00:00 None \n", + "37 2010 2010 3 14 NaN None \n", + "38 2011 2011 3 18 NaN None \n", + "39 2011 2011 9 8 17:15:00+00:00 None \n", + "40 2012 2011 12 9 01:30:00+00:00 None \n", + "41 2012 2012 3 9 NaN None \n", + "42 2013 2012 10 31 03:00:00+00:00 None \n", + "43 2013 2013 3 31 NaN None \n", + "44 2014 2014 5 1 21:30:00+00:00 None \n", + "45 2014 2014 6 2 NaN None \n", + "46 2015 2015 6 28 22:15:00+00:00 None \n", + "47 2016 2016 8 1 04:30:00+00:00 None \n", + "48 2017 2017 7 30 05:15:00+00:00 None \n", + "49 2018 2018 6 4 22:30:00+00:00 None \n", + "50 2019 2018 12 17 04:30:00+00:00 None \n", + "51 2020 2020 4 14 13:15:00+00:00 None \n", + "52 2021 2020 11 12 22:45:00+00:00 None \n", + "53 2022 2021 10 30 21:45:00+00:00 None \n", + "54 2023 2022 12 16 19:15:00+00:00 None \n", + "55 2024 2024 1 11 00:15:00+00:00 None \n", + "56 2025 2025 6 19 18:45:00+00:00 None " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "display(peak_data[0])" ] @@ -111,9 +1471,39 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:53.076772Z", + "iopub.status.busy": "2026-05-26T16:32:53.076623Z", + "iopub.status.idle": "2026-05-26T16:32:53.080026Z", + "shell.execute_reply": "2026-05-26T16:32:53.079488Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "peak_id str\n", + "geometry geometry\n", + "time_series_id str\n", + "monitoring_location_id str\n", + "parameter_code str\n", + "unit_of_measure str\n", + "value int64\n", + "last_modified datetime64[us, UTC]\n", + "time datetime64[us]\n", + "water_year int64\n", + "year int64\n", + "month int64\n", + "day int64\n", + "time_of_day str\n", + "peak_since object\n", + "dtype: object\n" + ] + } + ], "source": [ "print(peak_data[0].dtypes)" ] @@ -122,34 +1512,80 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The other part of the result returned from the `get_dv()` function is a metadata object that contains information about the query that was executed to return the data. For example, you can access the URL that was assembled to retrieve the requested data from the USGS web service. The USGS web service responses contain a descriptive header that defines and can be helpful in interpreting the contents of the response." + "The other part of the result is a metadata object describing the query that was executed. For example, you can access the URL that was assembled to retrieve the requested data from the USGS Water Data API." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:53.081836Z", + "iopub.status.busy": "2026-05-26T16:32:53.081722Z", + "iopub.status.idle": "2026-05-26T16:32:53.084237Z", + "shell.execute_reply": "2026-05-26T16:32:53.083710Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The query URL used to retrieve the data was: https://api.waterdata.usgs.gov/ogcapi/v0/collections/peaks/items?monitoring_location_id=USGS-01594440%2CUSGS-040851325¶meter_code=00060&skipGeometry=false&limit=50000\n" + ] + } + ], "source": [ - "print(\"The query URL used to retrieve the data from NWIS was: \" + peak_data[1].url)" + "print(\"The query URL used to retrieve the data was: \" + peak_data[1].url)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The following example is the same as the previous example but with multi index turned off (multi_index=False)" + "Peak records cover multiple parameters. By default `get_peaks()` returns both peak discharge (`00060`) and the gage height at the peak (`00065`); inspect the `parameter_code` column to see which are present." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:53.085717Z", + "iopub.status.busy": "2026-05-26T16:32:53.085625Z", + "iopub.status.idle": "2026-05-26T16:32:53.771173Z", + "shell.execute_reply": "2026-05-26T16:32:53.770633Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: peaks · 1 page · 97 rows · 924/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "['00060', '00065']\n", + "Length: 2, dtype: str\n" + ] + } + ], "source": [ - "site_ids = [\"01594440\", \"040851325\"]\n", - "peak_data = nwis.get_discharge_peaks(site_ids, multi_index=False)\n", - "print(\"Retrieved \" + str(len(peak_data[0])) + \" data values.\")" + "all_params = waterdata.get_peaks(monitoring_location_id=\"USGS-01594440\")\n", + "print(all_params[0][\"parameter_code\"].unique())" ] }, { @@ -158,17 +1594,734 @@ "source": [ "### Additional Examples\n", "\n", - "Example 2: Retrieve discharge peaks for a single site." + "#### Example 2: Retrieve peak discharge for a single site." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 8, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:53.773306Z", + "iopub.status.busy": "2026-05-26T16:32:53.773149Z", + "iopub.status.idle": "2026-05-26T16:32:54.494388Z", + "shell.execute_reply": "2026-05-26T16:32:54.493754Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: peaks · 1 page · 29 rows · 924/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
08489606e-89e2-4cab-9758-969be99c7efcPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9642026-05-06 17:32:44.238660+00:001937-04-2819371937428.0NoneNone
12091dc02-2a41-4bd7-a38d-018aff41d2d3POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s10302026-05-06 17:32:44.238660+00:001938-04-2719381938427.0NoneNone
254d4673d-232b-4c0f-84ac-3f2cfa25eae6POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8292026-05-06 17:32:44.238660+00:001939-04-1919391939419.0NoneNone
3d65a02cd-7e38-419e-9ca0-f82f7ffb66bbPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5902026-05-06 17:32:44.238660+00:001940-04-1419401940414.0NoneNone
4873cdcce-0b39-4328-b5d2-279318692e40POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s4302026-05-06 17:32:44.238660+00:001941-04-1119411941411.0NoneNone
595dc6356-39c1-4a3b-b6e4-05f346366e70POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8832026-05-06 17:32:44.238660+00:001942-04-1219421942412.0NoneNone
63889ccaf-cac9-40fd-8900-56ee32fad15fPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s6812026-05-06 17:32:44.238660+00:001945-05-041945194554.0NoneNone
713c36dc5-80ec-43b0-96f5-62a072a7d2d3POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9612026-05-06 17:32:44.238660+00:001946-04-2119461946421.0NoneNone
857120111-fa2d-476e-bdf7-603cbfb03350POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7812026-05-06 17:32:44.238660+00:001947-05-021947194752.0NoneNone
98e99a440-8114-4ded-9d68-5a06f9d7c7eaPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8202026-05-06 17:32:44.238660+00:001948-04-3019481948430.0NoneNone
104de7db39-d552-4e44-9625-e80af36f552cPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7262026-05-06 17:32:44.238660+00:001949-04-2719491949427.0NoneNone
11f9a71afe-de67-497e-a6d4-9f660041f0dbPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7812026-05-06 17:32:44.238660+00:001950-04-2219501950422.0NoneNone
12f2dac095-aeac-4999-bfb7-4092d1651536POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s6402026-05-06 17:32:44.238660+00:001951-04-2319511951423.0NoneNone
1342dfc367-59cf-4d12-ab62-405f856697caPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s13602026-05-06 17:32:44.238660+00:001952-04-3019521952430.0NoneNone
14c8c99d0a-1172-4ef4-adf4-892331793e1ePOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7152026-05-06 17:32:44.238660+00:001953-05-051953195355.0NoneNone
15dd627cb7-9bc0-43fc-a492-cbc38d0b48d7POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5462026-05-06 17:32:44.238660+00:001954-04-2319541954423.0NoneNone
16c1e1d9c4-6ad8-4280-baf8-742511f5e9efPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5962026-05-06 17:32:44.238660+00:001956-04-2419561956424.0NoneNone
17d2f8e901-5fe4-41ca-944a-fb430e30b678POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s10202026-05-06 17:32:44.238660+00:001957-05-061957195756.0NoneNone
188eb49b9a-dad1-4017-b07f-5a34b8918062POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5902026-05-06 17:32:44.238660+00:001958-04-3019581958430.0NoneNone
19184e855e-57e8-4521-a570-97d9e15e8973POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s3522026-05-06 17:32:44.238660+00:001959-04-2219591959422.0NoneNone
2057371ba5-8341-4d3e-ac4a-3d6f74e2a515POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8002026-05-06 17:32:44.238660+00:001960-04-081960196048.0NoneNone
21185789b2-59cd-42ff-b37f-f1fc734f85b3POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s3002026-05-06 17:32:44.238660+00:001961-04-051961196145.0NoneNone
2294025d15-a950-46ee-915e-15eb9e009819POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9002026-05-06 17:32:44.238660+00:001962-04-2719621962427.0NoneNone
23945a0c09-7b64-4f4b-91bc-cd1d83b3a248POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s4862026-05-06 17:32:44.238660+00:001963-04-2019631963420.0NoneNone
2410ae014a-2a37-466a-aba4-78540e1fda47POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7512026-05-06 17:32:44.238660+00:001964-04-2819641964428.0NoneNone
2559a2fcb8-bed1-4ac8-8582-bb63fb9f9e11POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9582026-05-06 17:32:44.238660+00:001965-04-2819651965428.0NoneNone
2649ea8bac-0e8a-4bec-b981-49a69c925ca7POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5242026-05-06 17:32:44.238660+00:001966-04-1619661966416.0NoneNone
276dee7073-0873-4765-91f1-ed8afb934620POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s6042026-05-06 17:32:44.238660+00:001967-05-1219671967512.0NoneNone
2821969473-a764-470b-8ec6-5fa316dbe362POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s11802026-05-06 17:32:44.238660+00:001984-05-01198419845NaNNoneNone
\n", + "
" + ], + "text/plain": [ + " peak_id geometry \\\n", + "0 8489606e-89e2-4cab-9758-969be99c7efc POINT (-112.05641 44.64793) \n", + "1 2091dc02-2a41-4bd7-a38d-018aff41d2d3 POINT (-112.05641 44.64793) \n", + "2 54d4673d-232b-4c0f-84ac-3f2cfa25eae6 POINT (-112.05641 44.64793) \n", + "3 d65a02cd-7e38-419e-9ca0-f82f7ffb66bb POINT (-112.05641 44.64793) \n", + "4 873cdcce-0b39-4328-b5d2-279318692e40 POINT (-112.05641 44.64793) \n", + "5 95dc6356-39c1-4a3b-b6e4-05f346366e70 POINT (-112.05641 44.64793) \n", + "6 3889ccaf-cac9-40fd-8900-56ee32fad15f POINT (-112.05641 44.64793) \n", + "7 13c36dc5-80ec-43b0-96f5-62a072a7d2d3 POINT (-112.05641 44.64793) \n", + "8 57120111-fa2d-476e-bdf7-603cbfb03350 POINT (-112.05641 44.64793) \n", + "9 8e99a440-8114-4ded-9d68-5a06f9d7c7ea POINT (-112.05641 44.64793) \n", + "10 4de7db39-d552-4e44-9625-e80af36f552c POINT (-112.05641 44.64793) \n", + "11 f9a71afe-de67-497e-a6d4-9f660041f0db POINT (-112.05641 44.64793) \n", + "12 f2dac095-aeac-4999-bfb7-4092d1651536 POINT (-112.05641 44.64793) \n", + "13 42dfc367-59cf-4d12-ab62-405f856697ca POINT (-112.05641 44.64793) \n", + "14 c8c99d0a-1172-4ef4-adf4-892331793e1e POINT (-112.05641 44.64793) \n", + "15 dd627cb7-9bc0-43fc-a492-cbc38d0b48d7 POINT (-112.05641 44.64793) \n", + "16 c1e1d9c4-6ad8-4280-baf8-742511f5e9ef POINT (-112.05641 44.64793) \n", + "17 d2f8e901-5fe4-41ca-944a-fb430e30b678 POINT (-112.05641 44.64793) \n", + "18 8eb49b9a-dad1-4017-b07f-5a34b8918062 POINT (-112.05641 44.64793) \n", + "19 184e855e-57e8-4521-a570-97d9e15e8973 POINT (-112.05641 44.64793) \n", + "20 57371ba5-8341-4d3e-ac4a-3d6f74e2a515 POINT (-112.05641 44.64793) \n", + "21 185789b2-59cd-42ff-b37f-f1fc734f85b3 POINT (-112.05641 44.64793) \n", + "22 94025d15-a950-46ee-915e-15eb9e009819 POINT (-112.05641 44.64793) \n", + "23 945a0c09-7b64-4f4b-91bc-cd1d83b3a248 POINT (-112.05641 44.64793) \n", + "24 10ae014a-2a37-466a-aba4-78540e1fda47 POINT (-112.05641 44.64793) \n", + "25 59a2fcb8-bed1-4ac8-8582-bb63fb9f9e11 POINT (-112.05641 44.64793) \n", + "26 49ea8bac-0e8a-4bec-b981-49a69c925ca7 POINT (-112.05641 44.64793) \n", + "27 6dee7073-0873-4765-91f1-ed8afb934620 POINT (-112.05641 44.64793) \n", + "28 21969473-a764-470b-8ec6-5fa316dbe362 POINT (-112.05641 44.64793) \n", + "\n", + " time_series_id monitoring_location_id parameter_code \\\n", + "0 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "1 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "2 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "3 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "4 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "5 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "6 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "7 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "8 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "9 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "10 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "11 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "12 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "13 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "14 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "15 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "16 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "17 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "18 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "19 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "20 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "21 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "22 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "23 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "24 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "25 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "26 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "27 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "28 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "\n", + " unit_of_measure value last_modified time \\\n", + "0 ft^3/s 964 2026-05-06 17:32:44.238660+00:00 1937-04-28 \n", + "1 ft^3/s 1030 2026-05-06 17:32:44.238660+00:00 1938-04-27 \n", + "2 ft^3/s 829 2026-05-06 17:32:44.238660+00:00 1939-04-19 \n", + "3 ft^3/s 590 2026-05-06 17:32:44.238660+00:00 1940-04-14 \n", + "4 ft^3/s 430 2026-05-06 17:32:44.238660+00:00 1941-04-11 \n", + "5 ft^3/s 883 2026-05-06 17:32:44.238660+00:00 1942-04-12 \n", + "6 ft^3/s 681 2026-05-06 17:32:44.238660+00:00 1945-05-04 \n", + "7 ft^3/s 961 2026-05-06 17:32:44.238660+00:00 1946-04-21 \n", + "8 ft^3/s 781 2026-05-06 17:32:44.238660+00:00 1947-05-02 \n", + "9 ft^3/s 820 2026-05-06 17:32:44.238660+00:00 1948-04-30 \n", + "10 ft^3/s 726 2026-05-06 17:32:44.238660+00:00 1949-04-27 \n", + "11 ft^3/s 781 2026-05-06 17:32:44.238660+00:00 1950-04-22 \n", + "12 ft^3/s 640 2026-05-06 17:32:44.238660+00:00 1951-04-23 \n", + "13 ft^3/s 1360 2026-05-06 17:32:44.238660+00:00 1952-04-30 \n", + "14 ft^3/s 715 2026-05-06 17:32:44.238660+00:00 1953-05-05 \n", + "15 ft^3/s 546 2026-05-06 17:32:44.238660+00:00 1954-04-23 \n", + "16 ft^3/s 596 2026-05-06 17:32:44.238660+00:00 1956-04-24 \n", + "17 ft^3/s 1020 2026-05-06 17:32:44.238660+00:00 1957-05-06 \n", + "18 ft^3/s 590 2026-05-06 17:32:44.238660+00:00 1958-04-30 \n", + "19 ft^3/s 352 2026-05-06 17:32:44.238660+00:00 1959-04-22 \n", + "20 ft^3/s 800 2026-05-06 17:32:44.238660+00:00 1960-04-08 \n", + "21 ft^3/s 300 2026-05-06 17:32:44.238660+00:00 1961-04-05 \n", + "22 ft^3/s 900 2026-05-06 17:32:44.238660+00:00 1962-04-27 \n", + "23 ft^3/s 486 2026-05-06 17:32:44.238660+00:00 1963-04-20 \n", + "24 ft^3/s 751 2026-05-06 17:32:44.238660+00:00 1964-04-28 \n", + "25 ft^3/s 958 2026-05-06 17:32:44.238660+00:00 1965-04-28 \n", + "26 ft^3/s 524 2026-05-06 17:32:44.238660+00:00 1966-04-16 \n", + "27 ft^3/s 604 2026-05-06 17:32:44.238660+00:00 1967-05-12 \n", + "28 ft^3/s 1180 2026-05-06 17:32:44.238660+00:00 1984-05-01 \n", + "\n", + " water_year year month day time_of_day peak_since \n", + "0 1937 1937 4 28.0 None None \n", + "1 1938 1938 4 27.0 None None \n", + "2 1939 1939 4 19.0 None None \n", + "3 1940 1940 4 14.0 None None \n", + "4 1941 1941 4 11.0 None None \n", + "5 1942 1942 4 12.0 None None \n", + "6 1945 1945 5 4.0 None None \n", + "7 1946 1946 4 21.0 None None \n", + "8 1947 1947 5 2.0 None None \n", + "9 1948 1948 4 30.0 None None \n", + "10 1949 1949 4 27.0 None None \n", + "11 1950 1950 4 22.0 None None \n", + "12 1951 1951 4 23.0 None None \n", + "13 1952 1952 4 30.0 None None \n", + "14 1953 1953 5 5.0 None None \n", + "15 1954 1954 4 23.0 None None \n", + "16 1956 1956 4 24.0 None None \n", + "17 1957 1957 5 6.0 None None \n", + "18 1958 1958 4 30.0 None None \n", + "19 1959 1959 4 22.0 None None \n", + "20 1960 1960 4 8.0 None None \n", + "21 1961 1961 4 5.0 None None \n", + "22 1962 1962 4 27.0 None None \n", + "23 1963 1963 4 20.0 None None \n", + "24 1964 1964 4 28.0 None None \n", + "25 1965 1965 4 28.0 None None \n", + "26 1966 1966 4 16.0 None None \n", + "27 1967 1967 5 12.0 None None \n", + "28 1984 1984 5 NaN None None " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "stations = \"06011000\"\n", - "data3 = nwis.get_discharge_peaks(stations)\n", + "station = \"USGS-06011000\"\n", + "data3 = waterdata.get_peaks(monitoring_location_id=station, parameter_code=\"00060\")\n", "display(data3[0])" ] }, @@ -176,16 +2329,231 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Example 3: Retrieve peak discharge data for a monitoring site between two dates" + "#### Example 3: Retrieve peak discharge for a monitoring location between two dates" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:32:54.496099Z", + "iopub.status.busy": "2026-05-26T16:32:54.495977Z", + "iopub.status.idle": "2026-05-26T16:32:54.955444Z", + "shell.execute_reply": "2026-05-26T16:32:54.954916Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: peaks · 1 page · 6 rows · 923/1,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
0c8c99d0a-1172-4ef4-adf4-892331793e1ePOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7152026-05-06 17:32:44.238660+00:001953-05-051953195355NoneNone
1dd627cb7-9bc0-43fc-a492-cbc38d0b48d7POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5462026-05-06 17:32:44.238660+00:001954-04-2319541954423NoneNone
2c1e1d9c4-6ad8-4280-baf8-742511f5e9efPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5962026-05-06 17:32:44.238660+00:001956-04-2419561956424NoneNone
3d2f8e901-5fe4-41ca-944a-fb430e30b678POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s10202026-05-06 17:32:44.238660+00:001957-05-061957195756NoneNone
48eb49b9a-dad1-4017-b07f-5a34b8918062POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5902026-05-06 17:32:44.238660+00:001958-04-3019581958430NoneNone
5184e855e-57e8-4521-a570-97d9e15e8973POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s3522026-05-06 17:32:44.238660+00:001959-04-2219591959422NoneNone
\n", + "
" + ], + "text/plain": [ + " peak_id geometry \\\n", + "0 c8c99d0a-1172-4ef4-adf4-892331793e1e POINT (-112.05641 44.64793) \n", + "1 dd627cb7-9bc0-43fc-a492-cbc38d0b48d7 POINT (-112.05641 44.64793) \n", + "2 c1e1d9c4-6ad8-4280-baf8-742511f5e9ef POINT (-112.05641 44.64793) \n", + "3 d2f8e901-5fe4-41ca-944a-fb430e30b678 POINT (-112.05641 44.64793) \n", + "4 8eb49b9a-dad1-4017-b07f-5a34b8918062 POINT (-112.05641 44.64793) \n", + "5 184e855e-57e8-4521-a570-97d9e15e8973 POINT (-112.05641 44.64793) \n", + "\n", + " time_series_id monitoring_location_id parameter_code \\\n", + "0 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "1 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "2 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "3 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "4 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "5 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", + "\n", + " unit_of_measure value last_modified time \\\n", + "0 ft^3/s 715 2026-05-06 17:32:44.238660+00:00 1953-05-05 \n", + "1 ft^3/s 546 2026-05-06 17:32:44.238660+00:00 1954-04-23 \n", + "2 ft^3/s 596 2026-05-06 17:32:44.238660+00:00 1956-04-24 \n", + "3 ft^3/s 1020 2026-05-06 17:32:44.238660+00:00 1957-05-06 \n", + "4 ft^3/s 590 2026-05-06 17:32:44.238660+00:00 1958-04-30 \n", + "5 ft^3/s 352 2026-05-06 17:32:44.238660+00:00 1959-04-22 \n", + "\n", + " water_year year month day time_of_day peak_since \n", + "0 1953 1953 5 5 None None \n", + "1 1954 1954 4 23 None None \n", + "2 1956 1956 4 24 None None \n", + "3 1957 1957 5 6 None None \n", + "4 1958 1958 4 30 None None \n", + "5 1959 1959 4 22 None None " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "data4 = nwis.get_discharge_peaks(stations, start=\"1953-01-01\", end=\"1960-01-01\")\n", + "data4 = waterdata.get_peaks(\n", + " monitoring_location_id=station,\n", + " parameter_code=\"00060\",\n", + " time=\"1953-01-01/1960-01-01\",\n", + ")\n", "display(data4[0])" ] } @@ -205,7 +2573,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.13.3" } }, "nbformat": 4, diff --git a/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb index 0daeba44..fec41c33 100644 --- a/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb @@ -4,9 +4,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# USGS dataretrieval Python Package `get_ratings()` Examples\n", + "# USGS dataretrieval Python Package Rating Curve Examples\n", "\n", - "This notebook provides examples of using the Python dataretrieval package to retrieve rating curve data for a United States Geological Survey (USGS) streamflow gage. The dataretrieval package provides a collection of functions to get data from the USGS National Water Information System (NWIS) and other online sources of hydrology and water quality data, including the United States Environmental Protection Agency (USEPA)." + "This notebook provides examples of using the Python dataretrieval package to retrieve stage–discharge rating curve data for United States Geological Survey (USGS) streamgages using the **USGS Water Data API** via the `waterdata` module. The `waterdata` module is the recommended way to access USGS water data and replaces the deprecated `nwis` module.\n", + "\n", + "Note: not all active USGS streamflow gages have traditional rating curves relating stage to discharge." ] }, { @@ -21,7 +23,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:35:38.570189Z", + "iopub.status.busy": "2026-05-26T16:35:38.570015Z", + "iopub.status.idle": "2026-05-26T16:35:41.024884Z", + "shell.execute_reply": "2026-05-26T16:35:41.024041Z" + } + }, "outputs": [], "source": [ "!pip install dataretrieval" @@ -36,15 +45,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:35:41.027533Z", + "iopub.status.busy": "2026-05-26T16:35:41.027350Z", + "iopub.status.idle": "2026-05-26T16:35:41.625447Z", + "shell.execute_reply": "2026-05-26T16:35:41.624733Z" + } + }, "outputs": [], "source": [ "from IPython.display import display\n", "\n", - "from dataretrieval import nwis\n", - "from dataretrieval import waterdata\n", - "import dataretrieval.waterdata as waterdata\n" + "from dataretrieval import waterdata\n" ] }, { @@ -53,37 +67,49 @@ "source": [ "### Basic Usage\n", "\n", - "The dataretrieval package has several functions that allow you to retrieve data from different web services. This example uses the `get_ratings()` function to retrieve rating curve data for a monitoring site from USGS NWIS. The following arguments are available:\n", + "This example uses the `get_ratings()` function to retrieve rating curve data for a monitoring location from the USGS Water Data STAC catalog. Commonly used arguments include:\n", "\n", - "Arguments (Additional arguments, if supplied, will be used as query parameters)\n", + "* **monitoring_location_id** (string or list of strings): USGS monitoring location id(s) in `AGENCY-ID` form (e.g. `\"USGS-10109000\"`).\n", + "* **file_type** (string or list): which rating file(s) to request — `\"exsa\"` (expanded, shift-adjusted; the default), `\"base\"`, or `\"corr\"`.\n", "\n", - "* **site** (string): A USGS site number. This is usually an 8 digit number as a string. If the nwis parameter site_no is supplied, it will overwrite the site parameter.\n", - "* **base** (string): Can be \"base\", \"corr\", or \"exsa\"\n", - "* **county** (string): County IDs from county lookup or \"ALL\"\n", - "* **categories** (Listlike): List or comma delimited string of Two-letter category abbreviations\n", - "\n", - "NOTE: Not all active USGS streamflow gages have traditional rating curves that relate stage to flow." + "Unlike most `waterdata` functions, `get_ratings()` returns a dictionary mapping each feature id (e.g. `\"USGS-10109000.exsa.rdb\"`) to a parsed pandas data frame." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Example 1: Get rating data for an NWIS Site" + "#### Example 1: Get the rating curve for a monitoring location" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:35:41.627674Z", + "iopub.status.busy": "2026-05-26T16:35:41.627479Z", + "iopub.status.idle": "2026-05-26T16:35:42.922768Z", + "shell.execute_reply": "2026-05-26T16:35:42.921494Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 409 rating points.\n" + ] + } + ], "source": [ - "# Specify the USGS site number/code\n", - "site_id = \"10109000\"\n", + "# Specify the USGS monitoring location id\n", + "site_id = \"USGS-10109000\"\n", "\n", - "# Get the rating curve data\n", - "ratingData = nwis.get_ratings(site=site_id, file_type=\"exsa\")\n", - "print(\"Retrieved \" + str(len(ratingData[0])) + \" data values.\")" + "# Get the (expanded, shift-adjusted) rating curve\n", + "ratings = waterdata.get_ratings(monitoring_location_id=site_id, file_type=\"exsa\")\n", + "rating = ratings[f\"{site_id}.exsa.rdb\"]\n", + "print(\"Retrieved \" + str(len(rating)) + \" rating points.\")" ] }, { @@ -92,30 +118,161 @@ "source": [ "### Interpreting the Result\n", "\n", - "The result of calling the `get_ratings()` function is an object that contains a Pandas data frame object and an associated metadata object. The Pandas data frame contains the rating curve data for the requested site.\n", - "\n", - "Once you've got the data frame, there's several useful things you can do to explore the data. You can execute the following code to display the data frame as a table.\n", + "`get_ratings()` returns a dictionary keyed by feature id; each value is a pandas data frame holding one rating table. For the `\"exsa\"` file type the columns are:\n", "\n", - "If the \"type\" parameter in the request has a value of \"base,\" then the columns in the data frame are as follows:\n", - "* INDEP - typically the gage height in feet\n", - "* DEP - typically the streamflow in cubic feet per second\n", - "* STOR - where an \"*\" indicates that the pair are a fixed point of the rating curve\n", + "* **INDEP** — typically the gage height, in feet\n", + "* **SHIFT** — the current shift in the rating for that value of INDEP\n", + "* **DEP** — typically the discharge, in cubic feet per second\n", + "* **STOR** — an `*` indicates the pair is a fixed point of the rating curve\n", "\n", - "If the \"type\" parameter is specified as \"exsa,\" then an additional column called SHIFT is included that indicates the current shift in the rating for that value of INDEP.\n", - "\n", - "If the \"type\" parameter is specified as \"corr,\" then the columns are as follows:\n", - "* INDEP - typically gage height in feet\n", - "* CORR - the correction for that value\n", - "* CORRINDEP - the corrected value for CORR" + "The `\"base\"` and `\"corr\"` file types provide alternative representations of the rating. You can display the data frame as a table." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:35:42.926145Z", + "iopub.status.busy": "2026-05-26T16:35:42.925867Z", + "iopub.status.idle": "2026-05-26T16:35:42.957032Z", + "shell.execute_reply": "2026-05-26T16:35:42.956252Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
INDEPSHIFTDEPSTOR
01.77-0.077.46NaN
11.78-0.077.87NaN
21.79-0.078.29NaN
31.80-0.078.73NaN
41.81-0.079.18NaN
...............
4045.810.002175.11NaN
4055.820.002186.34NaN
4065.830.002197.61NaN
4075.840.002208.91NaN
4085.850.002220.24*
\n", + "

409 rows × 4 columns

\n", + "
" + ], + "text/plain": [ + " INDEP SHIFT DEP STOR\n", + "0 1.77 -0.07 7.46 NaN\n", + "1 1.78 -0.07 7.87 NaN\n", + "2 1.79 -0.07 8.29 NaN\n", + "3 1.80 -0.07 8.73 NaN\n", + "4 1.81 -0.07 9.18 NaN\n", + ".. ... ... ... ...\n", + "404 5.81 0.00 2175.11 NaN\n", + "405 5.82 0.00 2186.34 NaN\n", + "406 5.83 0.00 2197.61 NaN\n", + "407 5.84 0.00 2208.91 NaN\n", + "408 5.85 0.00 2220.24 *\n", + "\n", + "[409 rows x 4 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "display(ratingData[0])" + "display(rating)" ] }, { @@ -127,45 +284,95 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:35:42.959125Z", + "iopub.status.busy": "2026-05-26T16:35:42.958970Z", + "iopub.status.idle": "2026-05-26T16:35:42.962068Z", + "shell.execute_reply": "2026-05-26T16:35:42.961463Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INDEP float64\n", + "SHIFT float64\n", + "DEP float64\n", + "STOR str\n", + "dtype: object\n" + ] + } + ], "source": [ - "print(ratingData[0].dtypes)" + "print(rating.dtypes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The other part of the result returned from the `get_ratings()` function is a metadata object that contains information about the query that was executed to return the data. For example, you can access the URL that was assembled to retrieve the requested data from the USGS web service. The USGS web service responses contain a descriptive header that defines and can be helpful in interpreting the contents of the response." + "Each rating data frame carries provenance in its `attrs`: `attrs[\"url\"]` records the catalog asset it was fetched from, and `attrs[\"comment\"]` holds the RDB header lines (rating id, parameter, last-shifted timestamp, etc.)." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:35:42.963937Z", + "iopub.status.busy": "2026-05-26T16:35:42.963829Z", + "iopub.status.idle": "2026-05-26T16:35:42.966039Z", + "shell.execute_reply": "2026-05-26T16:35:42.965512Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The rating was fetched from: https://api.waterdata.usgs.gov/stac-files/ratings/USGS.10109000.exsa.rdb\n" + ] + } + ], "source": [ - "print(\"The query URL used to retrieve the data from NWIS was: \" + ratingData[1].url)" + "print(\"The rating was fetched from: \" + rating.attrs[\"url\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Example 2: Get rating data for a different NWIS site by changing the site_id" + "#### Example 2: Get the rating curve for a different monitoring location" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:35:42.967727Z", + "iopub.status.busy": "2026-05-26T16:35:42.967608Z", + "iopub.status.idle": "2026-05-26T16:35:43.899866Z", + "shell.execute_reply": "2026-05-26T16:35:43.899110Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 2426 rating points.\n" + ] + } + ], "source": [ - "site_id = \"01594440\"\n", - "data = nwis.get_ratings(site=site_id, file_type=\"base\")\n", - "print(\"Retrieved \" + str(len(data[0])) + \" data values.\")" + "site_id = \"USGS-01594440\"\n", + "ratings = waterdata.get_ratings(monitoring_location_id=site_id, file_type=\"exsa\")\n", + "rating = ratings[f\"{site_id}.exsa.rdb\"]\n", + "print(\"Retrieved \" + str(len(rating)) + \" rating points.\")" ] } ], @@ -178,13 +385,14 @@ "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2" + "pygments_lexer": "ipython3", + "version": "3.13.3" } }, "nbformat": 4, diff --git a/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb index 808d2f9d..6ed4fa17 100644 --- a/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb @@ -4,9 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# USGS dataretrieval Python Package `get_stats()` Examples\n", + "# USGS dataretrieval Python Package Statistics Examples\n", "\n", - "This notebook provides examples of using the Python dataretrieval package to retrieve statistics for observed variables at a United States Geological Survey (USGS) monitoring site. The dataretrieval package provides a collection of functions to get data from the USGS National Water Information System (NWIS) and other online sources of hydrology and water quality data, including the United States Environmental Protection Agency (USEPA)." + "This notebook provides examples of using the Python dataretrieval package to retrieve summary statistics for observed variables at a United States Geological Survey (USGS) monitoring location using the **USGS Water Data API** via the `waterdata` module. The `waterdata` module is the recommended way to access USGS water data and replaces the deprecated `nwis` module.\n", + "\n", + "Two statistics functions are demonstrated:\n", + "\n", + "* `get_stats_date_range()` — monthly, calendar-year, and water-year summaries (the \"observationIntervals\" service).\n", + "* `get_stats_por()` — day-of-year and month-of-year summaries over the full period of record (the \"observationNormals\" service)." ] }, { @@ -21,7 +26,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:47.180991Z", + "iopub.status.busy": "2026-05-26T16:30:47.180861Z", + "iopub.status.idle": "2026-05-26T16:30:48.642662Z", + "shell.execute_reply": "2026-05-26T16:30:48.641964Z" + } + }, "outputs": [], "source": [ "!pip install dataretrieval" @@ -36,16 +48,21 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:48.645517Z", + "iopub.status.busy": "2026-05-26T16:30:48.645347Z", + "iopub.status.idle": "2026-05-26T16:30:49.348863Z", + "shell.execute_reply": "2026-05-26T16:30:49.348112Z" + } + }, "outputs": [], "source": [ "from IPython.display import display\n", "from matplotlib import ticker\n", "\n", - "from dataretrieval import nwis\n", - "from dataretrieval import waterdata\n", - "import dataretrieval.waterdata as waterdata\n" + "from dataretrieval import waterdata\n" ] }, { @@ -54,38 +71,68 @@ "source": [ "### Basic Usage\n", "\n", - "The dataretrieval package has several functions that allow you to retrieve data from different web services. This examples uses the `get_stats()` function to retrieve statistics for observed variable(s) for a USGS monitoring site from USGS NWIS. The following arguments are available:\n", - "\n", - "Arguments (Additional parameters, if supplied, will be used as query parameters).\n", + "This example uses `get_stats_date_range()` to retrieve monthly and annual statistics for an observed variable at a USGS monitoring location. Commonly used arguments include:\n", "\n", - "* **sites** (string or list of strings): A string or list of strings contining the USGS site identifiers for which to retrive data.\n", - "* **parameterCd** (string or list of strings): A list of USGS parameter codes for which to retrieve data.\n", - "* **statReportType** (string): The aggregation period for which statistics should be reported. Can be specified as 'daily' (default), 'monthly', or 'annual'.\n", - "* **statTypeCd** (string): The type of statistic to be returned in the result. Can be specified as 'all', 'mean', 'max', 'min', or 'median'" + "* **monitoring_location_id** (string or list of strings): USGS monitoring location id(s), formed as the agency code and site number joined by a hyphen (e.g. `\"USGS-02319394\"`).\n", + "* **parameter_code** (string or list of strings): 5-digit USGS parameter code(s), e.g. `\"00060\"` (discharge).\n", + "* **computation_type** (string or list of strings): the statistic(s) to compute — one or more of `arithmetic_mean`, `maximum`, `median`, `minimum`, `percentile`.\n", + "* **start_date** / **end_date** (string): optionally bound the period summarized, in `YYYY-MM-DD` format." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 1: Get all of the annual mean discharge data for a single site" + "#### Example 1: Get monthly and annual mean discharge for a single site" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:49.351135Z", + "iopub.status.busy": "2026-05-26T16:30:49.350950Z", + "iopub.status.idle": "2026-05-26T16:30:49.931817Z", + "shell.execute_reply": "2026-05-26T16:30:49.930934Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: observationIntervals · 1 page · 344 rows · 3,925/4,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieved 344 statistic values.\n" + ] + } + ], "source": [ "# Set the parameters needed to retrieve data\n", - "siteNumber = \"02319394\"\n", - "parameterCode = \"00060\" # Discharge\n", + "site = \"USGS-02319394\"\n", + "parameter_code = \"00060\" # Discharge\n", "\n", - "# Retrieve the statistics\n", - "x1 = nwis.get_stats(\n", - " sites=siteNumber, parameterCd=parameterCode, statReportType=\"annual\"\n", + "# Retrieve the statistics (monthly, calendar-year, and water-year means)\n", + "x1 = waterdata.get_stats_date_range(\n", + " monitoring_location_id=site,\n", + " parameter_code=parameter_code,\n", + " computation_type=\"arithmetic_mean\",\n", ")\n", - "print(\"Retrieved \" + str(len(x1[0])) + \" data values.\")" + "print(\"Retrieved \" + str(len(x1[0])) + \" statistic values.\")" ] }, { @@ -94,16 +141,411 @@ "source": [ "### Interpreting the Result\n", "\n", - "The result of calling the `get_stats()` function is an object that contains a Pandas data frame object and an associated metadata object. The Pandas data frame contains the statistics values for the site and observed variable requested.\n", + "Each `waterdata` function returns a tuple of a pandas data frame and a metadata object. The data frame holds the computed statistics; each row is one interval, identified by the `interval_type` column (`month`, `calendar_year`, or `water_year`), with the computed statistic in the `value` column.\n", "\n", - "Once you've got the data frame, there's several useful things you can do to explore the data." + "Once you've got the data frame, there are several useful things you can do to explore the data." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:49.933747Z", + "iopub.status.busy": "2026-05-26T16:30:49.933590Z", + "iopub.status.idle": "2026-05-26T16:30:49.948399Z", + "shell.execute_reply": "2026-05-26T16:30:49.947865Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
geometrymonitoring_location_idmonitoring_location_namesite_typesite_type_codecountry_codestate_codecounty_codestart_dateend_dateinterval_typevaluepercentilesample_countapproval_statuscomputation_idcomputationparameter_codeunit_of_measureparent_time_series_id
0POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792000-11-012000-11-30month600.967NaN30approved30eb30d0-c252-4601-a702-1e053e387200arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
1POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792000-12-012000-12-31month812.903NaN31approved0bfbd34c-53ed-429b-bea1-facc88c3c639arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
2POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792001-01-012001-01-31month1668.387NaN31approved533f6064-f298-4eb1-b74f-da0c09d8c525arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
3POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792001-02-012001-02-28month1234.286NaN28approved67ee8c57-e9d0-42a9-826f-a86dbac7a377arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
4POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792001-03-012001-03-31month3782.581NaN31approved7d13bc10-dacc-48c5-bf41-63c13569f47carithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
...............................................................
339POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792021-10-012022-09-30water_year2034.863NaN365approveda579a7a4-b5bc-45ce-a754-9d784d05dffearithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
340POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792022-10-012023-09-30water_year1731.784NaN365approvedef755db3-4cb0-44ec-9192-1a2f85708608arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
341POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792023-10-012024-09-30water_year3392.778NaN365approvedf73ed1da-35e0-4167-9387-cab6aad5961carithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
342POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792024-10-012025-09-30water_year1938.863NaN365approved5004f92c-d1f8-4c1c-9a41-ce1e73d15888arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
343POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792025-10-012026-02-08water_year394.382NaN131approved3f636529-a609-4e82-86b0-6c0e03c0072darithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
\n", + "

344 rows × 20 columns

\n", + "
" + ], + "text/plain": [ + " geometry monitoring_location_id \\\n", + "0 POINT (-83.18014 30.41049) USGS-02319394 \n", + "1 POINT (-83.18014 30.41049) USGS-02319394 \n", + "2 POINT (-83.18014 30.41049) USGS-02319394 \n", + "3 POINT (-83.18014 30.41049) USGS-02319394 \n", + "4 POINT (-83.18014 30.41049) USGS-02319394 \n", + ".. ... ... \n", + "339 POINT (-83.18014 30.41049) USGS-02319394 \n", + "340 POINT (-83.18014 30.41049) USGS-02319394 \n", + "341 POINT (-83.18014 30.41049) USGS-02319394 \n", + "342 POINT (-83.18014 30.41049) USGS-02319394 \n", + "343 POINT (-83.18014 30.41049) USGS-02319394 \n", + "\n", + " monitoring_location_name site_type site_type_code country_code \\\n", + "0 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "1 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "2 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "3 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "4 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + ".. ... ... ... ... \n", + "339 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "340 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "341 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "342 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "343 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "\n", + " state_code county_code start_date end_date interval_type value \\\n", + "0 12 079 2000-11-01 2000-11-30 month 600.967 \n", + "1 12 079 2000-12-01 2000-12-31 month 812.903 \n", + "2 12 079 2001-01-01 2001-01-31 month 1668.387 \n", + "3 12 079 2001-02-01 2001-02-28 month 1234.286 \n", + "4 12 079 2001-03-01 2001-03-31 month 3782.581 \n", + ".. ... ... ... ... ... ... \n", + "339 12 079 2021-10-01 2022-09-30 water_year 2034.863 \n", + "340 12 079 2022-10-01 2023-09-30 water_year 1731.784 \n", + "341 12 079 2023-10-01 2024-09-30 water_year 3392.778 \n", + "342 12 079 2024-10-01 2025-09-30 water_year 1938.863 \n", + "343 12 079 2025-10-01 2026-02-08 water_year 394.382 \n", + "\n", + " percentile sample_count approval_status \\\n", + "0 NaN 30 approved \n", + "1 NaN 31 approved \n", + "2 NaN 31 approved \n", + "3 NaN 28 approved \n", + "4 NaN 31 approved \n", + ".. ... ... ... \n", + "339 NaN 365 approved \n", + "340 NaN 365 approved \n", + "341 NaN 365 approved \n", + "342 NaN 365 approved \n", + "343 NaN 131 approved \n", + "\n", + " computation_id computation parameter_code \\\n", + "0 30eb30d0-c252-4601-a702-1e053e387200 arithmetic_mean 00060 \n", + "1 0bfbd34c-53ed-429b-bea1-facc88c3c639 arithmetic_mean 00060 \n", + "2 533f6064-f298-4eb1-b74f-da0c09d8c525 arithmetic_mean 00060 \n", + "3 67ee8c57-e9d0-42a9-826f-a86dbac7a377 arithmetic_mean 00060 \n", + "4 7d13bc10-dacc-48c5-bf41-63c13569f47c arithmetic_mean 00060 \n", + ".. ... ... ... \n", + "339 a579a7a4-b5bc-45ce-a754-9d784d05dffe arithmetic_mean 00060 \n", + "340 ef755db3-4cb0-44ec-9192-1a2f85708608 arithmetic_mean 00060 \n", + "341 f73ed1da-35e0-4167-9387-cab6aad5961c arithmetic_mean 00060 \n", + "342 5004f92c-d1f8-4c1c-9a41-ce1e73d15888 arithmetic_mean 00060 \n", + "343 3f636529-a609-4e82-86b0-6c0e03c0072d arithmetic_mean 00060 \n", + "\n", + " unit_of_measure parent_time_series_id \n", + "0 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "1 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "2 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "3 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "4 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + ".. ... ... \n", + "339 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "340 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "341 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "342 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "343 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", + "\n", + "[344 rows x 20 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Display the data frame as a table\n", "display(x1[0])" @@ -118,9 +560,44 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:49.949852Z", + "iopub.status.busy": "2026-05-26T16:30:49.949737Z", + "iopub.status.idle": "2026-05-26T16:30:49.952369Z", + "shell.execute_reply": "2026-05-26T16:30:49.951838Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "geometry geometry\n", + "monitoring_location_id str\n", + "monitoring_location_name str\n", + "site_type str\n", + "site_type_code str\n", + "country_code str\n", + "state_code str\n", + "county_code str\n", + "start_date str\n", + "end_date str\n", + "interval_type str\n", + "value str\n", + "percentile float64\n", + "sample_count int64\n", + "approval_status str\n", + "computation_id str\n", + "computation str\n", + "parameter_code str\n", + "unit_of_measure str\n", + "parent_time_series_id str\n", + "dtype: object\n" + ] + } + ], "source": [ "print(x1[0].dtypes)" ] @@ -129,16 +606,54 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Make a quick time series plot of the annual mean values." + "Make a quick time series plot of the annual (calendar-year) mean values." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:49.953886Z", + "iopub.status.busy": "2026-05-26T16:30:49.953772Z", + "iopub.status.idle": "2026-05-26T16:30:50.256250Z", + "shell.execute_reply": "2026-05-26T16:30:50.255766Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'Annual mean discharge (cfs)')" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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AMuDgMhLnnnmGRozZoxwsU5MTKQZM2bQ3r17XdaprKykWbNmUUJCAkVFRdHll19OOTk5LutkZGTQhRdeSBEREeJ1HnzwQaqtrXVZZ8WKFTRy5EjRLdGnTx/68MMPTf0B9tW+5f3KDuSNb9nZ2WRW1Nq3d999N40aNUp8JocPH97s79q2bRudccYZFBYWJtxsn3vuOTIzvtq3R44cafZzu27dOjIjauzXrVu30lVXXSU+h+Hh4TRgwAB65ZVXmvwuHGtnaLJvdX+s5dEdoP1MmzbN9sEHH9h27Nhh27Jli+2CCy6wpaWl2UpLS5V1br/9dlu3bt1sS5cutW3YsME2btw42+mnn648X1tbaxs8eLBtypQpts2bN9t++OEHW6dOnWxz585V1jl06JAtIiLCNnv2bNuuXbtsr776qi0wMNC2aNEi0759vtq3y5cv5/E1tr1799qysrKUW11dnc2sqLFvmbvuusv22muv2WbOnGkbNmxYk99TVFRkS0pKsl1zzTXid33yySe28PBw29tvv20zK77at4cPHxaf259//tnlc1tdXW0zI2rs1/fff992991321asWGE7ePCg7aOPPhKfRz6eSnCs3aLZvtX7sRYBkcrk5uaKN3zlypXi58LCQltwcLDt888/V9bZvXu3WGft2rXiZz5JBwQE2LKzs5V13nzzTVtMTIytqqpK/DxnzhzboEGDXH7XlVdeKQ4SVkGrfSu/pAUFBTar0p5968xjjz3W7En7jTfesMXHxyv7mnnooYds/fv3t1kFrfatDIg40Lci3u5XyZ///Gfb2WefrfyMY61Ns32r92MtSmYqU1RUJO47duwo7jdu3Eg1NTU0ZcoUZZ309HRKS0ujtWvXip/5fsiQIZSUlKSsM23aNDEwb+fOnco6zq8h15GvYQW02rcSLkskJyfTueeeS6tXryYr0Z596w687plnnkkhISEu+5/T8QUFBWQFtNq3kosvvliUkCZOnEjffvstWQW19iu/jnwNBsda0mzf6v1Yi4BIRerr6+nee++lCRMm0ODBg8VjXBvlk0FcXJzLunyClnVTvnc+Ycvn5XOtrcMn9oqKCjI7Wu5b/mK+9dZb9L///U/cuAY+adIk2rRpE1mB9u5bd3Bn/5sZLfct6zheeOEF+vzzz+n7778XARHraqwQFKm1X9esWUMLFiyg2267TXkM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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "ax = x1[0].plot(x=\"year_nu\", y=\"mean_va\")\n", + "# select the annual (calendar-year) means into a plain DataFrame for plotting.\n", + "# The statistics services return a GeoDataFrame carrying a site-point geometry,\n", + "# and report numeric values as strings, so we coerce ``value`` to float.\n", + "annual = x1[0].loc[\n", + " x1[0][\"interval_type\"] == \"calendar_year\", [\"start_date\", \"value\"]\n", + "].copy()\n", + "annual[\"year\"] = annual[\"start_date\"].str[:4].astype(int)\n", + "annual[\"value\"] = annual[\"value\"].astype(float)\n", + "annual = annual.sort_values(\"year\")\n", + "\n", + "ax = annual.plot(x=\"year\", y=\"value\", legend=False)\n", "ax.xaxis.set_major_formatter(ticker.FormatStrFormatter(\"%d\"))\n", "ax.set_xlabel(\"Year\")\n", "ax.set_ylabel(\"Annual mean discharge (cfs)\")" @@ -148,16 +663,31 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The other part of the result returned from the `get_stats()` function is a metadata object that contains information about the query that was executed to return the data. For example, you can access the URL that was assembled to retrieve the requested data from the USGS web service. The USGS web service responses contain a descriptive header that defines and can be helpful in interpreting the contents of the response." + "The other part of the result is a metadata object describing the query that was executed. For example, you can access the URL that was assembled to retrieve the requested data from the USGS Water Data API." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:50.257904Z", + "iopub.status.busy": "2026-05-26T16:30:50.257793Z", + "iopub.status.idle": "2026-05-26T16:30:50.260020Z", + "shell.execute_reply": "2026-05-26T16:30:50.259541Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The query URL used to retrieve the data was: https://api.waterdata.usgs.gov/statistics/v0/observationIntervals?computation_type=arithmetic_mean&monitoring_location_id=USGS-02319394&page_size=1000¶meter_code=00060\n" + ] + } + ], "source": [ - "print(\"The query URL used to retrieve the data from NWIS was: \" + x1[1].url)" + "print(\"The query URL used to retrieve the data was: \" + x1[1].url)" ] }, { @@ -171,21 +701,431 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2: Get all of the annual mean discharge data for two sites\n", + "#### Example 2: Get monthly and annual mean statistics for two sites\n", "\n", - "Note: Passing multiple parameters (temperature and flow) looks like it returns only what is available (in this example flow, 00060)" + "Multiple monitoring locations and parameter codes can be requested at once; only the data that are available are returned." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 8, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:50.261583Z", + "iopub.status.busy": "2026-05-26T16:30:50.261459Z", + "iopub.status.idle": "2026-05-26T16:30:53.015857Z", + "shell.execute_reply": "2026-05-26T16:30:53.015375Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: observationIntervals · 1 page · 2,114 rows · 3,924/4,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
geometrymonitoring_location_idmonitoring_location_namesite_typesite_type_codecountry_codestate_codecounty_codestart_dateend_dateinterval_typevaluepercentilesample_countapproval_statuscomputation_idcomputationparameter_codeunit_of_measureparent_time_series_id
0POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-05-011942-05-31month584.032NaN31approved043dcd51-8a2f-48ca-bb20-20933651f633arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
1POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-06-011942-06-30month2571.333NaN30approved0a96c646-ca99-45de-b8ea-b85954d5e029arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
2POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-07-011942-07-31month400.742NaN31approved88991dcf-0994-4d18-be50-378146f193fearithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
3POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-08-011942-08-31month549.581NaN31approved64fd8688-07e2-4710-8cd4-57ee0e25ff66arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
4POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-09-011942-09-30month1338.467NaN30approvedc8b93535-460a-4dfd-91ab-a5ec4a16c827arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
...............................................................
2109POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792021-10-012022-09-30water_year20.784NaN351approved6bdfe85c-4494-4143-84ce-e30849f1e68barithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2110POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792022-10-012023-09-30water_year21.121NaN361approvedc4ec6635-83cf-4d54-9fd8-caed23b96a98arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2111POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792023-10-012024-09-30water_year20.383NaN342approvede533a96f-655a-4753-b960-0560b0be2d97arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2112POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792024-10-012025-09-30water_year20.928NaN348approved197d9815-4ecd-4b89-a01e-50e128e4dbd6arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2113POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792025-10-012025-12-06water_year20.439NaN62approved6b6ce4c2-4ca8-4f73-86df-f3583f914dc0arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
\n", + "

2114 rows × 20 columns

\n", + "
" + ], + "text/plain": [ + " geometry monitoring_location_id \\\n", + "0 POINT (-80.14136 33.45378) USGS-02171500 \n", + "1 POINT (-80.14136 33.45378) USGS-02171500 \n", + "2 POINT (-80.14136 33.45378) USGS-02171500 \n", + "3 POINT (-80.14136 33.45378) USGS-02171500 \n", + "4 POINT (-80.14136 33.45378) USGS-02171500 \n", + "... ... ... \n", + "2109 POINT (-83.18014 30.41049) USGS-02319394 \n", + "2110 POINT (-83.18014 30.41049) USGS-02319394 \n", + "2111 POINT (-83.18014 30.41049) USGS-02319394 \n", + "2112 POINT (-83.18014 30.41049) USGS-02319394 \n", + "2113 POINT (-83.18014 30.41049) USGS-02319394 \n", + "\n", + " monitoring_location_name site_type site_type_code country_code \\\n", + "0 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "1 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "2 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "3 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "4 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "... ... ... ... ... \n", + "2109 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "2110 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "2111 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "2112 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "2113 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", + "\n", + " state_code county_code start_date end_date interval_type value \\\n", + "0 45 015 1942-05-01 1942-05-31 month 584.032 \n", + "1 45 015 1942-06-01 1942-06-30 month 2571.333 \n", + "2 45 015 1942-07-01 1942-07-31 month 400.742 \n", + "3 45 015 1942-08-01 1942-08-31 month 549.581 \n", + "4 45 015 1942-09-01 1942-09-30 month 1338.467 \n", + "... ... ... ... ... ... ... \n", + "2109 12 079 2021-10-01 2022-09-30 water_year 20.784 \n", + "2110 12 079 2022-10-01 2023-09-30 water_year 21.121 \n", + "2111 12 079 2023-10-01 2024-09-30 water_year 20.383 \n", + "2112 12 079 2024-10-01 2025-09-30 water_year 20.928 \n", + "2113 12 079 2025-10-01 2025-12-06 water_year 20.439 \n", + "\n", + " percentile sample_count approval_status \\\n", + "0 NaN 31 approved \n", + "1 NaN 30 approved \n", + "2 NaN 31 approved \n", + "3 NaN 31 approved \n", + "4 NaN 30 approved \n", + "... ... ... ... \n", + "2109 NaN 351 approved \n", + "2110 NaN 361 approved \n", + "2111 NaN 342 approved \n", + "2112 NaN 348 approved \n", + "2113 NaN 62 approved \n", + "\n", + " computation_id computation parameter_code \\\n", + "0 043dcd51-8a2f-48ca-bb20-20933651f633 arithmetic_mean 00060 \n", + "1 0a96c646-ca99-45de-b8ea-b85954d5e029 arithmetic_mean 00060 \n", + "2 88991dcf-0994-4d18-be50-378146f193fe arithmetic_mean 00060 \n", + "3 64fd8688-07e2-4710-8cd4-57ee0e25ff66 arithmetic_mean 00060 \n", + "4 c8b93535-460a-4dfd-91ab-a5ec4a16c827 arithmetic_mean 00060 \n", + "... ... ... ... \n", + "2109 6bdfe85c-4494-4143-84ce-e30849f1e68b arithmetic_mean 00010 \n", + "2110 c4ec6635-83cf-4d54-9fd8-caed23b96a98 arithmetic_mean 00010 \n", + "2111 e533a96f-655a-4753-b960-0560b0be2d97 arithmetic_mean 00010 \n", + "2112 197d9815-4ecd-4b89-a01e-50e128e4dbd6 arithmetic_mean 00010 \n", + "2113 6b6ce4c2-4ca8-4f73-86df-f3583f914dc0 arithmetic_mean 00010 \n", + "\n", + " unit_of_measure parent_time_series_id \n", + "0 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "1 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "2 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "3 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "4 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "... ... ... \n", + "2109 degC ea8d8089780f48428cff4a5ba344f1aa \n", + "2110 degC ea8d8089780f48428cff4a5ba344f1aa \n", + "2111 degC ea8d8089780f48428cff4a5ba344f1aa \n", + "2112 degC ea8d8089780f48428cff4a5ba344f1aa \n", + "2113 degC ea8d8089780f48428cff4a5ba344f1aa \n", + "\n", + "[2114 rows x 20 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "x2 = nwis.get_stats(\n", - " sites=[\"02319394\", \"02171500\"],\n", - " parameterCd=[\"00010\", \"00060\"],\n", - " statReportType=\"annual\",\n", + "x2 = waterdata.get_stats_date_range(\n", + " monitoring_location_id=[\"USGS-02319394\", \"USGS-02171500\"],\n", + " parameter_code=[\"00010\", \"00060\"],\n", + " computation_type=\"arithmetic_mean\",\n", ")\n", "display(x2[0])" ] @@ -194,24 +1134,419 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 3: Request daily mean and median values for temperature and discharge for a site for years between 2000 and 2007\n", + "#### Example 3: Day-of-year mean and median statistics over the period of record\n", "\n", - "NOTE: The startDt and endDt parameters are not directly supported by this function but are turned into query parameters in the request to USGS NWIS, which means that they can be used to limit the time window requested." + "`get_stats_por()` summarizes the full period of record by day of year (and month of year). Here we request both the mean and median daily statistics for discharge at a site." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T16:30:53.017565Z", + "iopub.status.busy": "2026-05-26T16:30:53.017426Z", + "iopub.status.idle": "2026-05-26T16:30:53.504048Z", + "shell.execute_reply": "2026-05-26T16:30:53.503553Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\r", + "Retrieving: observationNormals · 1 page · 756 rows · 3,923/4,000 requests remaining" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
geometrymonitoring_location_idmonitoring_location_namesite_typesite_type_codecountry_codestate_codecounty_codetime_of_yeartime_of_year_typevaluepercentilesample_countapproval_statuscomputation_idcomputationparameter_codeunit_of_measureparent_time_series_id
0POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-01day_of_year2046.695NaN82approvede7827589-ff6e-472d-9864-1bfe558b9639arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
1POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-02day_of_year2041.866NaN82approved9e8feb48-3652-4bca-82dc-c2f2a65650e5arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
2POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-03day_of_year2080.963NaN82approved35c0af68-80d2-4635-865c-bb224bfb5e9aarithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
3POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-04day_of_year2434.256NaN82approvedeacd3baa-e0ab-4c08-8334-bb3ed3b916c8arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
4POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-05day_of_year2597.819NaN83approved19f28744-96cb-4cfa-9d28-5f114b82fcc6arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
............................................................
751POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501508month_of_year563.050.084approvedb3fc0cec-1732-4bac-b153-a18bef567453median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
752POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501509month_of_year563.050.084approved8cd55012-5b16-42f7-8cb3-b234aa15f859median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
753POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501510month_of_year563.050.084approveda1b160db-bc3f-44e7-a150-881474373276median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
754POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501511month_of_year561.550.084approved48757820-8d5e-4407-bced-bca2717c6c97median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
755POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501512month_of_year553.550.084approved9373bc06-0df5-4a86-9590-dfadcdcb6efbmedian00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
\n", + "

756 rows × 19 columns

\n", + "
" + ], + "text/plain": [ + " geometry monitoring_location_id \\\n", + "0 POINT (-80.14136 33.45378) USGS-02171500 \n", + "1 POINT (-80.14136 33.45378) USGS-02171500 \n", + "2 POINT (-80.14136 33.45378) USGS-02171500 \n", + "3 POINT (-80.14136 33.45378) USGS-02171500 \n", + "4 POINT (-80.14136 33.45378) USGS-02171500 \n", + ".. ... ... \n", + "751 POINT (-80.14136 33.45378) USGS-02171500 \n", + "752 POINT (-80.14136 33.45378) USGS-02171500 \n", + "753 POINT (-80.14136 33.45378) USGS-02171500 \n", + "754 POINT (-80.14136 33.45378) USGS-02171500 \n", + "755 POINT (-80.14136 33.45378) USGS-02171500 \n", + "\n", + " monitoring_location_name site_type site_type_code country_code \\\n", + "0 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "1 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "2 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "3 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "4 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + ".. ... ... ... ... \n", + "751 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "752 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "753 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "754 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "755 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", + "\n", + " state_code county_code time_of_year time_of_year_type value \\\n", + "0 45 015 01-01 day_of_year 2046.695 \n", + "1 45 015 01-02 day_of_year 2041.866 \n", + "2 45 015 01-03 day_of_year 2080.963 \n", + "3 45 015 01-04 day_of_year 2434.256 \n", + "4 45 015 01-05 day_of_year 2597.819 \n", + ".. ... ... ... ... ... \n", + "751 45 015 08 month_of_year 563.0 \n", + "752 45 015 09 month_of_year 563.0 \n", + "753 45 015 10 month_of_year 563.0 \n", + "754 45 015 11 month_of_year 561.5 \n", + "755 45 015 12 month_of_year 553.5 \n", + "\n", + " percentile sample_count approval_status \\\n", + "0 NaN 82 approved \n", + "1 NaN 82 approved \n", + "2 NaN 82 approved \n", + "3 NaN 82 approved \n", + "4 NaN 83 approved \n", + ".. ... ... ... \n", + "751 50.0 84 approved \n", + "752 50.0 84 approved \n", + "753 50.0 84 approved \n", + "754 50.0 84 approved \n", + "755 50.0 84 approved \n", + "\n", + " computation_id computation parameter_code \\\n", + "0 e7827589-ff6e-472d-9864-1bfe558b9639 arithmetic_mean 00060 \n", + "1 9e8feb48-3652-4bca-82dc-c2f2a65650e5 arithmetic_mean 00060 \n", + "2 35c0af68-80d2-4635-865c-bb224bfb5e9a arithmetic_mean 00060 \n", + "3 eacd3baa-e0ab-4c08-8334-bb3ed3b916c8 arithmetic_mean 00060 \n", + "4 19f28744-96cb-4cfa-9d28-5f114b82fcc6 arithmetic_mean 00060 \n", + ".. ... ... ... \n", + "751 b3fc0cec-1732-4bac-b153-a18bef567453 median 00060 \n", + "752 8cd55012-5b16-42f7-8cb3-b234aa15f859 median 00060 \n", + "753 a1b160db-bc3f-44e7-a150-881474373276 median 00060 \n", + "754 48757820-8d5e-4407-bced-bca2717c6c97 median 00060 \n", + "755 9373bc06-0df5-4a86-9590-dfadcdcb6efb median 00060 \n", + "\n", + " unit_of_measure parent_time_series_id \n", + "0 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "1 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "2 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "3 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "4 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + ".. ... ... \n", + "751 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "752 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "753 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "754 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "755 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", + "\n", + "[756 rows x 19 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "x3 = nwis.get_stats(\n", - " sites=\"02171500\",\n", - " parameterCd=[\"00010\", \"00060\"],\n", - " statReportType=\"daily\",\n", - " statTypeCd=[\"mean\", \"median\"],\n", - " startDt=\"2000\",\n", - " endDt=\"2007\",\n", + "x3 = waterdata.get_stats_por(\n", + " monitoring_location_id=\"USGS-02171500\",\n", + " parameter_code=\"00060\",\n", + " computation_type=[\"arithmetic_mean\", \"median\"],\n", ")\n", "display(x3[0])" ] @@ -232,7 +1567,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.13.3" } }, "nbformat": 4, diff --git a/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb index 4d6eb927..31e6edd9 100644 --- a/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb @@ -4,9 +4,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# USGS dataretrieval Python Package `get_water_use()` Examples\n", + "# USGS dataretrieval Python Package Water Use Examples\n", "\n", - "This notebook provides examples of using the Python dataretrieval package to retrieve water use data. The dataretrieval package provides a collection of functions to get data from the USGS National Water Information System (NWIS) and other online sources of hydrology and water quality data, including the United States Environmental Protection Agency (USEPA)." + "> **Note:** USGS water-use data has **no USGS Water Data API equivalent**. The legacy `nwis.get_water_use()` service has been decommissioned and now raises a \"defunct\" error, so the examples below are retained for historical reference only — they document the former interface and are not runnable. There is currently no `waterdata` replacement for water-use data.\n", + "\n", + "This notebook formerly retrieved water use data through the USGS National Water Information System (NWIS)." ] }, { @@ -21,7 +23,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:43:41.521108Z", + "iopub.status.busy": "2026-05-26T17:43:41.520978Z", + "iopub.status.idle": "2026-05-26T17:43:45.257574Z", + "shell.execute_reply": "2026-05-26T17:43:45.256933Z" + } + }, "outputs": [], "source": [ "!pip install dataretrieval" @@ -36,15 +45,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:43:45.259977Z", + "iopub.status.busy": "2026-05-26T17:43:45.259818Z", + "iopub.status.idle": "2026-05-26T17:43:45.853921Z", + "shell.execute_reply": "2026-05-26T17:43:45.853378Z" + } + }, "outputs": [], "source": [ "from IPython.display import display\n", "\n", - "from dataretrieval import nwis\n", - "from dataretrieval import waterdata\n", - "import dataretrieval.waterdata as waterdata\n" + "from dataretrieval import nwis\n" ] }, { @@ -72,8 +86,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:43:45.856549Z", + "iopub.status.busy": "2026-05-26T17:43:45.856353Z", + "iopub.status.idle": "2026-05-26T17:43:45.858364Z", + "shell.execute_reply": "2026-05-26T17:43:45.857855Z" + } + }, "outputs": [], "source": [ "# [Defunct] pennsylvania = nwis.get_water_use(state=\"PA\")\n", @@ -100,8 +121,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:43:45.860047Z", + "iopub.status.busy": "2026-05-26T17:43:45.859939Z", + "iopub.status.idle": "2026-05-26T17:43:45.861855Z", + "shell.execute_reply": "2026-05-26T17:43:45.861330Z" + } + }, "outputs": [], "source": [ "# [Defunct] display(pennsylvania[0])" @@ -116,8 +144,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:43:45.863284Z", + "iopub.status.busy": "2026-05-26T17:43:45.863178Z", + "iopub.status.idle": "2026-05-26T17:43:45.864828Z", + "shell.execute_reply": "2026-05-26T17:43:45.864439Z" + } + }, "outputs": [], "source": [ "# [Defunct] print(pennsylvania[0].dtypes)" @@ -134,8 +169,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:43:45.866292Z", + "iopub.status.busy": "2026-05-26T17:43:45.866192Z", + "iopub.status.idle": "2026-05-26T17:43:45.867944Z", + "shell.execute_reply": "2026-05-26T17:43:45.867459Z" + } + }, "outputs": [], "source": [ "# [Defunct] ohio = nwis.get_water_use(years=[2000, 2005, 2010], state=\"OH\")\n", @@ -152,8 +194,15 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-26T17:43:45.869175Z", + "iopub.status.busy": "2026-05-26T17:43:45.869084Z", + "iopub.status.idle": "2026-05-26T17:43:45.871070Z", + "shell.execute_reply": "2026-05-26T17:43:45.870527Z" + } + }, "outputs": [], "source": [ "# Get water use data for livestock (LI) and irrigation (IT)\n", @@ -178,7 +227,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.13.3" } }, "nbformat": 4, From 531e480287ce9b506607922778e6aa69746af3ad Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 26 May 2026 12:50:16 -0500 Subject: [PATCH 6/8] docs(examples): rename NWIS_demo_1 -> peak_streamflow_trends The notebook content was migrated to the Water Data API (national peak annual streamflow trend analysis), so the NWIS_ filename prefix was stale. Rename the notebook, its .nblink, and the toctree slug to describe the content. This changes the published docs URL for this page. Co-Authored-By: Claude Opus 4.7 (1M context) --- demos/{NWIS_demo_1.ipynb => peak_streamflow_trends.ipynb} | 0 docs/source/examples/index.rst | 2 +- .../{nwisdemo01.nblink => peak_streamflow_trends.nblink} | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename demos/{NWIS_demo_1.ipynb => peak_streamflow_trends.ipynb} (100%) rename docs/source/examples/{nwisdemo01.nblink => peak_streamflow_trends.nblink} (50%) diff --git a/demos/NWIS_demo_1.ipynb b/demos/peak_streamflow_trends.ipynb similarity index 100% rename from demos/NWIS_demo_1.ipynb rename to demos/peak_streamflow_trends.ipynb diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst index edd43beb..4d07c0a5 100644 --- a/docs/source/examples/index.rst +++ b/docs/source/examples/index.rst @@ -61,7 +61,7 @@ Using ``dataretrieval`` to obtain nation trends in peak annual streamflow .. toctree:: :maxdepth: 2 - nwisdemo01 + peak_streamflow_trends Duplicating the R ``dataRetrieval`` vignettes functionality diff --git a/docs/source/examples/nwisdemo01.nblink b/docs/source/examples/peak_streamflow_trends.nblink similarity index 50% rename from docs/source/examples/nwisdemo01.nblink rename to docs/source/examples/peak_streamflow_trends.nblink index 48f0bc2b..1bf99495 100644 --- a/docs/source/examples/nwisdemo01.nblink +++ b/docs/source/examples/peak_streamflow_trends.nblink @@ -1,6 +1,6 @@ { - "path": "../../../demos/NWIS_demo_1.ipynb", + "path": "../../../demos/peak_streamflow_trends.ipynb", "extra-media": [ "../../../demos/datasets" ] -} \ No newline at end of file +} From 4748c6041e39ed0e6e7bfd59cde74073d4fe162a Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 26 May 2026 13:12:19 -0500 Subject: [PATCH 7/8] docs: fix obvious pre-existing Sphinx build warnings Clears 6 of the 10 make-html warnings (none were from the httpx/migration work): - get_channel: indent wrapped bullet continuations to the bullet-text column and add a trailing blank line so the time / last_modified "Examples" lists parse as RST instead of "bullet list ends without a blank line". - get_monitoring_locations: make the two identical "list of codes" hyperlinks anonymous (__) to avoid a duplicate-target warning. - conf.py: drop the deprecated sphinx_rtd_theme display_version option. - add the referenced docs/source/_static/ directory (was configured in html_static_path but missing). Remaining warnings (left as out of scope): the legacy NWIS_Metadata autodoc duplicate and the intentional !pip cells in WaterData_demo. Co-Authored-By: Claude Opus 4.7 (1M context) --- dataretrieval/waterdata/api.py | 15 ++++++++------- docs/source/_static/.gitkeep | 0 docs/source/conf.py | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 docs/source/_static/.gitkeep diff --git a/dataretrieval/waterdata/api.py b/dataretrieval/waterdata/api.py index 57fffc88..1ec9ed42 100644 --- a/dataretrieval/waterdata/api.py +++ b/dataretrieval/waterdata/api.py @@ -554,11 +554,11 @@ def get_monitoring_locations( county_code : string or iterable of strings, optional The code for the county or county equivalent (parish, borough, etc.) in which the monitoring location is located. A `list of codes - `_ is available. + `__ is available. county_name : string or iterable of strings, optional The name of the county or county equivalent (parish, borough, etc.) in which the monitoring location is located. A `list of codes - `_ is available. + `__ is available. minor_civil_division_code : string or iterable of strings, optional Codes for primary governmental or administrative divisions of the county or county equivalent in which the monitoring location is located. @@ -2751,9 +2751,10 @@ def get_channel( * A date-time: "2018-02-12T23:20:50Z" * A bounded interval: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" * Half-bounded intervals: "2018-02-12T00:00:00Z/.." or - "../2018-03-18T12:31:12Z" - * Duration objects: "P1M" for data from the past month or "PT36H" for - the last 36 hours + "../2018-03-18T12:31:12Z" + * Duration objects: "P1M" for data from the past month or "PT36H" + for the last 36 hours + channel_name : string or iterable of strings, optional The channel name. channel_flow : string or iterable of strings, optional @@ -2799,9 +2800,9 @@ def get_channel( * A date-time: "2018-02-12T23:20:50Z" * A bounded interval: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" * Half-bounded intervals: "2018-02-12T00:00:00Z/.." or - "../2018-03-18T12:31:12Z" + "../2018-03-18T12:31:12Z" * Duration objects: "P1M" for data from the past month or "PT36H" for the - last 36 hours + last 36 hours Only features that have a last_modified that intersects the value of datetime are selected. diff --git a/docs/source/_static/.gitkeep b/docs/source/_static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/conf.py b/docs/source/conf.py index 276bbd98..9d478f98 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -107,7 +107,6 @@ html_theme_options = { "logo_only": False, - "display_version": True, } # Add any paths that contain custom static files (such as style sheets) here, From 2b411b0924204f6cb05ccb54717848761867689a Mon Sep 17 00:00:00 2001 From: thodson-usgs Date: Tue, 26 May 2026 13:45:43 -0500 Subject: [PATCH 8/8] docs(examples): strip committed notebook outputs to match convention The migrated notebooks were committed with re-executed outputs, but this project keeps notebooks output-free and lets nbsphinx execute them at docs-build time (as the other Hydroshare notebooks do). Strip the outputs and the machine-specific language_info.version. Co-Authored-By: Claude Opus 4.7 (1M context) --- demos/R Python Vignette equivalents.ipynb | 318 +-- .../USGS_dataretrieval_Peaks_Examples.ipynb | 2418 +---------------- .../USGS_dataretrieval_Ratings_Examples.ipynb | 255 +- ...GS_dataretrieval_Statistics_Examples.ipynb | 1376 +--------- ...USGS_dataretrieval_WaterUse_Examples.ipynb | 78 +- demos/peak_streamflow_trends.ipynb | 447 +-- 6 files changed, 141 insertions(+), 4751 deletions(-) diff --git a/demos/R Python Vignette equivalents.ipynb b/demos/R Python Vignette equivalents.ipynb index 2f7f8abf..8cec99aa 100644 --- a/demos/R Python Vignette equivalents.ipynb +++ b/demos/R Python Vignette equivalents.ipynb @@ -9,15 +9,8 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:29.608908Z", - "iopub.status.busy": "2026-05-26T17:42:29.608564Z", - "iopub.status.idle": "2026-05-26T17:42:32.260083Z", - "shell.execute_reply": "2026-05-26T17:42:32.259231Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from dataretrieval import nwis, waterdata, wqp" @@ -34,32 +27,9 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:32.263095Z", - "iopub.status.busy": "2026-05-26T17:42:32.262819Z", - "iopub.status.idle": "2026-05-26T17:42:32.981278Z", - "shell.execute_reply": "2026-05-26T17:42:32.980581Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: monitoring-locations · 1 page · 2 rows · 988/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "{r getSite, echo=TRUE, eval=FALSE}\n", @@ -74,32 +44,9 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:33.015826Z", - "iopub.status.busy": "2026-05-26T17:42:33.015634Z", - "iopub.status.idle": "2026-05-26T17:42:33.402624Z", - "shell.execute_reply": "2026-05-26T17:42:33.401928Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: time-series-metadata · 1 page · 11 rows · 987/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "# Continuing from the previous example:\n", @@ -114,32 +61,9 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:33.404611Z", - "iopub.status.busy": "2026-05-26T17:42:33.404472Z", - "iopub.status.idle": "2026-05-26T17:42:34.130988Z", - "shell.execute_reply": "2026-05-26T17:42:34.130476Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: daily · 1 page · 1,096 rows · 986/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "# Choptank River near Greensboro, MD:\n", @@ -163,32 +87,9 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:34.132919Z", - "iopub.status.busy": "2026-05-26T17:42:34.132811Z", - "iopub.status.idle": "2026-05-26T17:42:34.877663Z", - "shell.execute_reply": "2026-05-26T17:42:34.877228Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: daily · 1 page · 364 rows · 985/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "siteNumber <- \"01491000\"\n", @@ -213,32 +114,9 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:34.879364Z", - "iopub.status.busy": "2026-05-26T17:42:34.879265Z", - "iopub.status.idle": "2026-05-26T17:42:35.276437Z", - "shell.execute_reply": "2026-05-26T17:42:35.275840Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: continuous · 1 page · 97 rows · 986/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "parameterCd <- \"00060\" # Discharge\n", @@ -258,15 +136,8 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:35.278224Z", - "iopub.status.busy": "2026-05-26T17:42:35.278100Z", - "iopub.status.idle": "2026-05-26T17:42:35.800108Z", - "shell.execute_reply": "2026-05-26T17:42:35.799574Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "\"\"\"\n", @@ -290,32 +161,9 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:35.801905Z", - "iopub.status.busy": "2026-05-26T17:42:35.801789Z", - "iopub.status.idle": "2026-05-26T17:42:36.965288Z", - "shell.execute_reply": "2026-05-26T17:42:36.964439Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: peaks · 1 page · 49 rows · 984/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "siteNumber <- '01594440'\n", @@ -328,27 +176,9 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:36.967386Z", - "iopub.status.busy": "2026-05-26T17:42:36.967183Z", - "iopub.status.idle": "2026-05-26T17:42:38.184282Z", - "shell.execute_reply": "2026-05-26T17:42:38.183553Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['USGS-01594440.base.rdb']" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "ratingData <- readNWISrating(siteNumber, \"base\")\n", @@ -363,32 +193,9 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:38.186467Z", - "iopub.status.busy": "2026-05-26T17:42:38.186272Z", - "iopub.status.idle": "2026-05-26T17:42:38.545255Z", - "shell.execute_reply": "2026-05-26T17:42:38.544660Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: observationIntervals · 1 page · 344 rows · 3,981/4,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "\"\"\"\n", "discharge_stats <- readNWISstat(siteNumbers=c(\"02319394\"),\n", @@ -404,15 +211,8 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:38.547164Z", - "iopub.status.busy": "2026-05-26T17:42:38.547011Z", - "iopub.status.idle": "2026-05-26T17:42:38.549668Z", - "shell.execute_reply": "2026-05-26T17:42:38.548979Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# R: readNWISdata(service=\"dv\", stateCd=\"WI\", parameterCd=\"00060\",\n", @@ -427,15 +227,8 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:38.551661Z", - "iopub.status.busy": "2026-05-26T17:42:38.551511Z", - "iopub.status.idle": "2026-05-26T17:42:38.553863Z", - "shell.execute_reply": "2026-05-26T17:42:38.553203Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# '''\n", @@ -447,15 +240,8 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:38.555324Z", - "iopub.status.busy": "2026-05-26T17:42:38.555222Z", - "iopub.status.idle": "2026-05-26T17:42:38.557187Z", - "shell.execute_reply": "2026-05-26T17:42:38.556614Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# '''\n", @@ -467,15 +253,8 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:38.558558Z", - "iopub.status.busy": "2026-05-26T17:42:38.558425Z", - "iopub.status.idle": "2026-05-26T17:42:38.560452Z", - "shell.execute_reply": "2026-05-26T17:42:38.559866Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# '''\n", @@ -488,15 +267,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:38.561850Z", - "iopub.status.busy": "2026-05-26T17:42:38.561741Z", - "iopub.status.idle": "2026-05-26T17:42:38.664579Z", - "shell.execute_reply": "2026-05-26T17:42:38.663906Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "\"\"\"site <- whatWQPsamples(siteid=\"USGS-01594440\")\"\"\"\n", @@ -506,15 +278,8 @@ }, { "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:42:38.666566Z", - "iopub.status.busy": "2026-05-26T17:42:38.666430Z", - "iopub.status.idle": "2026-05-26T17:42:38.737076Z", - "shell.execute_reply": "2026-05-26T17:42:38.736489Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "\"\"\"\n", @@ -570,8 +335,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" + "pygments_lexer": "ipython3" }, "vscode": { "interpreter": { diff --git a/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb index 1ab47d43..61d8c056 100644 --- a/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_Peaks_Examples.ipynb @@ -21,14 +21,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:50.390490Z", - "iopub.status.busy": "2026-05-26T16:32:50.390354Z", - "iopub.status.idle": "2026-05-26T16:32:51.939420Z", - "shell.execute_reply": "2026-05-26T16:32:51.938709Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "!pip install dataretrieval" @@ -43,15 +36,8 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:51.942658Z", - "iopub.status.busy": "2026-05-26T16:32:51.942468Z", - "iopub.status.idle": "2026-05-26T16:32:52.517630Z", - "shell.execute_reply": "2026-05-26T16:32:52.517076Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from IPython.display import display\n", @@ -81,39 +67,9 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:52.520431Z", - "iopub.status.busy": "2026-05-26T16:32:52.520162Z", - "iopub.status.idle": "2026-05-26T16:32:53.056653Z", - "shell.execute_reply": "2026-05-26T16:32:53.056044Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: peaks · 1 page · 57 rows · 924/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 57 peak values.\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "site_ids = [\"USGS-01594440\", \"USGS-040851325\"]\n", "peak_data = waterdata.get_peaks(\n", @@ -137,1327 +93,9 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:53.058237Z", - "iopub.status.busy": "2026-05-26T16:32:53.058107Z", - "iopub.status.idle": "2026-05-26T16:32:53.074655Z", - "shell.execute_reply": "2026-05-26T16:32:53.074200Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
03ae666b2-6658-4cd5-b0d9-9435e327f30ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s311002026-05-01 19:28:03.170712+00:001972-06-2219721972622NaNNone
1c2058a02-3a57-4867-b1eb-973d552bfa54POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s106002026-05-01 19:28:03.170712+00:001978-01-2719781978127NaNNone
2513931e9-5d25-499b-9433-2e1b23f6a4a1POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s115002026-05-01 19:28:03.170712+00:001979-09-071979197997NaNNone
33c4e6c32-42f1-4515-b90c-3194394ac576POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s39402026-05-01 19:28:03.170712+00:001979-10-11198019791011NaNNone
45df555c2-e9f7-4deb-bac5-3467d4eecf5cPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s16402026-05-01 19:28:03.170712+00:001981-02-2419811981224NaNNone
55efca373-025b-4980-89ce-b5dcaa6639d8POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s33802026-05-01 19:28:03.170712+00:001982-02-041982198224NaNNone
61d58764d-52c4-4344-bd29-9ab8852d1d89POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s57502026-05-01 19:28:03.170712+00:001983-06-2119831983621NaNNone
765bedc17-bc1f-4412-897e-c9c7bc763552POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s43402026-05-01 19:28:03.170712+00:001984-03-3019841984330NaNNone
86bedc7e5-5cc8-450d-b5a1-50449b20fe22POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s47302026-05-01 19:28:03.170712+00:001985-02-1319851985213NaNNone
92b87d057-7c2b-4cb7-bf44-576545008979POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s15202026-05-01 19:28:03.170712+00:001986-04-1619861986416NaNNone
10bf952852-cee7-43bc-88c9-70b197c4e62ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s40602026-05-01 19:28:03.170712+00:001986-12-25198719861225NaNNone
11867ee68a-f8ea-4f55-a2e1-5e9df0077fcdPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s35102026-05-01 19:28:03.170712+00:001987-11-30198819871130NaNNone
1210718b58-5763-44e5-a044-812c677f0b61POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s91902026-05-01 19:28:03.170712+00:001989-05-071989198957NaNNone
133dc8ee6a-c076-43d4-977a-e7252a94cac7POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s31402026-05-01 19:28:03.170712+00:001990-05-3019901990530NaNNone
14b9294137-2fad-4074-bd86-cf52f727ede6POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s47502026-05-01 19:28:03.170712+00:001991-03-2419911991324NaNNone
15537bb753-c6d6-49a1-a4dc-2b16b29256b5POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s32002026-05-01 19:28:03.170712+00:001992-03-2719921992327NaNNone
16dddd1dbe-b92a-4739-84b8-350918ecdd7cPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s55502026-05-01 19:28:03.170712+00:001993-03-051993199335NaNNone
1729e958a5-ebdb-4290-8fd8-843019b62155POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s69602026-05-01 19:28:03.170712+00:001993-11-29199419931129NaNNone
18942af9b6-7ace-4d6a-8cb2-f359e9511af3POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s41002026-05-01 19:28:03.170712+00:001995-03-091995199539NaNNone
1909d0f0e1-c7ac-4136-b4f8-e4639b313386POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s82802026-05-01 19:28:03.170712+00:001996-01-2019961996120NaNNone
209abb8890-e9c6-45bb-a302-678dc691207ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s69002026-05-01 19:28:03.170712+00:001996-11-0919971996119NaNNone
2101766a4b-dab4-4698-9c6a-3ef8f2e11153POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s48402026-05-01 19:28:03.170712+00:001998-03-1019981998310NaNNone
229e4dd73c-afd1-4fd2-95f4-3f5b3af886c4POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s82002026-05-01 19:28:03.170712+00:001999-09-1719991999917NaNNone
2301ad0ff4-f40e-4ad1-93e4-9460f5de1353POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s36402026-05-01 19:28:03.170712+00:002000-03-2220002000322NaNNone
243ea2d208-4285-4b9a-adeb-9b17b0d67feaPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s38002026-05-01 19:28:03.170712+00:002001-06-08200120016810:30:00+00:00None
259ad09dcd-0e49-4baf-a5dd-cb3b1dfb4c1aPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s15102026-05-01 19:28:03.170712+00:002002-04-2920022002429NaNNone
26406312f8-f7ff-4e68-ade4-8b4dd426bd44POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s69902026-05-01 19:28:03.170712+00:002003-02-242003200322400:30:00+00:00None
27e4550eba-d130-48e4-9abe-f7077b130ee8POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s57902026-05-01 19:28:03.170712+00:002003-12-1220042003121215:45:00+00:00None
28c06f6cf2-8668-4f70-851a-b8d704a24807POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s52102026-05-01 19:28:03.170712+00:002005-04-03200520054323:15:00+00:00None
2990bfb29a-82c2-4bba-85cb-f58b87874e28POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s2672026-05-05 03:36:50.236554+00:002005-08-1820052005818NaNNone
30b94cec80-5c1a-4c93-ad21-a6eea949ab67POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s2882026-05-05 03:36:50.236554+00:002006-03-1320062006313NaNNone
31b5fc68ad-d404-412a-8bdf-5df5c135887fPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s127002026-05-01 19:28:03.170712+00:002006-06-272006200662703:00:00+00:00None
32ce07ea88-b517-40a9-8a79-d45d0708c4caPOINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s3012026-05-05 03:36:50.236554+00:002007-03-1420072007314NaNNone
3333973c47-235c-49a6-ad3f-d4ae6dbbe64ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s55202026-05-01 19:28:03.170712+00:002007-04-162007200741615:15:00+00:00None
34c64dc6a7-4a8d-414a-9eae-fbbb9f5faf93POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s78602026-05-01 19:28:03.170712+00:002008-05-132008200851306:00:00+00:00None
356d756602-b9e4-4063-ad59-870b56014f4dPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s41302026-05-01 19:28:03.170712+00:002009-06-192009200961909:45:00+00:00None
3629ffbe23-6ed6-4c4a-97be-062acb4a4877POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s57802026-05-01 19:28:03.170712+00:002010-03-142010201031417:00:00+00:00None
37c4324b2a-1ef8-4aed-b3f2-26fbbd57cb79POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s3862026-05-05 03:36:50.236554+00:002010-03-1420102010314NaNNone
386074a5e7-4cb5-4177-98c4-66399fb74609POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s4362026-05-05 03:36:50.236554+00:002011-03-1820112011318NaNNone
39ba30e845-6905-41ec-af84-92804d870a0ePOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s168002026-05-01 19:28:03.170712+00:002011-09-08201120119817:15:00+00:00None
403b9cb606-0785-4d3e-9e52-3efa0ad559d9POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s49002026-05-01 19:28:03.170712+00:002011-12-092012201112901:30:00+00:00None
413bc2c0c3-50d0-44fe-a640-32a8219364b1POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s1502026-05-05 03:36:50.236554+00:002012-03-092012201239NaNNone
427a277bcb-435f-4b1e-b67c-dd5dac818ac2POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s108002026-05-01 19:28:03.170712+00:002012-10-3120132012103103:00:00+00:00None
439c628482-896d-4a8a-b12f-091b216cef52POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s4092026-05-05 03:36:50.236554+00:002013-03-3120132013331NaNNone
44c46e2dc3-6a5c-47ce-9322-dbf417d03caaPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s156002026-05-01 19:28:03.170712+00:002014-05-01201420145121:30:00+00:00None
45bd89314d-4ba6-429f-83fa-567e5f73ae25POINT (-87.93621 44.5011)fdd843797b3246efb9f4aa344b5946ccUSGS-04085132500060ft^3/s4292026-05-05 03:36:50.236554+00:002014-06-022014201462NaNNone
4684722b5e-c286-4d69-bdf1-75e05665adb0POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s66102026-05-01 19:28:03.170712+00:002015-06-282015201562822:15:00+00:00None
476caf417c-10b1-450d-957f-e5d43973afa9POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s61402026-05-01 19:28:03.170712+00:002016-08-01201620168104:30:00+00:00None
48e7d3ffaa-e6ff-486e-bbd0-5fcbb11a6268POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s49602026-05-01 19:28:03.170712+00:002017-07-302017201773005:15:00+00:00None
4999f75026-7ad8-4e9f-bec8-9da13af22530POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s83602026-05-01 19:28:03.170712+00:002018-06-04201820186422:30:00+00:00None
50f8b816ae-c4a4-4f99-aa80-470856f0c334POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s72202026-05-01 19:28:03.170712+00:002018-12-1720192018121704:30:00+00:00None
516ce447e3-ec2a-47df-bc1a-71ceff4e47fcPOINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s43402026-05-01 19:28:03.170712+00:002020-04-142020202041413:15:00+00:00None
52fd1f1fb2-0329-42ad-bdd1-866d46ef6461POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s40002026-05-01 19:28:03.170712+00:002020-11-1220212020111222:45:00+00:00None
53d353f7b9-eba2-41a9-baa8-fd787e199b23POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s45902026-05-01 19:28:03.170712+00:002021-10-3020222021103021:45:00+00:00None
54b68c3597-8210-4f49-8af6-05b14f003bb0POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s33302026-05-01 19:28:03.170712+00:002022-12-1620232022121619:15:00+00:00None
55850c52c4-5567-4e2f-b30a-fda4fcd0e121POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s74002026-05-01 19:28:03.170712+00:002024-01-112024202411100:15:00+00:00None
56f9895515-e652-483e-97d2-70412cafa722POINT (-76.69369 38.95592)92a4e13a755d406abad1529f6add68e9USGS-0159444000060ft^3/s23202026-05-01 19:28:03.170712+00:002025-06-192025202561918:45:00+00:00None
\n", - "
" - ], - "text/plain": [ - " peak_id geometry \\\n", - "0 3ae666b2-6658-4cd5-b0d9-9435e327f30e POINT (-76.69369 38.95592) \n", - "1 c2058a02-3a57-4867-b1eb-973d552bfa54 POINT (-76.69369 38.95592) \n", - "2 513931e9-5d25-499b-9433-2e1b23f6a4a1 POINT (-76.69369 38.95592) \n", - "3 3c4e6c32-42f1-4515-b90c-3194394ac576 POINT (-76.69369 38.95592) \n", - "4 5df555c2-e9f7-4deb-bac5-3467d4eecf5c POINT (-76.69369 38.95592) \n", - "5 5efca373-025b-4980-89ce-b5dcaa6639d8 POINT (-76.69369 38.95592) \n", - "6 1d58764d-52c4-4344-bd29-9ab8852d1d89 POINT (-76.69369 38.95592) \n", - "7 65bedc17-bc1f-4412-897e-c9c7bc763552 POINT (-76.69369 38.95592) \n", - "8 6bedc7e5-5cc8-450d-b5a1-50449b20fe22 POINT (-76.69369 38.95592) \n", - "9 2b87d057-7c2b-4cb7-bf44-576545008979 POINT (-76.69369 38.95592) \n", - "10 bf952852-cee7-43bc-88c9-70b197c4e62e POINT (-76.69369 38.95592) \n", - "11 867ee68a-f8ea-4f55-a2e1-5e9df0077fcd POINT (-76.69369 38.95592) \n", - "12 10718b58-5763-44e5-a044-812c677f0b61 POINT (-76.69369 38.95592) \n", - "13 3dc8ee6a-c076-43d4-977a-e7252a94cac7 POINT (-76.69369 38.95592) \n", - "14 b9294137-2fad-4074-bd86-cf52f727ede6 POINT (-76.69369 38.95592) \n", - "15 537bb753-c6d6-49a1-a4dc-2b16b29256b5 POINT (-76.69369 38.95592) \n", - "16 dddd1dbe-b92a-4739-84b8-350918ecdd7c POINT (-76.69369 38.95592) \n", - "17 29e958a5-ebdb-4290-8fd8-843019b62155 POINT (-76.69369 38.95592) \n", - "18 942af9b6-7ace-4d6a-8cb2-f359e9511af3 POINT (-76.69369 38.95592) \n", - "19 09d0f0e1-c7ac-4136-b4f8-e4639b313386 POINT (-76.69369 38.95592) \n", - "20 9abb8890-e9c6-45bb-a302-678dc691207e POINT (-76.69369 38.95592) \n", - "21 01766a4b-dab4-4698-9c6a-3ef8f2e11153 POINT (-76.69369 38.95592) \n", - "22 9e4dd73c-afd1-4fd2-95f4-3f5b3af886c4 POINT (-76.69369 38.95592) \n", - "23 01ad0ff4-f40e-4ad1-93e4-9460f5de1353 POINT (-76.69369 38.95592) \n", - "24 3ea2d208-4285-4b9a-adeb-9b17b0d67fea POINT (-76.69369 38.95592) \n", - "25 9ad09dcd-0e49-4baf-a5dd-cb3b1dfb4c1a POINT (-76.69369 38.95592) \n", - "26 406312f8-f7ff-4e68-ade4-8b4dd426bd44 POINT (-76.69369 38.95592) \n", - "27 e4550eba-d130-48e4-9abe-f7077b130ee8 POINT (-76.69369 38.95592) \n", - "28 c06f6cf2-8668-4f70-851a-b8d704a24807 POINT (-76.69369 38.95592) \n", - "29 90bfb29a-82c2-4bba-85cb-f58b87874e28 POINT (-87.93621 44.5011) \n", - "30 b94cec80-5c1a-4c93-ad21-a6eea949ab67 POINT (-87.93621 44.5011) \n", - "31 b5fc68ad-d404-412a-8bdf-5df5c135887f POINT (-76.69369 38.95592) \n", - "32 ce07ea88-b517-40a9-8a79-d45d0708c4ca POINT (-87.93621 44.5011) \n", - "33 33973c47-235c-49a6-ad3f-d4ae6dbbe64e POINT (-76.69369 38.95592) \n", - "34 c64dc6a7-4a8d-414a-9eae-fbbb9f5faf93 POINT (-76.69369 38.95592) \n", - "35 6d756602-b9e4-4063-ad59-870b56014f4d POINT (-76.69369 38.95592) \n", - "36 29ffbe23-6ed6-4c4a-97be-062acb4a4877 POINT (-76.69369 38.95592) \n", - "37 c4324b2a-1ef8-4aed-b3f2-26fbbd57cb79 POINT (-87.93621 44.5011) \n", - "38 6074a5e7-4cb5-4177-98c4-66399fb74609 POINT (-87.93621 44.5011) \n", - "39 ba30e845-6905-41ec-af84-92804d870a0e POINT (-76.69369 38.95592) \n", - "40 3b9cb606-0785-4d3e-9e52-3efa0ad559d9 POINT (-76.69369 38.95592) \n", - "41 3bc2c0c3-50d0-44fe-a640-32a8219364b1 POINT (-87.93621 44.5011) \n", - "42 7a277bcb-435f-4b1e-b67c-dd5dac818ac2 POINT (-76.69369 38.95592) \n", - "43 9c628482-896d-4a8a-b12f-091b216cef52 POINT (-87.93621 44.5011) \n", - "44 c46e2dc3-6a5c-47ce-9322-dbf417d03caa POINT (-76.69369 38.95592) \n", - "45 bd89314d-4ba6-429f-83fa-567e5f73ae25 POINT (-87.93621 44.5011) \n", - "46 84722b5e-c286-4d69-bdf1-75e05665adb0 POINT (-76.69369 38.95592) \n", - "47 6caf417c-10b1-450d-957f-e5d43973afa9 POINT (-76.69369 38.95592) \n", - "48 e7d3ffaa-e6ff-486e-bbd0-5fcbb11a6268 POINT (-76.69369 38.95592) \n", - "49 99f75026-7ad8-4e9f-bec8-9da13af22530 POINT (-76.69369 38.95592) \n", - "50 f8b816ae-c4a4-4f99-aa80-470856f0c334 POINT (-76.69369 38.95592) \n", - "51 6ce447e3-ec2a-47df-bc1a-71ceff4e47fc POINT (-76.69369 38.95592) \n", - "52 fd1f1fb2-0329-42ad-bdd1-866d46ef6461 POINT (-76.69369 38.95592) \n", - "53 d353f7b9-eba2-41a9-baa8-fd787e199b23 POINT (-76.69369 38.95592) \n", - "54 b68c3597-8210-4f49-8af6-05b14f003bb0 POINT (-76.69369 38.95592) \n", - "55 850c52c4-5567-4e2f-b30a-fda4fcd0e121 POINT (-76.69369 38.95592) \n", - "56 f9895515-e652-483e-97d2-70412cafa722 POINT (-76.69369 38.95592) \n", - "\n", - " time_series_id monitoring_location_id parameter_code \\\n", - "0 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "1 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "2 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "3 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "4 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "5 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "6 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "7 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "8 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "9 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "10 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "11 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "12 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "13 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "14 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "15 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "16 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "17 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "18 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "19 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "20 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "21 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "22 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "23 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "24 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "25 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "26 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "27 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "28 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "29 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "30 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "31 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "32 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "33 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "34 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "35 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "36 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "37 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "38 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "39 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "40 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "41 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "42 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "43 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "44 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "45 fdd843797b3246efb9f4aa344b5946cc USGS-040851325 00060 \n", - "46 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "47 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "48 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "49 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "50 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "51 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "52 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "53 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "54 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "55 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "56 92a4e13a755d406abad1529f6add68e9 USGS-01594440 00060 \n", - "\n", - " unit_of_measure value last_modified time \\\n", - "0 ft^3/s 31100 2026-05-01 19:28:03.170712+00:00 1972-06-22 \n", - "1 ft^3/s 10600 2026-05-01 19:28:03.170712+00:00 1978-01-27 \n", - "2 ft^3/s 11500 2026-05-01 19:28:03.170712+00:00 1979-09-07 \n", - "3 ft^3/s 3940 2026-05-01 19:28:03.170712+00:00 1979-10-11 \n", - "4 ft^3/s 1640 2026-05-01 19:28:03.170712+00:00 1981-02-24 \n", - "5 ft^3/s 3380 2026-05-01 19:28:03.170712+00:00 1982-02-04 \n", - "6 ft^3/s 5750 2026-05-01 19:28:03.170712+00:00 1983-06-21 \n", - "7 ft^3/s 4340 2026-05-01 19:28:03.170712+00:00 1984-03-30 \n", - "8 ft^3/s 4730 2026-05-01 19:28:03.170712+00:00 1985-02-13 \n", - "9 ft^3/s 1520 2026-05-01 19:28:03.170712+00:00 1986-04-16 \n", - "10 ft^3/s 4060 2026-05-01 19:28:03.170712+00:00 1986-12-25 \n", - "11 ft^3/s 3510 2026-05-01 19:28:03.170712+00:00 1987-11-30 \n", - "12 ft^3/s 9190 2026-05-01 19:28:03.170712+00:00 1989-05-07 \n", - "13 ft^3/s 3140 2026-05-01 19:28:03.170712+00:00 1990-05-30 \n", - "14 ft^3/s 4750 2026-05-01 19:28:03.170712+00:00 1991-03-24 \n", - "15 ft^3/s 3200 2026-05-01 19:28:03.170712+00:00 1992-03-27 \n", - "16 ft^3/s 5550 2026-05-01 19:28:03.170712+00:00 1993-03-05 \n", - "17 ft^3/s 6960 2026-05-01 19:28:03.170712+00:00 1993-11-29 \n", - "18 ft^3/s 4100 2026-05-01 19:28:03.170712+00:00 1995-03-09 \n", - "19 ft^3/s 8280 2026-05-01 19:28:03.170712+00:00 1996-01-20 \n", - "20 ft^3/s 6900 2026-05-01 19:28:03.170712+00:00 1996-11-09 \n", - "21 ft^3/s 4840 2026-05-01 19:28:03.170712+00:00 1998-03-10 \n", - "22 ft^3/s 8200 2026-05-01 19:28:03.170712+00:00 1999-09-17 \n", - "23 ft^3/s 3640 2026-05-01 19:28:03.170712+00:00 2000-03-22 \n", - "24 ft^3/s 3800 2026-05-01 19:28:03.170712+00:00 2001-06-08 \n", - "25 ft^3/s 1510 2026-05-01 19:28:03.170712+00:00 2002-04-29 \n", - "26 ft^3/s 6990 2026-05-01 19:28:03.170712+00:00 2003-02-24 \n", - "27 ft^3/s 5790 2026-05-01 19:28:03.170712+00:00 2003-12-12 \n", - "28 ft^3/s 5210 2026-05-01 19:28:03.170712+00:00 2005-04-03 \n", - "29 ft^3/s 267 2026-05-05 03:36:50.236554+00:00 2005-08-18 \n", - "30 ft^3/s 288 2026-05-05 03:36:50.236554+00:00 2006-03-13 \n", - "31 ft^3/s 12700 2026-05-01 19:28:03.170712+00:00 2006-06-27 \n", - "32 ft^3/s 301 2026-05-05 03:36:50.236554+00:00 2007-03-14 \n", - "33 ft^3/s 5520 2026-05-01 19:28:03.170712+00:00 2007-04-16 \n", - "34 ft^3/s 7860 2026-05-01 19:28:03.170712+00:00 2008-05-13 \n", - "35 ft^3/s 4130 2026-05-01 19:28:03.170712+00:00 2009-06-19 \n", - "36 ft^3/s 5780 2026-05-01 19:28:03.170712+00:00 2010-03-14 \n", - "37 ft^3/s 386 2026-05-05 03:36:50.236554+00:00 2010-03-14 \n", - "38 ft^3/s 436 2026-05-05 03:36:50.236554+00:00 2011-03-18 \n", - "39 ft^3/s 16800 2026-05-01 19:28:03.170712+00:00 2011-09-08 \n", - "40 ft^3/s 4900 2026-05-01 19:28:03.170712+00:00 2011-12-09 \n", - "41 ft^3/s 150 2026-05-05 03:36:50.236554+00:00 2012-03-09 \n", - "42 ft^3/s 10800 2026-05-01 19:28:03.170712+00:00 2012-10-31 \n", - "43 ft^3/s 409 2026-05-05 03:36:50.236554+00:00 2013-03-31 \n", - "44 ft^3/s 15600 2026-05-01 19:28:03.170712+00:00 2014-05-01 \n", - "45 ft^3/s 429 2026-05-05 03:36:50.236554+00:00 2014-06-02 \n", - "46 ft^3/s 6610 2026-05-01 19:28:03.170712+00:00 2015-06-28 \n", - "47 ft^3/s 6140 2026-05-01 19:28:03.170712+00:00 2016-08-01 \n", - "48 ft^3/s 4960 2026-05-01 19:28:03.170712+00:00 2017-07-30 \n", - "49 ft^3/s 8360 2026-05-01 19:28:03.170712+00:00 2018-06-04 \n", - "50 ft^3/s 7220 2026-05-01 19:28:03.170712+00:00 2018-12-17 \n", - "51 ft^3/s 4340 2026-05-01 19:28:03.170712+00:00 2020-04-14 \n", - "52 ft^3/s 4000 2026-05-01 19:28:03.170712+00:00 2020-11-12 \n", - "53 ft^3/s 4590 2026-05-01 19:28:03.170712+00:00 2021-10-30 \n", - "54 ft^3/s 3330 2026-05-01 19:28:03.170712+00:00 2022-12-16 \n", - "55 ft^3/s 7400 2026-05-01 19:28:03.170712+00:00 2024-01-11 \n", - "56 ft^3/s 2320 2026-05-01 19:28:03.170712+00:00 2025-06-19 \n", - "\n", - " water_year year month day time_of_day peak_since \n", - "0 1972 1972 6 22 NaN None \n", - "1 1978 1978 1 27 NaN None \n", - "2 1979 1979 9 7 NaN None \n", - "3 1980 1979 10 11 NaN None \n", - "4 1981 1981 2 24 NaN None \n", - "5 1982 1982 2 4 NaN None \n", - "6 1983 1983 6 21 NaN None \n", - "7 1984 1984 3 30 NaN None \n", - "8 1985 1985 2 13 NaN None \n", - "9 1986 1986 4 16 NaN None \n", - "10 1987 1986 12 25 NaN None \n", - "11 1988 1987 11 30 NaN None \n", - "12 1989 1989 5 7 NaN None \n", - "13 1990 1990 5 30 NaN None \n", - "14 1991 1991 3 24 NaN None \n", - "15 1992 1992 3 27 NaN None \n", - "16 1993 1993 3 5 NaN None \n", - "17 1994 1993 11 29 NaN None \n", - "18 1995 1995 3 9 NaN None \n", - "19 1996 1996 1 20 NaN None \n", - "20 1997 1996 11 9 NaN None \n", - "21 1998 1998 3 10 NaN None \n", - "22 1999 1999 9 17 NaN None \n", - "23 2000 2000 3 22 NaN None \n", - "24 2001 2001 6 8 10:30:00+00:00 None \n", - "25 2002 2002 4 29 NaN None \n", - "26 2003 2003 2 24 00:30:00+00:00 None \n", - "27 2004 2003 12 12 15:45:00+00:00 None \n", - "28 2005 2005 4 3 23:15:00+00:00 None \n", - "29 2005 2005 8 18 NaN None \n", - "30 2006 2006 3 13 NaN None \n", - "31 2006 2006 6 27 03:00:00+00:00 None \n", - "32 2007 2007 3 14 NaN None \n", - "33 2007 2007 4 16 15:15:00+00:00 None \n", - "34 2008 2008 5 13 06:00:00+00:00 None \n", - "35 2009 2009 6 19 09:45:00+00:00 None \n", - "36 2010 2010 3 14 17:00:00+00:00 None \n", - "37 2010 2010 3 14 NaN None \n", - "38 2011 2011 3 18 NaN None \n", - "39 2011 2011 9 8 17:15:00+00:00 None \n", - "40 2012 2011 12 9 01:30:00+00:00 None \n", - "41 2012 2012 3 9 NaN None \n", - "42 2013 2012 10 31 03:00:00+00:00 None \n", - "43 2013 2013 3 31 NaN None \n", - "44 2014 2014 5 1 21:30:00+00:00 None \n", - "45 2014 2014 6 2 NaN None \n", - "46 2015 2015 6 28 22:15:00+00:00 None \n", - "47 2016 2016 8 1 04:30:00+00:00 None \n", - "48 2017 2017 7 30 05:15:00+00:00 None \n", - "49 2018 2018 6 4 22:30:00+00:00 None \n", - "50 2019 2018 12 17 04:30:00+00:00 None \n", - "51 2020 2020 4 14 13:15:00+00:00 None \n", - "52 2021 2020 11 12 22:45:00+00:00 None \n", - "53 2022 2021 10 30 21:45:00+00:00 None \n", - "54 2023 2022 12 16 19:15:00+00:00 None \n", - "55 2024 2024 1 11 00:15:00+00:00 None \n", - "56 2025 2025 6 19 18:45:00+00:00 None " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "display(peak_data[0])" ] @@ -1471,39 +109,9 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:53.076772Z", - "iopub.status.busy": "2026-05-26T16:32:53.076623Z", - "iopub.status.idle": "2026-05-26T16:32:53.080026Z", - "shell.execute_reply": "2026-05-26T16:32:53.079488Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "peak_id str\n", - "geometry geometry\n", - "time_series_id str\n", - "monitoring_location_id str\n", - "parameter_code str\n", - "unit_of_measure str\n", - "value int64\n", - "last_modified datetime64[us, UTC]\n", - "time datetime64[us]\n", - "water_year int64\n", - "year int64\n", - "month int64\n", - "day int64\n", - "time_of_day str\n", - "peak_since object\n", - "dtype: object\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print(peak_data[0].dtypes)" ] @@ -1517,24 +125,9 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:53.081836Z", - "iopub.status.busy": "2026-05-26T16:32:53.081722Z", - "iopub.status.idle": "2026-05-26T16:32:53.084237Z", - "shell.execute_reply": "2026-05-26T16:32:53.083710Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The query URL used to retrieve the data was: https://api.waterdata.usgs.gov/ogcapi/v0/collections/peaks/items?monitoring_location_id=USGS-01594440%2CUSGS-040851325¶meter_code=00060&skipGeometry=false&limit=50000\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print(\"The query URL used to retrieve the data was: \" + peak_data[1].url)" ] @@ -1548,41 +141,9 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:53.085717Z", - "iopub.status.busy": "2026-05-26T16:32:53.085625Z", - "iopub.status.idle": "2026-05-26T16:32:53.771173Z", - "shell.execute_reply": "2026-05-26T16:32:53.770633Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: peaks · 1 page · 97 rows · 924/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "['00060', '00065']\n", - "Length: 2, dtype: str\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "all_params = waterdata.get_peaks(monitoring_location_id=\"USGS-01594440\")\n", "print(all_params[0][\"parameter_code\"].unique())" @@ -1599,726 +160,9 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:53.773306Z", - "iopub.status.busy": "2026-05-26T16:32:53.773149Z", - "iopub.status.idle": "2026-05-26T16:32:54.494388Z", - "shell.execute_reply": "2026-05-26T16:32:54.493754Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: peaks · 1 page · 29 rows · 924/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
08489606e-89e2-4cab-9758-969be99c7efcPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9642026-05-06 17:32:44.238660+00:001937-04-2819371937428.0NoneNone
12091dc02-2a41-4bd7-a38d-018aff41d2d3POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s10302026-05-06 17:32:44.238660+00:001938-04-2719381938427.0NoneNone
254d4673d-232b-4c0f-84ac-3f2cfa25eae6POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8292026-05-06 17:32:44.238660+00:001939-04-1919391939419.0NoneNone
3d65a02cd-7e38-419e-9ca0-f82f7ffb66bbPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5902026-05-06 17:32:44.238660+00:001940-04-1419401940414.0NoneNone
4873cdcce-0b39-4328-b5d2-279318692e40POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s4302026-05-06 17:32:44.238660+00:001941-04-1119411941411.0NoneNone
595dc6356-39c1-4a3b-b6e4-05f346366e70POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8832026-05-06 17:32:44.238660+00:001942-04-1219421942412.0NoneNone
63889ccaf-cac9-40fd-8900-56ee32fad15fPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s6812026-05-06 17:32:44.238660+00:001945-05-041945194554.0NoneNone
713c36dc5-80ec-43b0-96f5-62a072a7d2d3POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9612026-05-06 17:32:44.238660+00:001946-04-2119461946421.0NoneNone
857120111-fa2d-476e-bdf7-603cbfb03350POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7812026-05-06 17:32:44.238660+00:001947-05-021947194752.0NoneNone
98e99a440-8114-4ded-9d68-5a06f9d7c7eaPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8202026-05-06 17:32:44.238660+00:001948-04-3019481948430.0NoneNone
104de7db39-d552-4e44-9625-e80af36f552cPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7262026-05-06 17:32:44.238660+00:001949-04-2719491949427.0NoneNone
11f9a71afe-de67-497e-a6d4-9f660041f0dbPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7812026-05-06 17:32:44.238660+00:001950-04-2219501950422.0NoneNone
12f2dac095-aeac-4999-bfb7-4092d1651536POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s6402026-05-06 17:32:44.238660+00:001951-04-2319511951423.0NoneNone
1342dfc367-59cf-4d12-ab62-405f856697caPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s13602026-05-06 17:32:44.238660+00:001952-04-3019521952430.0NoneNone
14c8c99d0a-1172-4ef4-adf4-892331793e1ePOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7152026-05-06 17:32:44.238660+00:001953-05-051953195355.0NoneNone
15dd627cb7-9bc0-43fc-a492-cbc38d0b48d7POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5462026-05-06 17:32:44.238660+00:001954-04-2319541954423.0NoneNone
16c1e1d9c4-6ad8-4280-baf8-742511f5e9efPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5962026-05-06 17:32:44.238660+00:001956-04-2419561956424.0NoneNone
17d2f8e901-5fe4-41ca-944a-fb430e30b678POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s10202026-05-06 17:32:44.238660+00:001957-05-061957195756.0NoneNone
188eb49b9a-dad1-4017-b07f-5a34b8918062POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5902026-05-06 17:32:44.238660+00:001958-04-3019581958430.0NoneNone
19184e855e-57e8-4521-a570-97d9e15e8973POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s3522026-05-06 17:32:44.238660+00:001959-04-2219591959422.0NoneNone
2057371ba5-8341-4d3e-ac4a-3d6f74e2a515POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s8002026-05-06 17:32:44.238660+00:001960-04-081960196048.0NoneNone
21185789b2-59cd-42ff-b37f-f1fc734f85b3POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s3002026-05-06 17:32:44.238660+00:001961-04-051961196145.0NoneNone
2294025d15-a950-46ee-915e-15eb9e009819POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9002026-05-06 17:32:44.238660+00:001962-04-2719621962427.0NoneNone
23945a0c09-7b64-4f4b-91bc-cd1d83b3a248POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s4862026-05-06 17:32:44.238660+00:001963-04-2019631963420.0NoneNone
2410ae014a-2a37-466a-aba4-78540e1fda47POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7512026-05-06 17:32:44.238660+00:001964-04-2819641964428.0NoneNone
2559a2fcb8-bed1-4ac8-8582-bb63fb9f9e11POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s9582026-05-06 17:32:44.238660+00:001965-04-2819651965428.0NoneNone
2649ea8bac-0e8a-4bec-b981-49a69c925ca7POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5242026-05-06 17:32:44.238660+00:001966-04-1619661966416.0NoneNone
276dee7073-0873-4765-91f1-ed8afb934620POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s6042026-05-06 17:32:44.238660+00:001967-05-1219671967512.0NoneNone
2821969473-a764-470b-8ec6-5fa316dbe362POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s11802026-05-06 17:32:44.238660+00:001984-05-01198419845NaNNoneNone
\n", - "
" - ], - "text/plain": [ - " peak_id geometry \\\n", - "0 8489606e-89e2-4cab-9758-969be99c7efc POINT (-112.05641 44.64793) \n", - "1 2091dc02-2a41-4bd7-a38d-018aff41d2d3 POINT (-112.05641 44.64793) \n", - "2 54d4673d-232b-4c0f-84ac-3f2cfa25eae6 POINT (-112.05641 44.64793) \n", - "3 d65a02cd-7e38-419e-9ca0-f82f7ffb66bb POINT (-112.05641 44.64793) \n", - "4 873cdcce-0b39-4328-b5d2-279318692e40 POINT (-112.05641 44.64793) \n", - "5 95dc6356-39c1-4a3b-b6e4-05f346366e70 POINT (-112.05641 44.64793) \n", - "6 3889ccaf-cac9-40fd-8900-56ee32fad15f POINT (-112.05641 44.64793) \n", - "7 13c36dc5-80ec-43b0-96f5-62a072a7d2d3 POINT (-112.05641 44.64793) \n", - "8 57120111-fa2d-476e-bdf7-603cbfb03350 POINT (-112.05641 44.64793) \n", - "9 8e99a440-8114-4ded-9d68-5a06f9d7c7ea POINT (-112.05641 44.64793) \n", - "10 4de7db39-d552-4e44-9625-e80af36f552c POINT (-112.05641 44.64793) \n", - "11 f9a71afe-de67-497e-a6d4-9f660041f0db POINT (-112.05641 44.64793) \n", - "12 f2dac095-aeac-4999-bfb7-4092d1651536 POINT (-112.05641 44.64793) \n", - "13 42dfc367-59cf-4d12-ab62-405f856697ca POINT (-112.05641 44.64793) \n", - "14 c8c99d0a-1172-4ef4-adf4-892331793e1e POINT (-112.05641 44.64793) \n", - "15 dd627cb7-9bc0-43fc-a492-cbc38d0b48d7 POINT (-112.05641 44.64793) \n", - "16 c1e1d9c4-6ad8-4280-baf8-742511f5e9ef POINT (-112.05641 44.64793) \n", - "17 d2f8e901-5fe4-41ca-944a-fb430e30b678 POINT (-112.05641 44.64793) \n", - "18 8eb49b9a-dad1-4017-b07f-5a34b8918062 POINT (-112.05641 44.64793) \n", - "19 184e855e-57e8-4521-a570-97d9e15e8973 POINT (-112.05641 44.64793) \n", - "20 57371ba5-8341-4d3e-ac4a-3d6f74e2a515 POINT (-112.05641 44.64793) \n", - "21 185789b2-59cd-42ff-b37f-f1fc734f85b3 POINT (-112.05641 44.64793) \n", - "22 94025d15-a950-46ee-915e-15eb9e009819 POINT (-112.05641 44.64793) \n", - "23 945a0c09-7b64-4f4b-91bc-cd1d83b3a248 POINT (-112.05641 44.64793) \n", - "24 10ae014a-2a37-466a-aba4-78540e1fda47 POINT (-112.05641 44.64793) \n", - "25 59a2fcb8-bed1-4ac8-8582-bb63fb9f9e11 POINT (-112.05641 44.64793) \n", - "26 49ea8bac-0e8a-4bec-b981-49a69c925ca7 POINT (-112.05641 44.64793) \n", - "27 6dee7073-0873-4765-91f1-ed8afb934620 POINT (-112.05641 44.64793) \n", - "28 21969473-a764-470b-8ec6-5fa316dbe362 POINT (-112.05641 44.64793) \n", - "\n", - " time_series_id monitoring_location_id parameter_code \\\n", - "0 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "1 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "2 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "3 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "4 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "5 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "6 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "7 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "8 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "9 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "10 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "11 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "12 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "13 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "14 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "15 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "16 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "17 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "18 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "19 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "20 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "21 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "22 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "23 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "24 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "25 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "26 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "27 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "28 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "\n", - " unit_of_measure value last_modified time \\\n", - "0 ft^3/s 964 2026-05-06 17:32:44.238660+00:00 1937-04-28 \n", - "1 ft^3/s 1030 2026-05-06 17:32:44.238660+00:00 1938-04-27 \n", - "2 ft^3/s 829 2026-05-06 17:32:44.238660+00:00 1939-04-19 \n", - "3 ft^3/s 590 2026-05-06 17:32:44.238660+00:00 1940-04-14 \n", - "4 ft^3/s 430 2026-05-06 17:32:44.238660+00:00 1941-04-11 \n", - "5 ft^3/s 883 2026-05-06 17:32:44.238660+00:00 1942-04-12 \n", - "6 ft^3/s 681 2026-05-06 17:32:44.238660+00:00 1945-05-04 \n", - "7 ft^3/s 961 2026-05-06 17:32:44.238660+00:00 1946-04-21 \n", - "8 ft^3/s 781 2026-05-06 17:32:44.238660+00:00 1947-05-02 \n", - "9 ft^3/s 820 2026-05-06 17:32:44.238660+00:00 1948-04-30 \n", - "10 ft^3/s 726 2026-05-06 17:32:44.238660+00:00 1949-04-27 \n", - "11 ft^3/s 781 2026-05-06 17:32:44.238660+00:00 1950-04-22 \n", - "12 ft^3/s 640 2026-05-06 17:32:44.238660+00:00 1951-04-23 \n", - "13 ft^3/s 1360 2026-05-06 17:32:44.238660+00:00 1952-04-30 \n", - "14 ft^3/s 715 2026-05-06 17:32:44.238660+00:00 1953-05-05 \n", - "15 ft^3/s 546 2026-05-06 17:32:44.238660+00:00 1954-04-23 \n", - "16 ft^3/s 596 2026-05-06 17:32:44.238660+00:00 1956-04-24 \n", - "17 ft^3/s 1020 2026-05-06 17:32:44.238660+00:00 1957-05-06 \n", - "18 ft^3/s 590 2026-05-06 17:32:44.238660+00:00 1958-04-30 \n", - "19 ft^3/s 352 2026-05-06 17:32:44.238660+00:00 1959-04-22 \n", - "20 ft^3/s 800 2026-05-06 17:32:44.238660+00:00 1960-04-08 \n", - "21 ft^3/s 300 2026-05-06 17:32:44.238660+00:00 1961-04-05 \n", - "22 ft^3/s 900 2026-05-06 17:32:44.238660+00:00 1962-04-27 \n", - "23 ft^3/s 486 2026-05-06 17:32:44.238660+00:00 1963-04-20 \n", - "24 ft^3/s 751 2026-05-06 17:32:44.238660+00:00 1964-04-28 \n", - "25 ft^3/s 958 2026-05-06 17:32:44.238660+00:00 1965-04-28 \n", - "26 ft^3/s 524 2026-05-06 17:32:44.238660+00:00 1966-04-16 \n", - "27 ft^3/s 604 2026-05-06 17:32:44.238660+00:00 1967-05-12 \n", - "28 ft^3/s 1180 2026-05-06 17:32:44.238660+00:00 1984-05-01 \n", - "\n", - " water_year year month day time_of_day peak_since \n", - "0 1937 1937 4 28.0 None None \n", - "1 1938 1938 4 27.0 None None \n", - "2 1939 1939 4 19.0 None None \n", - "3 1940 1940 4 14.0 None None \n", - "4 1941 1941 4 11.0 None None \n", - "5 1942 1942 4 12.0 None None \n", - "6 1945 1945 5 4.0 None None \n", - "7 1946 1946 4 21.0 None None \n", - "8 1947 1947 5 2.0 None None \n", - "9 1948 1948 4 30.0 None None \n", - "10 1949 1949 4 27.0 None None \n", - "11 1950 1950 4 22.0 None None \n", - "12 1951 1951 4 23.0 None None \n", - "13 1952 1952 4 30.0 None None \n", - "14 1953 1953 5 5.0 None None \n", - "15 1954 1954 4 23.0 None None \n", - "16 1956 1956 4 24.0 None None \n", - "17 1957 1957 5 6.0 None None \n", - "18 1958 1958 4 30.0 None None \n", - "19 1959 1959 4 22.0 None None \n", - "20 1960 1960 4 8.0 None None \n", - "21 1961 1961 4 5.0 None None \n", - "22 1962 1962 4 27.0 None None \n", - "23 1963 1963 4 20.0 None None \n", - "24 1964 1964 4 28.0 None None \n", - "25 1965 1965 4 28.0 None None \n", - "26 1966 1966 4 16.0 None None \n", - "27 1967 1967 5 12.0 None None \n", - "28 1984 1984 5 NaN None None " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "station = \"USGS-06011000\"\n", "data3 = waterdata.get_peaks(monitoring_location_id=station, parameter_code=\"00060\")\n", @@ -2334,220 +178,9 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:32:54.496099Z", - "iopub.status.busy": "2026-05-26T16:32:54.495977Z", - "iopub.status.idle": "2026-05-26T16:32:54.955444Z", - "shell.execute_reply": "2026-05-26T16:32:54.954916Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: peaks · 1 page · 6 rows · 923/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
0c8c99d0a-1172-4ef4-adf4-892331793e1ePOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s7152026-05-06 17:32:44.238660+00:001953-05-051953195355NoneNone
1dd627cb7-9bc0-43fc-a492-cbc38d0b48d7POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5462026-05-06 17:32:44.238660+00:001954-04-2319541954423NoneNone
2c1e1d9c4-6ad8-4280-baf8-742511f5e9efPOINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5962026-05-06 17:32:44.238660+00:001956-04-2419561956424NoneNone
3d2f8e901-5fe4-41ca-944a-fb430e30b678POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s10202026-05-06 17:32:44.238660+00:001957-05-061957195756NoneNone
48eb49b9a-dad1-4017-b07f-5a34b8918062POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s5902026-05-06 17:32:44.238660+00:001958-04-3019581958430NoneNone
5184e855e-57e8-4521-a570-97d9e15e8973POINT (-112.05641 44.64793)1803f93f789a46f093cc4e7695554bdbUSGS-0601100000060ft^3/s3522026-05-06 17:32:44.238660+00:001959-04-2219591959422NoneNone
\n", - "
" - ], - "text/plain": [ - " peak_id geometry \\\n", - "0 c8c99d0a-1172-4ef4-adf4-892331793e1e POINT (-112.05641 44.64793) \n", - "1 dd627cb7-9bc0-43fc-a492-cbc38d0b48d7 POINT (-112.05641 44.64793) \n", - "2 c1e1d9c4-6ad8-4280-baf8-742511f5e9ef POINT (-112.05641 44.64793) \n", - "3 d2f8e901-5fe4-41ca-944a-fb430e30b678 POINT (-112.05641 44.64793) \n", - "4 8eb49b9a-dad1-4017-b07f-5a34b8918062 POINT (-112.05641 44.64793) \n", - "5 184e855e-57e8-4521-a570-97d9e15e8973 POINT (-112.05641 44.64793) \n", - "\n", - " time_series_id monitoring_location_id parameter_code \\\n", - "0 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "1 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "2 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "3 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "4 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "5 1803f93f789a46f093cc4e7695554bdb USGS-06011000 00060 \n", - "\n", - " unit_of_measure value last_modified time \\\n", - "0 ft^3/s 715 2026-05-06 17:32:44.238660+00:00 1953-05-05 \n", - "1 ft^3/s 546 2026-05-06 17:32:44.238660+00:00 1954-04-23 \n", - "2 ft^3/s 596 2026-05-06 17:32:44.238660+00:00 1956-04-24 \n", - "3 ft^3/s 1020 2026-05-06 17:32:44.238660+00:00 1957-05-06 \n", - "4 ft^3/s 590 2026-05-06 17:32:44.238660+00:00 1958-04-30 \n", - "5 ft^3/s 352 2026-05-06 17:32:44.238660+00:00 1959-04-22 \n", - "\n", - " water_year year month day time_of_day peak_since \n", - "0 1953 1953 5 5 None None \n", - "1 1954 1954 4 23 None None \n", - "2 1956 1956 4 24 None None \n", - "3 1957 1957 5 6 None None \n", - "4 1958 1958 4 30 None None \n", - "5 1959 1959 4 22 None None " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "data4 = waterdata.get_peaks(\n", " monitoring_location_id=station,\n", @@ -2573,8 +206,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb index fec41c33..398fac34 100644 --- a/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_Ratings_Examples.ipynb @@ -23,14 +23,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:35:38.570189Z", - "iopub.status.busy": "2026-05-26T16:35:38.570015Z", - "iopub.status.idle": "2026-05-26T16:35:41.024884Z", - "shell.execute_reply": "2026-05-26T16:35:41.024041Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "!pip install dataretrieval" @@ -45,15 +38,8 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:35:41.027533Z", - "iopub.status.busy": "2026-05-26T16:35:41.027350Z", - "iopub.status.idle": "2026-05-26T16:35:41.625447Z", - "shell.execute_reply": "2026-05-26T16:35:41.624733Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from IPython.display import display\n", @@ -84,24 +70,9 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:35:41.627674Z", - "iopub.status.busy": "2026-05-26T16:35:41.627479Z", - "iopub.status.idle": "2026-05-26T16:35:42.922768Z", - "shell.execute_reply": "2026-05-26T16:35:42.921494Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 409 rating points.\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Specify the USGS monitoring location id\n", "site_id = \"USGS-10109000\"\n", @@ -130,147 +101,9 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:35:42.926145Z", - "iopub.status.busy": "2026-05-26T16:35:42.925867Z", - "iopub.status.idle": "2026-05-26T16:35:42.957032Z", - "shell.execute_reply": "2026-05-26T16:35:42.956252Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
INDEPSHIFTDEPSTOR
01.77-0.077.46NaN
11.78-0.077.87NaN
21.79-0.078.29NaN
31.80-0.078.73NaN
41.81-0.079.18NaN
...............
4045.810.002175.11NaN
4055.820.002186.34NaN
4065.830.002197.61NaN
4075.840.002208.91NaN
4085.850.002220.24*
\n", - "

409 rows × 4 columns

\n", - "
" - ], - "text/plain": [ - " INDEP SHIFT DEP STOR\n", - "0 1.77 -0.07 7.46 NaN\n", - "1 1.78 -0.07 7.87 NaN\n", - "2 1.79 -0.07 8.29 NaN\n", - "3 1.80 -0.07 8.73 NaN\n", - "4 1.81 -0.07 9.18 NaN\n", - ".. ... ... ... ...\n", - "404 5.81 0.00 2175.11 NaN\n", - "405 5.82 0.00 2186.34 NaN\n", - "406 5.83 0.00 2197.61 NaN\n", - "407 5.84 0.00 2208.91 NaN\n", - "408 5.85 0.00 2220.24 *\n", - "\n", - "[409 rows x 4 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "display(rating)" ] @@ -284,28 +117,9 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:35:42.959125Z", - "iopub.status.busy": "2026-05-26T16:35:42.958970Z", - "iopub.status.idle": "2026-05-26T16:35:42.962068Z", - "shell.execute_reply": "2026-05-26T16:35:42.961463Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "INDEP float64\n", - "SHIFT float64\n", - "DEP float64\n", - "STOR str\n", - "dtype: object\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print(rating.dtypes)" ] @@ -319,24 +133,9 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:35:42.963937Z", - "iopub.status.busy": "2026-05-26T16:35:42.963829Z", - "iopub.status.idle": "2026-05-26T16:35:42.966039Z", - "shell.execute_reply": "2026-05-26T16:35:42.965512Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The rating was fetched from: https://api.waterdata.usgs.gov/stac-files/ratings/USGS.10109000.exsa.rdb\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print(\"The rating was fetched from: \" + rating.attrs[\"url\"])" ] @@ -350,24 +149,9 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:35:42.967727Z", - "iopub.status.busy": "2026-05-26T16:35:42.967608Z", - "iopub.status.idle": "2026-05-26T16:35:43.899866Z", - "shell.execute_reply": "2026-05-26T16:35:43.899110Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 2426 rating points.\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "site_id = \"USGS-01594440\"\n", "ratings = waterdata.get_ratings(monitoring_location_id=site_id, file_type=\"exsa\")\n", @@ -391,8 +175,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb index 6ed4fa17..4eae6267 100644 --- a/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_Statistics_Examples.ipynb @@ -26,14 +26,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:47.180991Z", - "iopub.status.busy": "2026-05-26T16:30:47.180861Z", - "iopub.status.idle": "2026-05-26T16:30:48.642662Z", - "shell.execute_reply": "2026-05-26T16:30:48.641964Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "!pip install dataretrieval" @@ -48,15 +41,8 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:48.645517Z", - "iopub.status.busy": "2026-05-26T16:30:48.645347Z", - "iopub.status.idle": "2026-05-26T16:30:49.348863Z", - "shell.execute_reply": "2026-05-26T16:30:49.348112Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from IPython.display import display\n", @@ -88,39 +74,9 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:49.351135Z", - "iopub.status.busy": "2026-05-26T16:30:49.350950Z", - "iopub.status.idle": "2026-05-26T16:30:49.931817Z", - "shell.execute_reply": "2026-05-26T16:30:49.930934Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: observationIntervals · 1 page · 344 rows · 3,925/4,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Retrieved 344 statistic values.\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Set the parameters needed to retrieve data\n", "site = \"USGS-02319394\"\n", @@ -148,404 +104,9 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:49.933747Z", - "iopub.status.busy": "2026-05-26T16:30:49.933590Z", - "iopub.status.idle": "2026-05-26T16:30:49.948399Z", - "shell.execute_reply": "2026-05-26T16:30:49.947865Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
geometrymonitoring_location_idmonitoring_location_namesite_typesite_type_codecountry_codestate_codecounty_codestart_dateend_dateinterval_typevaluepercentilesample_countapproval_statuscomputation_idcomputationparameter_codeunit_of_measureparent_time_series_id
0POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792000-11-012000-11-30month600.967NaN30approved30eb30d0-c252-4601-a702-1e053e387200arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
1POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792000-12-012000-12-31month812.903NaN31approved0bfbd34c-53ed-429b-bea1-facc88c3c639arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
2POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792001-01-012001-01-31month1668.387NaN31approved533f6064-f298-4eb1-b74f-da0c09d8c525arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
3POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792001-02-012001-02-28month1234.286NaN28approved67ee8c57-e9d0-42a9-826f-a86dbac7a377arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
4POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792001-03-012001-03-31month3782.581NaN31approved7d13bc10-dacc-48c5-bf41-63c13569f47carithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
...............................................................
339POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792021-10-012022-09-30water_year2034.863NaN365approveda579a7a4-b5bc-45ce-a754-9d784d05dffearithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
340POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792022-10-012023-09-30water_year1731.784NaN365approvedef755db3-4cb0-44ec-9192-1a2f85708608arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
341POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792023-10-012024-09-30water_year3392.778NaN365approvedf73ed1da-35e0-4167-9387-cab6aad5961carithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
342POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792024-10-012025-09-30water_year1938.863NaN365approved5004f92c-d1f8-4c1c-9a41-ce1e73d15888arithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
343POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792025-10-012026-02-08water_year394.382NaN131approved3f636529-a609-4e82-86b0-6c0e03c0072darithmetic_mean00060ft^3/sdabac917c8ea4f66a163ddc9d3bb4840
\n", - "

344 rows × 20 columns

\n", - "
" - ], - "text/plain": [ - " geometry monitoring_location_id \\\n", - "0 POINT (-83.18014 30.41049) USGS-02319394 \n", - "1 POINT (-83.18014 30.41049) USGS-02319394 \n", - "2 POINT (-83.18014 30.41049) USGS-02319394 \n", - "3 POINT (-83.18014 30.41049) USGS-02319394 \n", - "4 POINT (-83.18014 30.41049) USGS-02319394 \n", - ".. ... ... \n", - "339 POINT (-83.18014 30.41049) USGS-02319394 \n", - "340 POINT (-83.18014 30.41049) USGS-02319394 \n", - "341 POINT (-83.18014 30.41049) USGS-02319394 \n", - "342 POINT (-83.18014 30.41049) USGS-02319394 \n", - "343 POINT (-83.18014 30.41049) USGS-02319394 \n", - "\n", - " monitoring_location_name site_type site_type_code country_code \\\n", - "0 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "1 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "2 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "3 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "4 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - ".. ... ... ... ... \n", - "339 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "340 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "341 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "342 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "343 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "\n", - " state_code county_code start_date end_date interval_type value \\\n", - "0 12 079 2000-11-01 2000-11-30 month 600.967 \n", - "1 12 079 2000-12-01 2000-12-31 month 812.903 \n", - "2 12 079 2001-01-01 2001-01-31 month 1668.387 \n", - "3 12 079 2001-02-01 2001-02-28 month 1234.286 \n", - "4 12 079 2001-03-01 2001-03-31 month 3782.581 \n", - ".. ... ... ... ... ... ... \n", - "339 12 079 2021-10-01 2022-09-30 water_year 2034.863 \n", - "340 12 079 2022-10-01 2023-09-30 water_year 1731.784 \n", - "341 12 079 2023-10-01 2024-09-30 water_year 3392.778 \n", - "342 12 079 2024-10-01 2025-09-30 water_year 1938.863 \n", - "343 12 079 2025-10-01 2026-02-08 water_year 394.382 \n", - "\n", - " percentile sample_count approval_status \\\n", - "0 NaN 30 approved \n", - "1 NaN 31 approved \n", - "2 NaN 31 approved \n", - "3 NaN 28 approved \n", - "4 NaN 31 approved \n", - ".. ... ... ... \n", - "339 NaN 365 approved \n", - "340 NaN 365 approved \n", - "341 NaN 365 approved \n", - "342 NaN 365 approved \n", - "343 NaN 131 approved \n", - "\n", - " computation_id computation parameter_code \\\n", - "0 30eb30d0-c252-4601-a702-1e053e387200 arithmetic_mean 00060 \n", - "1 0bfbd34c-53ed-429b-bea1-facc88c3c639 arithmetic_mean 00060 \n", - "2 533f6064-f298-4eb1-b74f-da0c09d8c525 arithmetic_mean 00060 \n", - "3 67ee8c57-e9d0-42a9-826f-a86dbac7a377 arithmetic_mean 00060 \n", - "4 7d13bc10-dacc-48c5-bf41-63c13569f47c arithmetic_mean 00060 \n", - ".. ... ... ... \n", - "339 a579a7a4-b5bc-45ce-a754-9d784d05dffe arithmetic_mean 00060 \n", - "340 ef755db3-4cb0-44ec-9192-1a2f85708608 arithmetic_mean 00060 \n", - "341 f73ed1da-35e0-4167-9387-cab6aad5961c arithmetic_mean 00060 \n", - "342 5004f92c-d1f8-4c1c-9a41-ce1e73d15888 arithmetic_mean 00060 \n", - "343 3f636529-a609-4e82-86b0-6c0e03c0072d arithmetic_mean 00060 \n", - "\n", - " unit_of_measure parent_time_series_id \n", - "0 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "1 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "2 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "3 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "4 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - ".. ... ... \n", - "339 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "340 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "341 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "342 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "343 ft^3/s dabac917c8ea4f66a163ddc9d3bb4840 \n", - "\n", - "[344 rows x 20 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Display the data frame as a table\n", "display(x1[0])" @@ -560,44 +121,9 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:49.949852Z", - "iopub.status.busy": "2026-05-26T16:30:49.949737Z", - "iopub.status.idle": "2026-05-26T16:30:49.952369Z", - "shell.execute_reply": "2026-05-26T16:30:49.951838Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "geometry geometry\n", - "monitoring_location_id str\n", - "monitoring_location_name str\n", - "site_type str\n", - "site_type_code str\n", - "country_code str\n", - "state_code str\n", - "county_code str\n", - "start_date str\n", - "end_date str\n", - "interval_type str\n", - "value str\n", - "percentile float64\n", - "sample_count int64\n", - "approval_status str\n", - "computation_id str\n", - "computation str\n", - "parameter_code str\n", - "unit_of_measure str\n", - "parent_time_series_id str\n", - "dtype: object\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print(x1[0].dtypes)" ] @@ -611,37 +137,9 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:49.953886Z", - "iopub.status.busy": "2026-05-26T16:30:49.953772Z", - "iopub.status.idle": "2026-05-26T16:30:50.256250Z", - "shell.execute_reply": "2026-05-26T16:30:50.255766Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Text(0, 0.5, 'Annual mean discharge (cfs)')" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAGwCAYAAABIC3rIAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAh8tJREFUeJztnQd4VHX2/g/pvRBISAihQ+hdQFBREGwo6s91Ldh1dbGi4p/Vta4r6trW3lZ3XVTUtaIiSFOaSO+9BEgjIb2X+T/nO/O9mUmdydw7c8v7eZ5hbmYuk5s7M/eee8573tPBZrPZCAAAAADAwgT4ewMAAAAAAPwNAiIAAAAAWB4ERAAAAACwPAiIAAAAAGB5EBABAAAAwPIgIAIAAACA5UFABAAAAADLE2T5PeAG9fX1lJmZSdHR0dShQwfsMgAAAMAAsNViSUkJpaSkUEBA6zkgBERuwMFQt27d1Hp/AAAAAOBDjh07Rqmpqa2ug4DIDTgzJHdoTEyMOu8OAAAAADSluLhYJDTkebw1EBC5gSyTcTCEgAgAAAAwFu7IXSCqBgAAAIDlQUAEAAAAAMuDgAgAAAAAlgcBEQAAAAAsDwIiAAAAAFgeBEQAAAAAsDwIiAAAAABgeRAQAQAAAMDyICACAAAAgOVBQAQAAAAAy4OACAAAAACWBwERAAAAACwPAiJgWqpq6/y9CQAAoFtsNhtV1uA4KUFABEzJtuOFNOSxxfTKz/v9vSkAAKBL/jx/E415+mfKL63y96boAgREwJT8uj+Pquvq6bfD+f7eFAAA0CWrD+RRSWUt7c0p8fem6AIERMCUZOSXi/uiihp/bwoAAOgOLpUVV9aK5aJyHCcZBETAlBw9VSbuERABAEBTcosbymSFuHAUICACps4QFeOLDgAATcgpqVSWC8qrsYcQEAGzpoKziu1f9pKqWqqvt/l7kwAAQLcZIpTM7CBDBEzH8YJysjliIL5n0SAAAIAGchwXjUwhNEQCBETAdBx1lMskxZUQDAIAgDO5Jc4aIpTMGAREwPQBEYTVAADgSi4yRE1AQARMR8YpBEQAAOBuhggXjXYQEAHTcTTf3nIvQacZAAC4Ag1RUxAQAdOWzCJDAsU9rn4AAKCVgAgaIgECImAq6uptdKzAHhAN6hor7hEQAQBA8y7V9p/rMeQVAREwG1lFFVRTZ6PgwA6U3iVaPIYuMwAAaOpBFBoUQIEBHcRyEUxskSEC5nSo7hYfQXERIWIZX3QAAGgg1+FSnRQTRrHhwWK5EF5ECIiAuTjiCIi6J0RQTFiQWC6qgDEjAABIchwZoqSYUIpTAqJqy+8g+xkDAJMNde2eEKlc+aDLDAAAmmaIEmPCqNYx2qgQJTNkiIC5kCWztI4RSkCEkhkAQA1OlVXTwZOlpskQJUY3ZIiKUDJDQATM2XIvSmYyQ4TRHQAAFbj+X+tp2ku/UHZRQ8u60TVEUmtZiNZ7BETAPNhsNsWlGiUzAIDax5c92cWixHQgt9QUXWacIYKougH4EAHTkF9WTaVVtdShA1G3juEuJTM+mAEAQHvh4whbejhnWMyRIbIfJwtQMkNABMxXLkuOCaPQoEClZMYHMTYeAwCA9pJXWtVEg2MqDVEFusyQIQKmIcPRYZaWEKGM7oDpGABA7WGoRs4QsUu1bDThLrP4SIeGqNz+mJXxa0D05ptv0tChQykmJkbcxo8fTz/++KPy/KRJk6hDhw4ut9tvv93lNTIyMujCCy+kiIgISkxMpAcffJBqa119Z1asWEEjR46k0NBQ6tOnD3344Yc++xuBHwTVHSPFPX9e0GkGAFCDk84BkYEzRPLvYJdq9mqDhkgnPkSpqak0b9486tu3r9B4/Pvf/6ZLLrmENm/eTIMGDRLr3HrrrfTkk08q/4cDH0ldXZ0Ihrp06UJr1qyhrKwsuu666yg4OJj+/ve/i3UOHz4s1uFAav78+bR06VK65ZZbKDk5maZNm+aHvxpo3XLfvVPDZ4S/8Nwqi04zAIBaAZHzYFSjIbed9UN80QhHf50ERNOnT3f5+emnnxZZo3Xr1ikBEQdAHPA0x+LFi2nXrl30888/U1JSEg0fPpyeeuopeuihh+jxxx+nkJAQeuutt6hnz570wgsviP8zYMAAWrVqFb300ksIiEzGkfwylwwRo2SIkA4GAHhBXml1s+UzoyG3nfVDDJyqdagh4mzPp59+SmVlZaJ0JuGsTqdOnWjw4ME0d+5cKi+3ZwGYtWvX0pAhQ0QwJOGsT3FxMe3cuVNZZ8qUKS6/i9fhx1uiqqpKvIbzDeifhpZ7pwwRzBkBABpkiIzaueqcIWJkl1lZdR1V11q7+cTvozu2b98uAqDKykqKioqir776igYOHCieu/rqq6l79+6UkpJC27ZtE5mfvXv30pdffimez87OdgmGGPkzP9faOhzkVFRUUHh4eJNteuaZZ+iJJ57Q7G8G6sPt9vIKToqqGZgzAgDU4KRTl1lVbT0VV9YqGWgjZog6OzJE0WHBwqrEZrNbC8jHrYjfA6L+/fvTli1bqKioiL744gu6/vrraeXKlSIouu2225T1OBPEup/JkyfTwYMHqXfv3pptE2eiZs+erfzMwVO3bt00+31APf1QfEQwxYQ1HKQgqgYAqJ0hYnKLKw0ZEDXOEHEnLh8ziypqROu9lQMiv5fMWOfDnV+jRo0SmZlhw4bRK6+80uy6Y8eOFfcHDhwQ96wtysnJcVlH/ix1Ry2tw11tzWWHGO5Gk51v8gb0zVGpH0po0A8xCIgAAGoGRAEdjO1FJP8OnnQvkWWzQotrLf0eEDWmvr5eaHiagzNJDGeKGC61ccktNzdXWWfJkiUigJFlN16HO8uc4XWcdUrA+BxtRj/EyGxRcYWrFQMAALhLXb2NTpXZz0t9EqMM7UUkM0SJ0fYMkauwuoasjF9LZlyaOv/88yktLY1KSkro448/Fp5BP/30kyiL8c8XXHABJSQkCA3RfffdR2eeeabwLmKmTp0qAp+ZM2fSc889J/RCjzzyCM2aNUtkeRhut3/ttddozpw5dNNNN9GyZcvos88+o++//96ffzrQzIPINSBChggA4C1s3VFvY28zogHJMbQvp9SwGaLcZjJEscqA1xqyMn4NiDizw75B7B8UGxsrAh0Ohs4991w6duyYaKd/+eWXRecZa3guv/xyEfBIAgMDaeHChXTHHXeIjE9kZKTQIDn7FnHLPQc/HExxKY69j9577z203JvWpbr5klmxxb/oAADvy0wdI0IoOTbcsBkidqmWWaDmM0TVZGX8GhC9//77LT7HARCLq9uCu9B++OGHVtdhx2s2ewQWyBA1LpmF2z/iMGYEAHjbYcaCY5lZMaJbtYtLtePY6KwhKrL4haPuNEQAeAp7Z2QWVjQbEKFkBgDwljynVnXZnWVEt2qZ1UqMCRUu1RJoiOwgIAKqUlVbRzV1vjX3Ol5QLur7ESGB1DnKtWUUAREAQLUMUVSo4vBsRLdqqXtKciqXOWuICixeMkNABFSjtq6epr70C13wyq9UzxGKjzvM0jpGuFz1OHeZlVf7PlADAJgDWWrq1ChDZDS3avZOkhkiZ9i/jUHJDAAVr6JYy7M/t1SZK+ZLU0YOiBojnaoZCKsBAN4ERJwhksaF0q3aSOQoc8xcM0TwIbKDDBFQDWcPix2ZxT4XVPfo5NphJl1Yo0Pt4kGrX/0AALwMiKJDKSw4UCnFy4yLUZBC8MYZothw2XaPkhkAquAccOzMLPK5S3VzGSLXeWbGupoDAOiDPKcuM8aoOiIpqm6sIUKGyA4yRECTDNEuX2aIWnCplmDiPQBArbZ7xqidZi1liGSXWUllrdCCWhUEREA1nDU6OzOLfSI4ZPF2hgyIOjYtmTGxDr8NlMwAAO3pnJUXe50cXawyoDCaW3WOzBA5AjqJ85DaYgtn0hEQAdVwrj+z1X1WUaVPvuDsQxQU0IFS4ly/5E3nmUFDBADwjPxS+3GNjzEykyJFyUZyq3YO7GTJTxIUGKBoLa3sVo2ACKhG4wwMZ4m05kiePTuUGh8uvtTNAS8iAIC3+iHODgU4Rt0b0a1abmtIUIBLRkgSKyfeW/jCEQERUI3Gk5J9IaxuaYaZM5hnBgBQo8NMYsQMkfNQ18Z+bS7jO8oREAGgWoaoW0f78MMdJ4r9NuW++S4z637RAQBemjJG2VvTnTNERtIQKaaMjTrMJHFovUeGCKgfEE3o3Unc7/JBhqitDjMGJTMAgJoZIiO6VcuOOBnMtVgyK7fuhSNKZkA15BdpfO8EcZ9ZVEkFZdV+c6mWICACAKjVcu+8bCS3alkyazlDFCzuERABoGKGiAXOPRwZGy2F1XxlJkeENOdSLYlxtN0XVxjjwAUA0KEpo9PgaCO6Vee04EHURENUgQwRAF4j2zXZBn5QSqxY3qFh2YyvZNhIjEGGCACgbcnMNbNiNLdqKQBvU0NUjrZ7ALyirt5GJVX24ISvnAamxGieIZL6Ia6J8xVbS6BkBgBQU1RtRLdq2Xaf1EaGqAAaIgC8o6SyhqS2kAOQQUpAVKT5DLOWHKobGzPyNrKzNQAAeCOqds4QGaXTrM0MUYQc8IqSGQBeIevOESGBwvhLlswO55VRmSNzpJWgurUOM+e2e46FSquhIwIAuEd5dS2VVdc1HxDFGMeLiF2qZeanrQxREUpmAHiH7EyQnQp88OAvHmeNdmdpUzY74mZAxOW00CB7Q6WVTccAAJ6RV2LX04QFB1CUY7SFxEhu1TLL1ZJLtUuXWYV1j5FouweqZohkNoaRWSKtdETuuFRLYM4IAPCUk6WVytiOxu7ORnKrVjrMopt3qXb2ISqqsK60AAERUAV5VSHTrsxgjXVE7rhUSyCsBgCopR8ymlv1SUU/1Hy5zPkYabOx3tKa0gIEREDVDJFzOnagbL3XYIQH1/Zlu2tbJTMmJkx6EVk3HQwA8IyTjkn3zh5EzWWI9O5WLYM22RnXHKFBgUIDyhRWWLP1HgERUAUpxJNeFozsNNufW0LVtfWq7ukMR8s9B2CyO6I1Gga8WvPKBwCgboZIGhxW1ujfrVpaA7SWIWKs7laNgAiomyFyKpmxYzUHIjV1NtqXU6JNucyN7JDYLscX3courAAA9QIiI7lVK2M7WskQMbEWb71HQARUQV5ROJfMWLwns0S7VBZWuzPDrDlRNQIiAIDnpozNZ1aM4lbdMNg1zM0MUTVZEQREQDMNESMDIrVHeBx1dJh5miEqrrTmlQ8AQJ3BrkZ0q5aBXZslswhrXzgiIAKadZlp2Xrf0GHWdss9g5IZAMBT8lopmZkyQxQBDREAXiMNDxtniAZ3tWeI2JyR5535S0Mkx3dY9coHAOAZ3DmmZIhaKpkZIEPk7FLdVoYoVhnwas3jJDJEQBVkoOHcZcb07BRF4cGBVF5dJ8Z4qEFNXT2dKKwQy93dMGV0MWZEQAQAcAPuHJPdsW1miHTsRaS4VAcGNMngt5ghqoCGCIB2I79AjTNEgQEdKD05WlWDxszCCpFt4nEcbV3xSFAyAwC0J5CIDg0SHWXNIUtQenarluW8zq24VEvilXlmyBAB0C4qa+qEF0fjtnuJ2p1mR506zAICWv+CS2LC7caMRfAhAgC4QV4bgmqjuFVLS4CWhro2WzKrQEAEQLuQZSiOTfhqqjGDVRZWH3WYMrpbLmPQZQYAaFfLfSsBkRHcqhUPIse2ulMyK0DbPQDeD3ZtLmMjO8249V6Ng8ZRhxbJXUG1c0DEmgDOaAEAQHtNGY3kVp3jQYYoDiUzAFRquW+kH5L06xJFQQEdROdCZlGlihki9wOiyJAgkcFi0GkGAGiLtjrMGNYWyTmJcoCqbifdt9Fy79wUw8d0vWa8tARdZkCzlnvnoYF9EqPE8s4TRT53qWY4c4VOMwCAmhkiV3PGKp2XzNzPENXV26i0Sp8ZLy1BQARUyxDJOTjNMbirLJt5pyPiq5YGl2r3NURi+zC+AwDgqai6lQyRc9lMr15EUlTtToYoLDhQdO9a1YsIARHQbGxH851mRV5f7XC9ntv5u8aFe/R/Yc4IAFA9Q6QIq/WdIXJHQ2T18R0eBUS7d++mxx57jM455xzq3bs3JScn09ChQ+n666+njz/+mKqqPPtAvPnmm+L/x8TEiNv48ePpxx9/VJ6vrKykWbNmUUJCAkVFRdHll19OOTk5Lq+RkZFBF154IUVERFBiYiI9+OCDVFvrmupbsWIFjRw5kkJDQ6lPnz704YcferSdoHWKHB0JLWmI1BzhIVvuU+LCKMRxJeMu6DQDAKg12NUIbtXcRHKqrNolcHNbR1SOgKhZNm3aRFOmTKERI0bQqlWraOzYsXTvvffSU089Rddee60oYzz88MOUkpJCzz77rNuBUWpqKs2bN482btxIGzZsEIHWJZdcQjt37hTP33ffffTdd9/R559/TitXrqTMzEy67LLLlP9fV1cngqHq6mpas2YN/fvf/xbBzqOPPqqsc/jwYbHO2WefTVu2bBHbfcstt9BPP/3k1jYCdTJEAx0ZoqyiSsp3pKLbw9H8Mo9mmDVbMrPgFx0A4D719TbKdwQSbWWI9DzPTArD3XGplsRa2K26qWlMM3BmhjMvX3zxBcXFxbW43tq1a+mVV16hF154gf7yl7+0+brTp093+fnpp58WWaN169aJYOn9998XmScOlJgPPviABgwYIJ4fN24cLV68mHbt2kU///wzJSUl0fDhw0WQ9tBDD9Hjjz9OISEh9NZbb1HPnj3FNjH8/zmoe+mll2jatGnNbhcHdM5BXXGxuoNJrTLY1Zmo0CDq2SlSjO/gLNGZ/Tq363dlODrM0jzoMJPAnBEA4A7swyNnLyZEtayNdHGr1mGGSGat3HGplshMPzJELbBv3z7685//3GowxHDJ69NPPxXBk6dwtof/b1lZmXgdzhrV1NSIzJQkPT2d0tLSRODF8P2QIUNEMCThIIcDGJll4nWcX0OuI1+jOZ555hmKjY1Vbt26dfP477GqD1FryCyRN2Wzhin37QmIHPPMKpEhAgC0nVnpGBlCwYEBboqq9ZchkjPW5Da6Qxw0RK0THNz6ia6wsNCj9Z3Zvn270Aexvuf222+nr776igYOHEjZ2dkiw9M4COPgh59j+N45GJLPy+daW4eDpooK+4DQxsydO5eKioqU27Fjx9z+e6yIvJJoTUPkLKz2ZqaZUjLzsMOMQZcZAMAjQXUb+iFXUbX+3KrljDV39UNMnKNbuNCCbtUed5mxRmjBggXKz3/4wx+E6Llr1660detWjzegf//+Qtvz22+/0R133CEE2lwG8yccnEmht7wB7zREziM8vJlp1h5TRgm6zAAAno3taL1cpne36vZkiGJRMnMf1uTIEtKSJUvEjTvDzj///HaVyjgLxJ1fo0aNEqWqYcOGCR1Sly5dhFi6cfaJu8z4OYbvG3edyZ/bWoeDnPBwz9q2QesBkbyyaCtDdCivrF2mX/x7ZDbKE1PGJl1mFmwnBQBokyHSs1t1w9gOTzJEwZYd8OpxhohLUDIgWrhwocgQTZ06lebMmUO///671xtUX18vBM0cIHHpbenSpcpze/fuFW32rDFi+J5Lbrm5uco6HKBxsMNlN7mO82vIdeRrAO/gFLG7GaKEqFDq4vhi7s4qbrdDNbfBRjYzRLYtUDIDAKg16d4IbtU5bnopORPvuLC1YjeuxwFRfHy8oqlZtGiRIljmEyMLoz2BtTq//PILHTlyRAQ2/DN7Bl1zzTVCzHzzzTfT7Nmzafny5UJkfeONN4pAhjvMGA7EOPCZOXOmKNdxK/0jjzwivIu47MWwLunQoUMiYNuzZw+98cYb9Nlnn4mWfuA9nOmR3RjutHUO7hrT7hEeDQ7VnmeHGIzuAACoacqod7fq3PZkiMLRdu827AN09dVXU9++fSk/P1+UypjNmzeL0pdHb1ZuLl133XWUlZUlAiA2aeSg5txzzxXPc2t8QECAaPvnrBF3h3FAIwkMDBRZKtYecaAUGRkpNEhPPvmksg633H///fciAOJSHLfzv/feey223APPkCUstnvn1HFbDEyJpZ9357ZrhIfSYdbOgKjBmFFfdX7gXzZlFND7qw7TBYOT6bzBXYQLOrA2ssusLVNGvbtVezLHrLEPUYEFM0Qe1x04SOnRo4fIEj333HOiQ4zhoIZb8z2BfYZaIywsjF5//XVxa4nu3bvTDz/80OrrTJo0SQRsQH3cLZc17TQr9qkpo/M2clartq6egtpopwXW4IPVR+j7bVni1iMhgm47szddNrKrWwE+MCeeZog66zBD5OJS7ZGGKEQpmXHlx13/IssERDz2gnU4XC5j4fMDDzwgRmU4gxKU1QXVngVE+3NKqKq2jkKDAn2WIYp2CB+Zkspaio9su4MEmB8phOXE0JH8cvrLV9vpxSX76KaJPeiasd3dDvaBecgrdc+lWs8ZIpnlCg7sQPFuHp+dS2bVdfVUUVNHESGe6zWNSoC7M8zYMJF54oknlGUAPM0Q8UBWDp5q6220P6fUZy7VDBusRYYEWnZwIWgeeRX91rWj6NGLBlJKbJgQ1T63aC9NmLeMnvlht66u/IG21NQ1ZFbc6TLTq1u1MuU+OsyjLE9ESKAIoqzoVu1W6McjMVjQPHHiRJFCe/7555VSWWOc54gB8yO/MLGOgYBtwV9MzhKtPpBPO04U0eCudm+itqisqRNz0Jge7TBllHDgVlZdh4AIKMiTX2p8BE0d1IVmju9O323NpLdXHqK9OSX09i+H6F+rD9NlI1LptrN6Ue/OzR/7gDnId2SHWEsmO67cFVXrKUMkO9486TCTx2g+nvNFAR/fU+KsY0/jVkDEA1N5yj0LmHlnse9QUFDT/8rPISCyFp5miOTkew6IPNERHXNkh6JDgzxK/zbXaZZZVInxHUAZ4ikDok6OmVWcSbxsZCpdOqIrLd+bS2+tOETrj5yiBRuO0Wcbj9HUgUl0+1m9aURaPPaiifVDCZEhFOCmwF6WzDiTqBfdjSwFJ3lgyijhLL4IiCw24DXIXTdpnjPGcNcX64kSExO13jZgAOQXxl0NUXtHeEj9EJfLvDnYyNZ7lMyANJ9zuEY00ZTx5+yc9CRx23i0gN5aeZCW7Mqhn3bab2N7dqTbJ/WmSf066+IECPzjQdScW7UedGcyQ+SJoLqxjshqXkRB7TFOBEBS3M4MEbM7q0R4GLnT5uzNyA5nYM4InDlVZj9psNNwa0M8R3WPp3evG00HcktEKe3rLSfot8OnxC29S7TIGF00NBmdixbsMHN2q+ZgiDMzegiI5BwzT1rure5W7XHfMXeZ/etf/2ryOD/Gc86ARQe7epAh6tkpksKDA0UHw+E894TVGY6W+7R2ttw3nmdWXAEvItCgF2EXdXfokxhNz18xjH6ZczbdekZPIdLfk11C9y7YQmc9v4LWHMzDbjU4sjvLXUG1Xt2q5XYktiNDFOvQhFpNVO1xQPT2229Tenp6k8cHDRok5pwBa9EeDRFnhAYkR4tld3VE3A7NsE+MNyBDBJzJd+iHWC/iCcmx4fTwhQNpzf+bTA9M7Sf+/4nCCprzxTbdTTwH7R3s6llA1CCs1kenmeyM9C5DVE1Wol2zzJKTk5s83rlzZ2HOCKzaZeZZilh2l7kbEHnbci9BQASaC4g6ttOTil197zynL614cJJwaz9eUEH7PLSTAMYd7Nq8sLpKV38HNEQaBkQ82HX16tVNHufHUlJSPH05YMEMkbOwmlvv24J1RscLpIbIy5JZuF02V1xprVQwaJ5TSsnMO5PO6LBgmtCnk1j+eXcOdrcZSmYeZlb05FbNLtUy2PcqQ1RureOkxwHRrbfeSvfeey998MEHdPToUXFj/RA7VfNzwKpO1Z6dUKSwmjNEbZUYMgsrqKbORiGBAdSlHfXwZueZWUwsCFoXVbc3Q+TM5AH2zlsERMYmrx2iar25Vee5uFR7/tmOc/wfq5XMPO4ye/DBB8VQV55bVl1drcwce+ihh8S0emAtR1eeC9aeDFHfpCjxZeWAirUXbIrXVrkstWO414M3UTIDzuQpGiLPr6IbMzk9iR6mHbTlWKEoV3h6QgXG7TJz0RDpIEMks1Rc9nPXS8kZZIjchP02uJvs5MmTtG7dOtq6dSudOnUKhowWxDnLwi2nnsAzzPom2oXVO060riM64ugw88ahurEPETJEQM2SGdMlNoyGdI0lTngu35OLHWxA2BG/xHGR5+6k+ybjO3SQIVKm3Lczox7n6DKzml9bu8d98+iOMWPG0ODBgyk0FFdCVkR6VPDQ1PZMjpc6ol1tGDRmSFPGjt4JqhlkiIAz0qVajZIZM2VAkrhH2czY2aGQoACPL/Iau1XrY45Z+87NcQ4NUUG5tUpmbp3Fbr/9djp+/LhbL7hgwQKaP3++t9sFTCyobupYXazplPtmNUSVtX4/aAHjd5m1pCP6dX+eyDYA43oQeeo+3titWg8ZovZ0mMnuSfm3WOlz7FYIzC317DM0YcIEmj59Oo0ePVp0lLF2qKCggHbt2kWrVq0S4z348XfeeUf7LQd+p6gdpoztab1Xy6Xa2ZiRO9d4yGtUqMcyOmCiOWbyCtjT8khrQX5ybJgYRLz2YD6dnY4RR1bQD+nNrdobDyI5M5L1mnyc5Atf/tusgFsZoqeeeor27dsnAqI33niDxo0bR2lpaWKeGc85u+666+jQoUMiEGJd0dChQ7XfcmD4DNGA5Bjii7Ds4kqlK6IxnMU56tAQedtyz4QFB4huNSvWx4Er/P7zAZ9pTydOc3BWQWaJlqD93rimjO0MkKVmx99eRN5miDqIiffWa713W/iRlJREDz/8MG3fvp3y8vJo06ZNwnto7969Ikv0xRdf0Hnnnaft1gJdUei4upYCPE+JDA2ino4gp6UsUV5pNZVX14nAKTU+nLyFv+jKgFcLfdFBy+Uy1sCxZkQtJjt0RMt256Isa6EMkfNkeX+7VcuATHojtYc4JSCyjo6oXfWC+Ph4cQPWpsgxD0wGGO1hYEoMHcorE5Pvz+rXucnzGafs2aGU2HDRmaYGbM7IGSmYM1qbU+0c29EW43slUERIoMh8cqAvS8PAnJPunUnUiVs1l+ychd7e6IgKLZRJV++yCFgOadrVXg2ROzqioyp2mEnQaQaYfMfJz93Bru7Ceosz+tpdq5fsgmu1lTJEDV5EVX71h+PMunPGypsMUZGFMukIiIDfNEQunWYtjPBQs8OssbAaGiJro3aHWXPt90v3ICCywqT7Jq33fiyZyaAuKKB9LtVWdqtGQAS87zLzKiCKVabZlzQzX0xNQbUE4zuAliUzhrvLWPfGpqNZRRXY4YbLELXvM6EHt2rFlDG6fS7VEoiqAfBxhoivzlNi7VdVu7NKNG25lyAgAlqYMjrDXUoj0+w6y6W74VptBLijVdEQRbVPe6MHt2plbIeXcx/joCFyj9raWvr555/p7bffppIS+0ksMzOTSktLvXoDgLGQYjspvmsvAx1ZouYm36vpUt144j1KZtYmTyMNkQTDXo0Fz2VkI0KmU3szRA7tkT/dqpWWey9n6cVBQ9Q2PN1+yJAhdMkll9CsWbPETDOG55s98MADXr0BwFhIfwpvDchacqzmA5TUeWiSIfKzmywwb8nMWUe05mA+lVfjs2aUchmbtUaEBHnVZcaBlZyJ5rexHV4Iqpl4x/cCGqJWuOeee4RTNXsPhYc3+MJceumltHTp0tb+KzARfPUjB6RK8Z33AVFRs/ohLmlEO4TQaoAuM6B1yYzpmxglMpvVtfVilAcwiilj+z8P4SF2t2p/6ohkh5s3LfcMNERu8Ouvv9IjjzxCISGuH5oePXrQiRMn3HkJYAIqauqouq5elQyRbL0/kFvqMjdHiw4zBl1mQOsus8au1T+j/V73yFb19rbc68WtWna4eZshipNdZmi7b5n6+nqqq2s67I2Hv0ZHR3v1BgDjIPU33NoZGeKdYSLPfoqPCKbaehvtyylpGhCpqB9iIKoGPMdMZojUmmPWHOdK1+o9ucqYEKBPpJmhtwGRv92qZYZIBmZea4gq4EPUIlOnTqWXX37Z5SqIxdSPPfYYXXDBBV69AcA4yKsG7kTwdCp0Y/j/y/Z7Zx2RdKlOU7Hlnomx4BddsmhHFn256ThZHXYpV+aYRWo3hHNMz45iNAhno7YcK9Ts9wD/exDpxa1aBmLtHezauMuMtZxc9rUCHvsQvfDCC2KG2cCBA6myspKuvvpqpVzGwmpgDWQw4c3YDmcGdW2qI9I6Q2S1gOjYqXK6Y/4mmv3ZVjGJ3cooc8xCg1QbCdMcwYEBNKm/vWy2FMNeTe1SrQe3anaplp/t9g52lbBuU17rWuVY6XFAlJqaSlu3bqW//OUvdN9999GIESNo3rx5tHnzZkpMtH/xgYUyRGoFRErrfXGTgKhHJ5U1RI5trqqtd9EsmZ1P1meQ7AT++w+7RdmIrC6o9kJA6y5TpI4IAZGpJ903yRD5oWTGVhL8HWcpQ0cvm10CAzo46S2t4Vbdrt7CoKAguvbaa9XfGmAYZIeZt4Lqxp1me7KLRSmjtr6eMh0Ov2kd1S2ZcVaAr3z4wMGlE549ZXaqautowe/HxDL/7dtPFNG3WzNpxoiuZEXyS7VtuXdmUr9EcXLZl1MqsnTdVM54An2JqhUNkR+6zJQp9166VDuXzTg7ZBVhtccB0bffftuiDiQsLIz69OlDPXv2VGPbgCEGu6pzQumZECkmhJdX19Ghk6Xiy8wBCz/mTRtsc/Brc1DEPkQc2MkrOjOzaEe2SKXzwfrq07rTSz/vo+d/2kvnDe5iiYCwMfll9hNHx0jtBNUSNi4d0yOe1h06JbJEN07A8dHMJTN/ulU3eBCpc0yLCw+moxbqNPM4IJoxY4YIfhq7cMrH+H7ixIn09ddfU3y83boemA81xnY0DlIGJsfQhqMFQlgtX5d9XLwVbbd0kuKAqKjCGoZ589dliPs/jkmj287sJcpnJwor6MM1R+j2s3qT1TjlwwyRNGlEQKRfuHysjO3wVkPUyK1ai+NXS+Q4zTFTg1hlwKs1AiKPNURLliyhMWPGiPuioiJx4+WxY8fSwoUL6ZdffqH8/Hy4VpsctVyqmyub8QiPhqGu2pQXrNR6vze7hNYfOSXKNledlibM4x6Y1l889/ryA4qexpIeRD7QEDGTHe33vx06Jcq0QF/wCZ9tP5gEL7OG/nSrPunIEMmynbfEOY6TheXWOEa0y6n6xRdfpMmTJwvfIb7x8vPPP08PPvggTZgwQbTlc5AEzIvaGSLGufX+iBRUq9xyb0Vzxvm/HVU8cbo4BuleOqIrDUiOoZLKWvrn0v1kNbQe29GYnp0iqXfnSHHSXbnXPu4I6AeZHWLNTEiQx6dFF/iCg60W/KEjkhoitWQAcRHWOU4yHr/zBw8epJgY+5W8M/zYoUOHxHLfvn0pLw9W9WZGfkHkF0bt1nuZIUrTOkNk8qv1sqpa+nKT3UH+2nHdlcc5W/TwBQPE8n/XHaXDefb9bTUNUYKPMkTMlIH2LBHa73WsH1LJpFPREfm49V56EKmfIaohK+BxQDRq1CiRCZJDXRlenjNnjiilMfv376du3bqpu6XA9BmivonRFBzYQWh7NhwpEI91V7nDrIkXkcm/6N9syRTGapyhOL13gstzE/t2orP6dRZZi+cW7SErdpn5QlTdeNjr8r0nqdYx9gaYS1AtkQGJr1vv1c4QxUJD1DrvvfceHT58WPgRcUcZ33j5yJEj4jmGnat53llbPPPMMyKI4rIbexixYHvv3r0u60yaNEmI0pxvt99+u8s6GRkZdOGFF1JERIR4HQ7Yamtda7crVqygkSNHUmhoqNjmDz/80O0PBWjdqVotOFXdL8k+/kXW3rXSEFnBrZoFnZz9Ya4Zm9ZsG+5fLhhA/PCPO7Jp49FTZBV8XTJjRqbFixE1/Jnj5gFg3oDIX27VsrPN2zlmEv68WklD5HGXWXp6Ou3atYsWL15M+/btE4/179+fzj33XAoIsCecOLBxh5UrV9KsWbNEUMQBDJs98mgQfv3IyIbMwK233kpPPvmk8jMHPhKeq8bBUJcuXWjNmjWUlZVF1113HQUHB9Pf//53sQ4HcLwOB1Lz58+npUuX0i233ELJyck0bdo0T3cBcPqCqJkhksJqOb6DzcV4zpkWWKFktvlYIe3KKqbQoAD6v1Gpza7Tv0s0XTGqGy3YcIye/n43/e+O033aFeOvQFHrSffNwWXKs9MTRQmTy2bjerlm7ID/x3aoNdfOH27VnHWUpWBoiHwQENXU1FB4eDht2bKFzjvvPHHzhkWLFrn8zFkbzvBs3LiRzjzzTJcAiAOe5uDAjAOon3/+mZKSkmj48OH01FNP0UMPPUSPP/44hYSE0FtvvSW8kXjsCDNgwABatWoVvfTSS80GRFVVVeImKS5ucE8GJIwTZQYnNlzdEwpPvv9sg33WFhvYBQV6J3C0coZIZocuGprSql/U7Kn9hEnjpoxCkSm6YEgymZniilqlo8iXAZEsm3FA9PPuXHr4woE+/d2gZfK0yhD5sGTGxpLshsOBt1qZz1jH8R0aombgrEtaWlqz0+7VgFv4mY4dO7o8zlmdTp060eDBg2nu3LlUXm7vQGLWrl1LQ4YMEcGQhIMcDmJ27typrDNlyhSX1+R1+PGWSnmxsbHKDXooV0oqa5QREFpkiCTsQaQVMY4uELMGRAVl1bRwW5ZYvnZcWpsC0FvP7CWWn120x/SDHOVVdFRokM9NKc/s15lCAgOEiP3gyVKf/m6g/WDXxhqikz7MELHvkfwb1HCpdpZEWKVk5vHl98MPPyxKW6dOqas3qK+vp3vvvVe07XPgI+Hhsf/9739p+fLlIhj66KOPXMaGZGdnuwRDjPyZn2ttHQ6aKirs4yGc4d8jPZb4duyYfeQBIJcggl2kvW1RbUx6lxhloKBW+iFXHyJzGjN+sfG4CGw4wBzeLa7N9f90Zi9RLuD5cTKzZFb8US6TcBA2tpf9gu/nXTk+//3AxxoiH2aIZECkVoeZc5cZN7pwZcDseKwheu211+jAgQOUkpJC3bt3d9H6MJs2bWrXhrCWaMeOHaKU5cxtt92mLHMmiHU/7HvE7f+9e2vjsMvCa74B3wx2dSYyNEh0RB06WaZphsjME+/ZdVd6D3GrvTuaIN7v90/tR3O/3E7/XLafLh+Vqnr2T3emjH4IiJhzBybRr/vzaOnuXPqTBV3CzTzYtUmXmQ/dqqWgurOKo4hinY4BbGIb76fvjK5Hd6jNnXfeqbhcc8daa7AjNsNBGQdErC1av369yzo5OfYrL6k74nv5mPM67J3EmijgGTKIkDoctZkxvCu9++shOqNvZ9KKGBM7Va86kCeMLXle2yXDU9z+f1eMSqV/rTpM+3NL6Y3lB2iuw6fIrC33as/Ic5dz0hPp0W920oajp0Rp0+wnGb3DYuRTjpKQ2hki6VYtjWC1JFeDDFFQYIA4jvDfUFBu/s+qxwHRY489ptov58j5rrvuoq+++kq0xbszFJYF3Qxnipjx48fT008/Tbm5uUKQzbBLNgc7AwcOVNb54YcfXF6H1+HHgefIuTZqttw7c/fkvnTn2X1Uq4O3duXDX3ROBbMQ0SzIktdlI7tSREiQRwc/bsO/8cPf6YPVR0R2yYyT2U8pg139c3BPjY+g9C7RtCe7hJbvzaXLRrZ+EQi0L6GyJpIPAWp9JqRbNTvBc6Dik4BImWMWpvrcx5KqWkvMM9OmhceDMhnrgz7++GPhRcRaH75JXQ+XxbhjjLvO2Ofo22+/FS313IE2dOhQsQ636XPgM3PmTNq6dSv99NNPwgOJX1uWvbjdnl202Txyz5499MYbb9Bnn31G9913nz//fMOihSljY7QMhhjnAxSLxM1CVlGFmKjOXOPkTO0uk/p3FgaO1XX19I/Frp5g5iuZ+a8szmUzhstmwL/IQCIhKlTVCyNfu1VroSFyGd9hchPbdgVE3GH2j3/8g0477TRRiuKOMOebJ7z55ptCtMzmi5zxkbcFCxaI57llntvpOehh/6P777+fLr/8cvruu++U1wgMDBTlNr7njA8LrjlocvYt4szT999/L7JCw4YNE+33bCIJD6L2UeRIL8ep3HLvS1gMHu7oMDKTjuiT9ceItY9je3ZUTC49gbUOnCViyQO7XG87Xkhmwx+mjC0Ne12576Tpu/qs1mHWZOq9j4TVMrCTgZhaxMnW+wrzd5p5XDJ74oknRDDBwQlnYrjrjLM3X3/9NT366KMel8xag9vd2byxLVjc3bgk1hgOujZv3uzR9oE2MkQalcx8BWe4KmrqTNNpVlNXT5+uz2gyt6w9XlCXDu9KX24+IcwaP71tnKnMGqWGyJdzzBoztGus0KuwmPe3w/ma6uWAm4JqlfRDEhmY+MqtWv4etXRQEnmct4IXkccZIvYEevfdd0VAFBQURFdddZUIkDgYWrdunTZbCXSF/GIYvQvJbJ1m3MbNV4ncKTNtUPNGpu5y/7T+wuH6t8OnhImgmfB3l5ksCU9Ot2se0X6vj0n3WmWIfFEyc3apVj9DFCzuERA1A2t8uP2diYqKUswUL7roIlGWAubHFxoiXxATbi5zxv86Wu2vHJPqtT9U17hwummivcnhmR93i+yT2UTVCX7UEDkPe+WAs61sOTCOB5EkUWaIfFAy08KluomGyCTHydbw+KjJbfE8L4zhtncencH8/vvv8O6xCFp3mfkKM80zO3SylFYfyBfan6tOa92Z2l3umNRbZFHYE+rT34+Zb46ZH0tmzIQ+nUQW7kRhheg4A+YKiHzpVp1bor5LdRMNkQXcqj0OiC699FIxHJXhlvm//vWv1LdvXyFkvummm7TYRqAzik2TITLPlc/83+zaoXP6J4q2brU68e6Z3Fcsv/LzPlN047Hjbk2dze+iatmaPbFPJ7HMw16Bv00Z1f08+NKtWuqH1Jpy3+z4DhMcJ1UXVc+bN09ZvvLKK8VsM54JxkHR9OnT1d4+oGunauN2mTm33hs9IKqsqROjOrwVUzfH1WPT6MM1R8TsrbdXHqIHpvUnIyOzQ5EhgT6fY9YcUwYm0dI9uaJsduc59uAT+ElDpFGGiDVEWrtVy5Z7tT2IGDkYGhoiN+BW99mzZyMYshBm0RA1zDMzdkD03dZM8Z6kxoeL4aFqEhwYQA+dly6W31t1SPgcmcKU0c/lMokUVm85VqiUPYB/MkRSBK0WMjjhTlY2NvSJKaOGGaIigx8nNckQMfv37xfDVtkdmoeyOuNp6z0wXjaCv+Bmabs3wxf9v45yGWdztHDcnjYoicb0iKffjxTQC4v30T+uGEZGb7n3pyljY+HtsNRY2nq8iJbvyaUrx6ij/wLuH8+4jMp0jlI3u+JLt2plbIcWGaJw60y89zgg4pb7O+64gzp16iSMGZ3TgLyMgMjcyGwKv+0848bImEFDtP14EW09VkjBgR3oD6O7afI7pFnjpW+sof9tOk43TehJA1NiyMgls046msnEJo0cEC3ZhYDIX+WykMAApetUTbgFvqSyVJTN+iR6bpSqhwxRrFOGiAdHaz1FwFCi6r/97W9idhi33/NcMTY7lLf2TroHxiyXGf2L0dBlVmv4uWXnD05WbVJ3c4xIi6eLhiaL1l5uwzcqevAgaqn9ftWBkyJjAXwHt6tLQbUWGh9fuVVrNbbD+TjJDvhal/4MFxAVFBTQFVdcoc3WAOO03BtcP2QGDREHp99sPaGJmLo55kxLF5moX/fniZEThi6Z6URDxAxIjqaU2DAxGX31gTx/b46l0Krl3tfzzLQa7MqEBgVSRIhjzJHJ3ao9Dog4GJLeQ8B6yC+E0QXVZjBm/HLTcXES7ZcUJTQ+WpOWEEHXj+8hlv/+/W6q40tGw5oy6icg4syEnG1mNldwqwdESoZIw4CIXapl6U+LkpnzBXCByXVEbhVN//nPfyrLffr0Ed5DPKaDHauDg11PjHfffbf6Wwl0lyGKdbRimiVDpHVbrNrw9krvoZnjuvts2+88pw99tuEY7c0pocU7s+n8IclkxJKZv12qm2u//2jdUVq2J4fq6wcbvhxtFDQPiHzgVs2f6QaXam3+jtiIEMosqjS9F5FbAdFLL73k8jOP7OChq40Hr/JBGQGRuTFLy73z31Bbb6Py6jqKNJBIfN2hU3Qgt1SksmeM6Oqz38ueJFMHdRG+R/z7jYYeS2bMuF4dhTcSZxJ2ZBbR0NQ4f2+SJThZ2uDwrGWGSEu3aqkfYh2UFl2mVuo0c+sMcPjwYe23BBiCIscXwgwaovDgQAoK6CACIg70jBQQybllHAxFa9jO2xzyalqm6Y3YZaankpnUabCH1I87skXZDAGRb8grcYiqNdYQaZkhkvoktYe6WtGLyLsJkMBymClDxBlNI84zYwO/n3Zki+Vrx2ovpm6M7GaTHTqGnGOms4CIUXREuzDGw1ec1GjSfUtu1Voggy21jSWbHd8BUbUrl19+OT377LONHiV67rnn0H1mAcwy2LWJOaOBvuif/X5MZLVGpsX5xQ9IznySJxOjUFpVS9V19brUEDFn9+9MXPHYlVVMz/ywm2oc2wqMLKrW3q26YY6ZdhmiWGXAq3GOkz7JEP3yyy90wQUXNHn8/PPPF88Ba2SIpKmh0Yk2mDkjd3Z9sv6Yz1rtm0NeTRutZCb1Q6y7YhdhvZEQFUp3nt1HLL/9yyG68u21dKLQ2KNSrB4QSbdqZzdptTnpywxRhbGywpoHRKWlpRQS0jTdzN1mxcXFam0X0P1gV3MEREYzZ+TxDnySjI8Ipgv81OEl9RZ5jpOJUdCjKWNjZk/tT29eM1KcRDdlFNIFr/xKS1BC04SyqlplDJGWpqYyUNHKiyjHBxqieKkhQobIFW61X7BgQZMd9umnn9LAgQM1e0OAPig2kYaIiXFcvRklQyTF1FeM7ua3ae3y5MFBZFWtcZyV9SqobgxbGfxw9xlixhl/Lm/9zwZ6auEuqq5FCU2L7BBnDLVsqNBaWJ3rgwxRrCyZGeQ42V48/hSwB9Fll11GBw8epHPOOUc8tnTpUvrkk0/o888/12IbgS41RPo+qZjRrTojv1xxiL76NP8NAeXsILf3cvmOy1ApceFkKFNGDbMBatGtYwR9fvvp9OyiPfT+qsPituHIKXrt6pHiOaCioFrDQMIsGaI4RVSNkpkL06dPp6+//poOHDhAf/7zn+n++++n48eP088//0wzZszQ7A0B/oe7JMzUZWa0iffz1x8VBmxn9O1EPTpF+m072DRQZlmMpCOSXXF6Lpk5ExIUQH+9aCC9e91o8TnlAbAX/PNXWrQjy9+bZi79kMYBspIh0iAgYpfqfOlS7QMNUZEBjpM+b7u/8MILafXq1VRWVkZ5eXm0bNkyOuuss9TfOqC7Lh05rsEsXWYxBskQcWnq8w3H/Sqmbr713jgBkVFKZo05d2ASfX/3RNFVWFJZS7f/dxM99s0OQ5UrrSiolsjuL1naUlsXx4dk7k7UMvMZ59RlppV9gCEDomPHjomMkGT9+vV077330jvvvKP2tgGdCqr5ytVf+hW1MYoP0S/78sQJPTk2jCanJ/p7c5yE1cZJoevZg6gtUuMjaMGfxtOfzuolfv732qN0+Ztr6Ehemb83zbDIYF5LQbXWJTP5mvw3aOVS7XwBzHYfZdXmDcQ9DoiuvvpqWr58uVjOzs6mKVOmiKDo4YcfpieffFKLbQQ6QaZLzdJhZqSS2aGT9jEZp/XsSEGB/vdTNaIXkTLHzAAaouYIDgyguecPoA9uGCO6fnacKKaLXl1F323N9PemGRJfZYi0FFXLsR1a6ocYvgAODQowvY7I4yPrjh076LTTThPLn332meg6W7NmDc2fP58+/PBDLbYR6C0gMkm5jIkJM0ZAlOnwo+mqEwGzEb2IpNbCaCWzxpydnkg/3HMGjekRL8rYd32ymf7y1XaqdLSQA52VzJwyRGqXm3JLtNcPWcmt2uOAqKamhkJD7TufhdQXX3yxWE5PT6esLIj9zIzZBNWuXWb69iE6UWi/EtRLR5cRx3cYuWTWmOTYcPrk1nHCyLFDB6KPf8ugGa+vpoOOTCLw/9gOSaJjfIcWbtUyQ6SlS3VzOiKz4nFANGjQIHrrrbfo119/pSVLltB5550nHs/MzKSEhAQtthHoBPlFkJ4UZsAoJTPpWNw1XicBUXSIocwZ+crcCMaMnsCl0wem9af/3HSayHrtyS6h6a+uoq82N2g8QcvIz65Wg10lESFBTm7V6n5fpFBbzkzTklgLuFV7HBDxHLO3336bJk2aRFdddRUNGzZMPP7tt98qpTRgTsyYIYoJD1Ku3vRsfKe3kpnRusxYCCrf3wSH/sksnNG3M/14zxk0vlcClVfX0X0LttKcL7aiC62NANlXPkSuZTN1dUQywJIz07QkznHcN3OGyGNjRg6EuNWex3TEx8crj992220UEQHDMDMjrwzMpCGKdmiIZKeZ1h0n7YF1IjIY1V/JrMpQ+qHw4EBxxW42uGTy31vG0j+X7qd/LttPn204TgOTY+iGCT39vWm6hL9PNXU2lwYBLWHR88GTZaoLq3N8mCGKs4AXUbvaVQIDA12CIaZHjx6UmOj/dmCgHWYb28Fwq6pMZ+v1iy6zQ7zfozQcMdCegKignE8s+s2sScxWLmvps3zfuf3onsl9xc+/Hy3w9ybpXlDN36nQIO0tRLRqvfdphihCaojMWzJz6+g6cuRIMZ6Dg6ARI0ZQB1bxtcCmTZvU3D6gx8GuJsoQyU4zNrzTa0Ak9UN6yQ7JwIJtT9gUjsXKWrf9essph/jbbOWy5hjV3X6xuvNEkb83Rbf4slymlVs1m+TKDK1PNEThKJkJLrnkEqWzDOM5rIsZNUTy7+GgQ69u1ScK9KUfktkIDoq4y4yvtnUfEFkgQyQZlBIr7o/kl4sysLSWAE0zRL4olzkHXmq6VXMZ2Bcu1ZJ4mSHS6XHSZxmixx57rNllYNUuM3MdYPXeadYgqNZX0MFlMw6IjKAjypODXSP1pxFTGw76OHjmIH9XZjGN64Xu35Y9iHzznZIXDGqWzHJ85FLdRENkYlG1/y1vgWEwa4ZIdprpNkOks5Z7I3oRWalkxgxKiRH3O1A286sHkZZu1TLbJH2OfNZlVqH/77umGSLWDrWmG3Lm1KlT3m4T0L1TtblOKg3zzGp1nSHSk4bIudwgO7j0jJVKZszgrrG0eFcO7cws9vemWNqluiW3anfPp+5kiJJ8lOWKtYBTtVsB0csvv6ws5+fn09/+9jeaNm0ajR8/Xjy2du1a+umnn+ivf/2rdlsK/Ap3EnH7txkzRHovmUkNkf4CIuO03luhy8yZwV2RIWoNmdX0lYaosVu1Groun2eIIho0RGoFdYYMiK6//npl+fLLLxdDXO+8807lsbvvvptee+01Mcrjvvvu02ZLgV9xLifFONrUTTfPTIdXPrV19ZTtMHNL1VtAFG2gklmZb0+A/mawQ1jNozzKq2tN6b1kpAyRcKsODRLBEGeJ1AiIcnzYcu9cMmOD08qaegoP0d6uQPcaIs4EyXEdzvBjHBB5wjPPPENjxoyh6Oho4WHEHWx79+51WaeyspJmzZolxoJERUWJgCwnJ8dlnYyMDLrwwguFMSS/zoMPPki1ta7ljxUrVgj7AO6W69OnDwbReojsLOAvtR6mrWuRCuaOHL2RU2LvJAkJDNCdaaShMkSObexoAVG1NGrkkz1/dnZnlfh7c8jqAZFzJkctt+qTPs4QRYQEUnBgB1PriDw+s3Fg8s033zR5nB/zdJbZypUrRbCzbt06MReNB8dOnTqVysrKlHU44/Tdd9/R559/LtbnmWmXXXaZ8nxdXZ0Ihqqrq2nNmjX073//WwQ7jz76qLLO4cOHxTpnn302bdmyhe6991665ZZbRHAHPBRUm8yDSO8lM1kuS44LowAfdJJ4gsy2yJOLEeaYGX3SvScM6WrPEu3MhB9RY/+eU2W+D4iUTjOVvi++1hB16NBBmWNpVh2Rx3nUJ554QgQTnHEZO3aseOy3336jRYsW0bvvvuvRa/H/cYYDGc7wbNy4kc4880wqKiqi999/nz7++GM655xzxDoffPABDRgwQARR48aNo8WLF9OuXbtEdiopKYmGDx9OTz31FD300EP0+OOPU0hIiBhG27NnT3rhhRfEa/D/X7VqFb300ktCCwXapsikpowuJTMdBkSKoDpWX+UyI3WZ8XyvKsccM6toiJjBKTG0bE8uOs2aKZ8q/j0+zBhKYbWcUK/epHvf/Q1xEcEiI2zWgMjjDNENN9xAq1evppiYGPryyy/FjZc5wODnvIEDIKZjx47ingMjzhpNmTJFWSc9PZ3S0tKEkJvh+yFDhohgSMJBDs9a27lzp7KO82vIdeRrNKaqqkr8f+eb1TFryz0To3SZ6e9LrteWe+era77a5qtuvSL1Q2HBASLtbxUGOTJEO07g+OWMzGhycOwL/x4t3KpdXap9508Wp2TT9X0R1F7apbTjzND8+fNV3ZD6+npRypowYQINHjxYPJadnS0yPHFxcS7rcvDDz8l1nIMh+bx8rrV1ONCpqKig8PDwJtomzoSBBuT8mjhHytSUJTMdXvXocWyHRGZbOBYqKK/WncZJIk8cnA0wY2dMa633zL6cEqqsqaOwYOsEg+54EPn686qmW7WLS7UPs55xjgoBzzA0I7pRx7KWaMeOHfTpp5/6e1No7ty5Ilslb8eOHSOrU1RR65JNMaMxI3eA1Oss09EwtkNfLtVMcGAAxTsOkHoWVlvNg0iSEhsm3p/aepsIioD/BNVqu1X/sD1LCep82eQSa3INkS4CIm7hX7hwIS1fvpxSU1OVx7t06SLE0oWFhS7rc5cZPyfXadx1Jn9uax0u9TXODjHcicbPOd+sjuwqMKOGSGaIbDZ7UKTPsR0RpEcUHVGJflPoVvMgknA2bLCfy2aH88po49EC0hP+CogSVcoQLfg9gx7/bpdYvuq0NPIlcdKc0aQlswB/d39wMPTVV1/RsmXLhPDZmVGjRlFwcDAtXbpUeYzb8rnNXppC8v327dspNzdXWYc71jiIGThwoLKO82vIdeRrAGtriEKDAoW+RG/jO/j70eBSrb8MkVFa72WGyCpjO5ob9LrDD51m/Pm95t11dPmba2jV/jzSC3k+HtvRnIaI9017+HzDMfp/X24Xyzec3oPundKXfEmcjuUFhg+IuEz23//+V3SRsRcRa334xroeJjY2lm6++WaaPXu2yB6xyPrGG28UgQx3mDHcps+Bz8yZM2nr1q2ilf6RRx4Rr82ZHub222+nQ4cO0Zw5c2jPnj30xhtv0GeffQYTyfZ0mZkwINJrpxlvS1l1nW41RK7mjFW69yCyUst9Y8fqnX6YaXYgt5Qyi+zZkLlfbaMKx2fZshmiRm7VnvK/jcdpzv+2iUz29eO702PTB/pcExdn8vEdfg2I3nzzTaHRmTRpEiUnJyu3BQsWKOtwa/xFF10kDBm5FZ/LX9zZJgkMDBTlNr7nQOnaa6+l6667TrhpSzjz9P3334us0LBhw0T7/XvvvYeWew8wc4bIZZ6ZjgIiKahmvx+9CmIVLyI9B0RKyUyfom9fOFbvzi4R43d8yfojDXMtj52qoBeXuJruWi0gkm7V7dERfbX5OD3wxVYRDF07Lo0ev3iQXxoE4pTxHeYsmXncZcamifPmzRMlKC5TcXeYM5yJcRd30oZhYWH0+uuvi1tLdO/enX744YdWX4eDrs2bN7u9baB5p2ozGjPq1ZxRrzPMjKYhUkpmFswQpXWMUEZGcMZmQLLv9JC/H7YHRGN6xNPvRwro/VWHafqwFBqa6to1bPZJ942zRCUneXxHJfVJjHLr/3yz5QTd/5k9GLp6bBo9efFgv3VLxpk8Q+RxQMSmjOwYzSUqzuZYqY3Vypg9QxSjw4CoQVCt34BInlT0XDKzsoaI3c0HpsTQb4dPCYNGnwZER+xi6nsm96PPNx6jb7Zk0pwvttF3d00UHYp+1xD5OEMk544dPFnmtlv1d1sz6b4FW0SL/R/HdKO/XTLYr471cY4uMz0dJ/0aEP3444+i/MR+QcAacCavwak6xNwlMx2ZM+rZg0jSKTpE9wFRfqk1u8wk3GnGAdHOzGK6wke/83hBufj8svHhiLQ4GpAcTb/sO0l7skvonV8O0ayz+5A/qKqtU7Ib/vDNSopx3636+21ZdK8jGPrD6FT6+6VD/D6+J87kGSKPw/T4+HjFSRpYA55sXO3QH5g1Q6THkllmYaXuM0RG6DLLd8yt8uWYBj0KqzlD5Ct+d+iHeHxIZGgQJUSF0qPT7V2/ryzdT4dOlpI/g2MeUuqPYxkP3WXayhD9uD2L7v50s3Ck/r9RqTTvsqF+D4acJRMsDGezT7J6QMRzwnhwanl5uTZbBHSHFNAFBXSgSJOOPogJC9JdQGSIDJEjIOITjd5MLZny6loR0DMdLVgycxZW78oq9tmIlfWH7eWyMT0aLp5nDO9KZ/brTNW19aJ13B+fFymo5s+tPwIMd+aZLdqRTXd9Yg+GLhvRlZ69XB/BEMN6NDnuRE8NKH4LiLhDi1vbefQFzxAbOXKkyw2YD5ke5Ssqs2rGlHlmDkduPQVEqTqcYyaRuhx2Q9ZTMNk4IxAaFGDaYL4tenWOovDgQDHklo0SfZkhGtOzISDiY8fTMwaLeXLrD5+iT38/ZpkOM3fdqhfvzKY7P94kvk8zhqfQ81cM8+m8Nfcm3ktzRv19332uIZoxY4Y2WwL0L6g2aYeZHktmrHWQB289Z4jY1JKza8WVtaJsFq8znU6+U4eZWYP5tgh0CKvZMXpnZpHb3U3eiNi5o61xhojp1jGCHpjan55cuIue+WE3nZOeSF1ifWc6Kku7/pq715pb9c+7cmiWIxi6eFgKvfCH4boKhiTsRcfvsRl1RB4HRI899pg2WwJ0S6HJTRn12GWW5dAPsYO2nBemV9ickQMibmfumxRNeuKUQz9k1XKZZLAjIGId0SXDu/okO8SBV3NC9utP70HfbM2krccK6a/f7KB3Zo7yWbCqZIj8FBA1dquWf/fyPbn05/mbqKbORhcNTaYX/6CvzJAz8sJYDvw2E7qYZQb0jawVm1VQrccuM+eWe71nNhqE1dU67jCzpqBaMsiHM80a/Ieab77hE/2zlw8RmsQlu3KEZsbnHkR+Kpk5u1WXOtyqV+zNpT99tFE0rlw4JJlevnK4Twe2ekqciUtmHu/1uro6+sc//kGnnXaacI3mjjPnGzDzYFfzXmXrzan6uAEE1U28iNz0VvGHB1EnnZXy/CWs5plm7Z2j5WmG6LSe8S2uk94lhv48qbdYfvTbnT6bjeVvDZGzWzVniVbuO0m3OYKh8wd3oZf/qO9gyMWtGhkioieeeIJefPFFuvLKK8XYDZ4zdtlll1FAQAA9/vjjYkcBc2F2U8bGJTOtTxieZIj0LKhuPL5Dj633Vp1035i+SVEUEhhAJZW1lHFKuw7hsqpa2pFpz0Kd1jOh1XVnndOHeneOFEHK33/YTWY3ZWycJfpy03G67T8bRNfd1IFJ9M+rRvjVsNJdFFG1CTVEHu/9+fPn07vvvkv3338/BQUF0VVXXSXmgnEr/rp167TZSqCbLjOzIv82ruHLNm1/oky5jzVCQKRfLyKlZGZxDRGfaNOTozUvm23OKBTt4lzqbcs/iwX58y4fKpYXbDhGaw7kkS/b7v0Fu1Uzb6w4SFW19TRlQBK9dvVIQwRDLuaMOsmmq4nH7wBPo+d2eyYqKkpkiRgewMoO1sB8WCFDxC3ZUsSoB2G1bLnvaoQMUbR+NURSVG3FOWaNGeRUNtN6oCvPL3MH1hnNHNddLM/9ajtVVNeZumTm7FbNTE5PpDeuGUkhQcYIhpw1RL4qc/oSj9+F1NRUysrKEsu9e/emxYsXi+Xff/+dQkOtLVw0KzJAkFcGZoSFy3oyZ5Qu1UbQEOk5Q9Qw2BXHJl84ViuCaif/obaYc15/6hITRkfzy+nlpfs0NekscwRc/gyI0h3z5M7u35neuNZYwRAjrTXMOPHe43fi0ksvFZPumbvuuov++te/Ut++fem6666jm266SYttBH7GChkiPXWasYOvkiGKM5CGqETHGiKLl8ychdU800wLnRxrYTZl2B2qT2uhw6w5osOC6W8zBovl9349rFnAlldi/yywSaU/TTpvmtCT/nfH6fTudaNF2dBoxJpYQ+SxD9G8efOUZRZWp6Wl0dq1a0VQNH36dLW3D+jJh8jEGSIXc0Y/f9H5JM4nF+6296VpnRpt987eKnrSEKFkRtS/S7QoC3PWLKuoUvXs4/YTRUITw75Znpo/ThmYJPx3Fm7LojlfbKNv7pyguqbmZGmlMpDYn59RzgiN6u5eSVHfXWY1ZDa8/sSNHz9edJohGDIvVskQ6cWcUWaHkqLDDCG0lOUHbh1mg0a9wHoU9nthrN5lxoQFB1JfR6CiRRZGttuP7tGxXQHHY9MHiWMMz1x7f9Vh05kymoU4nRwntaBdR9uPPvqIJkyYQCkpKXT06FHx2Msvv0zffPON2tsH/AyXb2QJKTbc3CeVGJ2UzDINJKiWJ9ooh7eKnnREcso9t5vL7bM6g6VBo6M1Xgv9kCflssaB9SMXDhDLLy3Zp/rcNT0Iqs1AnKNSwMaSNXX+78j1a0D05ptviozQBRdcQIWFhcKokYmLixNBETAX7Fsi5QamzxCF6ePKR2m5N4B+SM86IkVQHWXdOWbNjfBgdqqcIeILpw1HCzwWVDfm/0al0sQ+nUTpbe6X21TVOiEgUofoMB7yTbo4Vvo9IHr11VeFD9HDDz9MgYENgrDRo0fT9u3b1d4+4GdkJwFPqDZaN4RRB7weL5ABkf71Q3oe39EwtsPcmc32ZYjUDYj25ZaI7w0Llgc5gq72wIHr3y8dImb4rTt0ihb8fky1bTzp+Dx0jjLO90qPBAZwR645hdUen+EOHz5MI0aMaPI4t9yXlamb4gT+xwqmjE3Hd9Tqw6XaUBki/bXew6W6KQOSY8TVPY+NaG7iurflMhYLe6t7S0uIoPvP7S+Wn/5hN+UWV6pryhiNAFmtslmRyVrvPf7k9uzZk7Zs2dLk8UWLFtGAAfb6LzAPVhFUMzHhQboSVRuqZOY4yegpIIIpY1MiQ4OoV6dIpf1eLdYfcZTL2qkfasyNE3rQ0NRYUbJ/7Nud6g52hahavQGv5RbPELF+aNasWbRgwQJR312/fj09/fTTNHfuXJozZ442Wwn8RqGFAiK9DHg1mqha7xmiBJwAXRjiKJuppSPi80CDIaM67eQ84HTeZUNFeebHHdm0aEe2168p9W0QVXtPrElb7z1uvbjlllsoPDycHnnkESovL6err75adJu98sor9Mc//lGbrQR+wwou1XoyZmQ33QLHQcZQGSJH0HHSYX6nB6AhallH9PWWTNVmmrHmLbu4koIDO9CIbur56wxMiaE/ndlLzPx69JsdFBLUgZJiwoSrNevCPBHKc9CmZIjQZaZehqjC4gERc80114gbB0SlpaWUmJio/pYBXVBUbj/BxZm85V4vXWYyOxQdFqRsjxHQY4aoYWyH+T+77ZlpxkaKarDekR3iQCtcZQfouyf3FdmhQ3lldNOHG5TH2UqBp8bLAEncx9p/dn5Mbg/7Y7HZqb8Hu5qFODng1XF+MAtemXNERESIG7CAhshCGSJ/BkQnHDPMjDCyw5nOOtQQQVTdcuZFatUKyqqV2VTeGjK213+oLY+rd64bRS8s3idmnbEQnDsZ2QSUM1OyI7MleD4hu73Liwu+0ODXBN4RZ1INkccBUX5+Pj366KO0fPlyys3Npfp6V2OmU6fsXw5gDqzYZVZeXScMx/zhEn1Cabk3VkDknCHSy/gORVSNjECTz3n3hAgRYLCwemLfTqpkiNQSVDemT2I0vXntKOVnzvRwYJRTzLcqyi6yL3PZzvkxdinnzFBxZanyf9M64gJeVQ1RhcUDopkzZ9KBAwfo5ptvpqSkJF0c+IB2WKnLjK8eJSys9seJVBFUGzQgqqypFxPF9eAMjTlmrQ965YCI/Yi8CYi4lZ3LWczoHr6Zz8V+aKnxEeLWEhyUczBkD5AqRYDEGcNJ/Tv7ZBvNTjxKZnZ+/fVXWrVqFQ0bNszPbwnwBYUWElVzZwufyNmSvshPAZERW+5lOzeb8vFVOXfz+DsgqqypE5k+BpPumzKoawx9vz3L65lmGxzlsv5J0crQTz3AF+p8Ece3fknR/t4cE/sQ1ZCZ8LgmkJ6eThUVrddtgXkotlCGyLXTzD/mjCcM2HKvRy8iqR/izqdoHWSr9JghUsOLaP0RddvtgTGIdTTZmE1D5HFA9MYbb4ixHStXrhR6ouLiYpcbMBfyA2+FLjPnspm/rnwaSmbGGy+gp06zU44xDQmRoSjrN4Mcr8EDVEu8sJlQBNU9E9r9GsB4xKFk5tgRcXEi8DnnnHNcdpAUUsphr8AcWElD5O9Os7p6m9A6MF3jjCf+VLyIdDDPLM8hqMYcs+bhcnBKbBhlFlXSrsxiGtvL84CGAyn+v1p1mAH9d5kVV9aK4xYbaJoBj3PJ7D8UHBxMH3/8MUTVJqeqtk5oQqzSdu9vt2runKmtt1FQQAdDmscpGaISHWWIoqyR2WwPg7rGioBoRzsDok0ZhVRvI+rWMVy0tgPrEOt0gczHSm+tGwwbEO3YsYM2b95M/fvbh+8B8yKzJNxIaBUdRowfM0Sy5Z5PLka84ursCD50UTJzaIiQIWpdR7RkV067R3go4zqQHbIcQYEB4pxQUlUrGm/MEhB5rCEaPXo0HTt2TJutAbqiyMmDKMCAJ2ijZYgUQbXBOswknRxZLT0ERMocs0jjZdp8xeCudh0Rt957I6hGucyaxJpQR+TxZf9dd91F99xzDz344IM0ZMgQUT5zZujQoWpuH/AjVtMP+XuemeEDIkVU7f8DZL4jKEPJrGV41AZzILeUKqrrPBq7weX0LccKxfKYntAPWREWVrNTuJnMGT0OiK688kpxf9NNNymPsZgaomozd5hZJyBiq39/lcyMOOVet11mKJm1SWJ0qHjP+P3anV1MI9Pcb53ffrxIOEZ3igqhXp0ivXuzgCGJc3Qey0qCJQOiw4cPa7MlQHfIyF/qaqyUBi6u8L0PUaZjjpnRTBklfHLUi6gac8zahi9kh3SNoeV7TwodkScBkSyXje7eEbYGFiUWJTOi7t27+/t9AD5CZkn05EBr5rZ7Kao2uoaIR3d4WoLRKkMkgzTQctmMA6IdJ4rbJ6hGucyyxMkBryYqmfl+eqUTv/zyC02fPp1SUlLEVcbXX3/t8vwNN9wgHne+nXfeeU2GybIVQExMjPBI4hlrpaUNw/yYbdu20RlnnEFhYWHUrVs3eu6553zy9xmdIodYzlols2C/l8yMmiHirhOeM6WHspnUEHWEqLpVBjkcq7d70GnGvjMbjhSIZQiqrUuckiFCQKQKZWVlYiba66+/3uI6HABlZWUpt08++cTleQ6Gdu7cSUuWLKGFCxeKIOu2225TnmcTyalTp4rM1saNG+n555+nxx9/nN555x11/ggTA1G1b/c1t7AyKQZ0qWb4gqWzYs5Y5dc5ZpylYtB2716n2b6cEiGUdoc92cXis8rz6gYkY06Y1TVEhVbuMlOT888/X9xaIzQ0lLp06dLsc7t376ZFixbR77//LuwAmFdffZUuuOAC+sc//iEyT/Pnz6fq6mr617/+RSEhITRo0CDasmULvfjiiy6BE7D2YNfm2u7r620+sxuQ2SE+gUeEGNfziUtU3C3nTx3RKac5ZlIkD5qHy7P8/ear/H3ZpTQk1Z4xcqdcNrJ7vPCjARbPEFUgQ+QzVqxYQYmJicII8o477hDz0yRr164VZTIZDDFTpkyhgIAA+u2335R1zjzzTBEMSaZNm0Z79+6lggJ72rcxVVVVmNHmlCGykqha/q3swFtaXetz/ZBRs0N6ar137jDjrBVoGd4/ctCru35EvyvlMgx0tTJxDm0pSmY+gstl//nPf2jp0qX07LPPioGynFGS89Kys7NFsORMUFAQdezYUTwn10lKSnJZR/4s12nMM888Q7GxscqNdUdWxIpt92HBgYoOxpfmjJlFxhZU66n1vqHDDKaM7jBIGjS6oSNiexVlwj0cqi1NnCND5K9B2FrgVj45Pj7e7SstFjmrxR//+EdlmU0g2fSxd+/eIms0efJk0oq5c+fS7NmzXXRIVgyKii1ozCj/3pMlVeKLnhrvW1NGowqqJZ2i/T++QzFlNMk4Aa1pyBC13Wl2NL9cfDdCAgNoWLc4H2wd0H2XWbnFNEQvv/wy6YFevXpRp06d6MCBAyIgYm1Rbm6uyzq1tbUiKJO6I77PyclxWUf+3JI2iXVLfLM6DRoia51YWHciAyJfYfSWez1liGDK2D7H6t1ZxVRTV0/BreiCZHZoaGqsyKYC6xLrlCHypd7S7wHR9ddfT3rg+PHjQkOUnJwsfh4/fjwVFhaK7rFRo0aJx5YtW0b19fU0duxYZZ2HH36YampqlDEj3JHGmiTOfIGWU+NW7DJzFVbX+t6l2iwBUUm1/+eYwYPILbp3jBAdY6VVtXTwZCmld7GX0JoD/kOg8XGS9ZbcdWiG84RXLQKVlZVeiY/ZL4g7vvgmXbB5OSMjQzzH89LWrVtHR44cETqiSy65hPr06SNE0cyAAQOEzujWW2+l9evX0+rVq+nOO+8UpTbuMGOuvvpqIahmfyJuz1+wYAG98sorLiUx0Mx7U1Ur/Eas1mXmrwGvpimZ6SFD5BB0o2TmHnxlPzBF6ohaP4b/joGuwEFoUCBFOMxXzTK+I6A93kEcdLCYOTIyUmRZnG+esGHDBhoxYoS4MRyk8PKjjz5KgYGBwlDx4osvpn79+omAhrNAv/76q0s5i9vq09PTRQmN2+0nTpzo4jHEoujFixeLYIv///333y9eHy33rSOzQywwtlpqXHaa+apkxjOhch1t6kadYybp7NAQ+dOHKL8Mpozt1hG1IqzOLamkI/nlxHJSbrkHIE5xqzaHjshjk445c+bQ8uXL6c0336SZM2cKU8UTJ07Q22+/TfPmzfPotSZNmiRKMy3x008/tfka3FH28ccft7oOi7E5kALuY8UOM39NvM8uqiT+GoQGBRg+qyEzRCWVtcIg0R/BNOaYtd+gcWcrrfe/H7a323NJzQzlEeA9sREhlFlUaZrWe48Dou+++060wnMwc+ONN4qRGFzGYidoztawczTwLSxoq66rV/XkY9UOM3/MMzvhpB8yum8O7zs2RKyps4nAxB+aKMwxa7+wemdmcYsCWVkuG4v5ZcCk88w8LplxBxd3ezE8P0y22XOpisdmAN/z5MJdNPSJxbTLjbZZd7GiS7W/5pkZfYaZMxzQJTj8f/zlVi01RBjb4T69OkVSWHAAlVfX0eH8smbX+U0OdIX/EGjsRWSS1nuPAyIOhliPw7B257PPPlMyR+waDXwLC5+/3HRc6FC+2nxctdeVKVArZ4h8JapuEFQb26VaD15EPI9LzoSTgRloGx7BMTC5ZYNGvjjgGWbMmJ7QDwFzDnj1OCDiMtnWrVvF8v/7f/9PaIh4ivx9990nusKAb2HvkOJK+wlg2R5XTyZvaGi5N7amxQii6oaW+wgyA/7sNJPlsqCADhQTjjlm7S2bNWbT0QKhc+uREEGJ0eYI3IH3xMoBryYpmXl8xODAx3lu2J49e4QPEOuIWLwMfMuag3nK8sGTZZSRX05pCd6fWGXXgCVLZo4Tqa81RKbJEPlxnlm+43fGY46Zqp1mGNcBrJAh8voSisXUfAP+Ye3BhmG3zLI9OXTDhJ5evy5E1dxlVutbUbXBW+4bB0Ts9u2vDJHRu/X8PdOMu3+dBf4wZATNEa8ERNXWDIiefPLJVp9njx/gG9hmf71D6DhjeAp9vSWTlu09qUpApLTdWzBD5MsuMz7xmMWlWtLJ4RDtj5KZ9CCCS7Xn9E2MFjPK+ELg2KkKJdPM9gnbjtuzRqdBUA2csHzJ7KuvvnLeH2IkBouseco8D15FQOQ7tp8oorLqOnEC//PZfURAtO5QPpVX11JEiHfJP6uO7XDWELFQXWsvHc5oVNbUC7O7LrHmKJl1jvafhkiWzDDp3nPYhLV/l2hxXNmRWaQERFuPFQpbD35fu6tQjgdmLJlVkxnw+Ky5efPmJo/xyI4bbriBLr30UrW2C3hQLhvXqyP1TYyibh3DxZXd6gP5dO7AJK/2oZW7zKJCgohtWHhyCZcOtQyIMgsrxX3nqFBhhW8G/KkhQsnMe4NGERCdKKILhiQ3GddhdJ8soFHbfYU5NERezTKTsB/RE088QX/961/VeDngYUB0eu9O4kB1Tv9E1brNrJwhYlM6X3WanSgsN40HkZ66zKAhah+DpLDaqdNs/RG7Q/WYHmi3B67EyS6z8ppWp05YKiBiioqKxA34zm9FXrmN750g7s9OtwdEK/bmev3hlIFAXIQ1xam+Mmc84cgQmUVQ7awh4oMk69x8icxKdcSke+9a7x3CavY545Z7ZgwcqkELGaLaepuQb1iuZPbPf/7T5Wf+0mRlZdFHH31E559/vprbBlphc0YhVdXWi5MPl8uYcb0SKDw4kLKKKml3VokywdpT+CTG0+6tmiHy5TyzEwXmElQz8REhFBjQQZxMWdPjS23UKSmqRpdZu0jvEi3eOx67kl1cKd4/PhZEhwWJGWYAOMNyAp7ByOci1hFFhRrb+8vjrX/ppZdcfg4ICKDOnTvT9ddfT3PnzlVz24Bb+qEEpa7PH84JfRLo5925tHxvbrsDImeH5pgwY3/A9d5pZrYOM1ly5LEZ3HbPZTPfBkQQVXsDH0P4AmtPdgntOFFMx07ZS7qju8eLQAmA5rJEOcVVIiOcavCqqsdnOzm2A+hHP+QMl804IGId0ayz+7TrtWUQEB0aJCz9rYhizqix4VhmkXnmmDXWEXFAdNLHOiI56R5t997piOwBURHtzS4Rj6FcBlrTEXFAZAZhtTXPdganorqONh8rcNEPSSY5hNWbMwqowHFy8BRpwx5rQQ8iX5szypKZWVyqm3gR+dCcUcwxc7xfKJl512nGcEDk3GEGQHPEmsit2uMMUVlZGc2bN4+WLl1Kubm5VF/vKpo8dOiQmtsHmmHD0VNUU2ej5NgwMVvIGS69sA6Ar/B+2X+SLhne1eN9aOUOM1+KqtnjSGY0Uk0yx0zCNgK+br0vKLO/V1zake8faL+wetWBPKENYX+iIan2xwBoTJzjPCHHPVkqILrlllto5cqVNHPmTEpOToYvhR/LZZwdas4XhMtmHBBx2axdAZGFXaolvmi7lyM7IkMCTTeItJMfzBmlSzWLulnHBNrHgOQYYRTKwRAzvFucaTyygPrEWTlD9OOPP9L3339PEyZM0GaLQJuskQFRL9dymeSc9ER6c8VBWrnvpOj08VQMKV1HrZwhUkpmGgZEiqA6Ptx0Fxb+GN8hBdXyd4P2wZ1CPTtF0qGTZeJnlMtAa/AFiL98x/yuIYqPj6eOHVFP9hcllTXCSbY5/ZBkRLc4cULniJ21RJ5SVCFb7q17YvFJhqjAnIJqf5kzNnSYWfdzqxZDHGUzBoJq0Bp9HLYvO53MPC0TED311FNiXll5ub0dE/gWFjly1ietYwSlxjevO+HOsLP6dW63a7WsBVu5ZOaLtnszttw3CYhKqn1vyoiAyGsGOxyrObk8Mi3O+xcEpmV4tzhFhM/nJkuVzF544QU6ePAgJSUlUY8ePSg42PWkuWnTJjW3D7TYbt98dsi5bPbt1kwREM05L92j/QhRdUNAJLuWtHSpRoZIHWDKqB52fzOisT0TKBoCddAKvTpHCR0kO1UfyC0VA4ItExDNmDFDmy0BnumH2giIOEPEV3csruZMhCcnXUVUbWENkTSk1FZUXW7eDFG0vWx1qryaauvqfeJnpcwxc2SnQPvhrrJvZk0wZbAO1CUwoIP4vKw7dIq2Hi+0VkD02GOPabMlwC2x866s4lYF1ZL4yBAakRZPG48WCNfqa8Z2d3sPI0PUkCHisQVandAzTTjHTNIxIkRkGHikHgdFidHa+yzxmAnxu1EyU4WhqSiVAfcY1i3OHhAdK6Q/jO5GRqXdR/nq6mo6fvw4ZWRkuNyAdvAHjk8wvTtHUmJM2ycYLpsxyz3UEcGYsUFUrVXZrL7eRlkmdalmOIDkoMiXOiLFpRoBEQA+ZZgjeOYMkZHxOCDat28fnXHGGRQeHk7du3ennj17ihvrifgeaMe6Q82P62iJsx2u1asP5AsTQHdBhogoODCAIkICNSub8UgLNtfkdHOSw7PHbPi60wxdZgD4L0PE7Mkq8ehcY/iS2Y033khBQUG0cOFCGDP6mDUH89zSD0kGJEdTl5gwMbWagyk51qM1bDabkzGjtduXuWxWXl2nSUB03NFyz++PWefFsY5ob47vAqJ8x++BhggA35ISGyb8v7jTk2UdI9PirREQbdmyhTZu3Ejp6Z51LgHv4EGZ+3JKlQ4Qd2Czv7PTO9Mn64+Jspk7AVFlTT1V19kdaq1szCj//qyiSiquVD8gMnPLvT8yRNW19crcOZTMAPAtHTp0EGWzpXtyaduxQsMGRB5fmg4cOJDy8uyZCuD7chnPKfNENCrLZsv3nhTZH3c9iIICOohWSiuj5TwzGRCZbahr8wGR9hqiAoe7OpcgrR7IA+BPEf7W43bjYEsERM8++yzNmTOHVqxYQfn5+VRcXOxyA9qw1kP9kGRCn04UEhhAGafK6aDDit9d/ZDZxknoya1azjEzo6C6qTljlc86zOIjgjHHDAA/MKyb3cyTO82MisclsylTpoj7yZMnuzzO2Qc+gdbVGVdQZZSBrp4QGRpEY3t1pF/354mymbRZbwk5oC/Wwi7VTeeZ1Wo6x8ysyJliLCD3mQdRpDkF6gAYpdPsUF6ZuIg0YqbW44Bo+fLl2mwJaBFuzz6cVyaMFk/r6fkcOW6/54CIXatvPbNXq+uiw6wBOYFeS1G1qTNE0b4rmclJ9/AgAsA/xEeGiJFSXI3YfryIJvb1rJphyIDorLPOavG5HTt2eLs9oJXs0OCuse2KujkgeuK7XWIOGguEpTamOeBS7Zt5ZjJDlGrigKizD0XViikjJt0D4Nf2+4xT5cKPyIgBkdf9viUlJfTOO+/QaaedRsOGDVNnq4Aq5TJJ94RI6tU5kmrrbbRqf55bomojpjs1K5mp3GVWUlmjdESZOkPkCIi4nMVGlL4pmVnbKgIAfzIs1dg6onYHRL/88gtdf/31wovoH//4B51zzjm0bt06dbcOCG2WnF/mqaDamXMc3WZcNmsNmQ2xugcRIzNpxSpniOTIjriIYKHxMisJjmwNT8CWXWDau1RDQwSAvw0atx63QECUnZ1N8+bNo759+9IVV1xBMTExVFVVRV9//bV4fMyYMdptqUU5dqpCdCRxG/zo7u33dpBjPFbszW31al0RVSNDpFnJTGm5jzVvdki6fXPQ5wsdkZx0j5IZAP5jUEqM0LrmFFdRdpH9ws+UAdH06dOpf//+tG3bNnr55ZcpMzOTXn31VW23DtDaQ/YS1/BucV5lE0b36EhRoUHixLT9RMs+ERBVNyA77dTOEB23QMu9r80ZpYYIJTMA/EdESBD1S4o2bJbI7YDoxx9/pJtvvpmeeOIJuvDCCykw0Nqmfb5ijZf6IUlIUACd4RC5tVY2ayiZQUOklTGjIqg2cct949Z7rQMizDEDQB8Md5TNtpk5IFq1apUQUI8aNYrGjh1Lr732GhyrfaAf8lZQ3bxrddsBEUpmzqLqWrdcvt3lRIH5XaobZ4h49IwvNEQyAAMA+Nmx+liReQOicePG0bvvvktZWVn0pz/9iT799FNKSUmh+vp6WrJkiQiW2iPM5lIcvw6bOrIWyRk+CT366KNCuB0eHi5MIffv3++yzqlTp+iaa64Reqa4uDiRxSottc/8knCZ74wzzqCwsDDq1q0bPffcc2QE2Fk6t6RKZHfUmA0zKb2zuN92vIhySypb1RAhQ2TfBzwKgkXBatrRN8wxiyCz44vxHTV19Uog3xGiagB04Vi97Xih5t2lfu8yi4yMpJtuuklkjLZv307333+/EFQnJibSxRdf7NFrlZWViVb9119/vdnnOXD55z//SW+99Rb99ttv4ndPmzaNKisbTuYcDO3cuVMEZQsXLhRB1m233aY8z+NEpk6dSt27dxdDaZ9//nl6/PHHhVWAUcZ1jEqLp7Bg70uUidFhNKSr/cO6cu/JZtdBhqgB3ueXDE8Ryy8s3kvqj+0wf4aos2LOqF2GqMCRHWIxZxyaAQDwK/2Soik0KEBk1o/ktz0uyjQ+RCyy5qDl+PHj9Mknn3j8/88//3z629/+RpdeemmT5zg7xOLtRx55hC655BIaOnQo/ec//xFibplJ2r17Ny1atIjee+89UcabOHGiEHpz9orXY+bPn0/V1dX0r3/9iwYNGkR//OMf6e6776YXX3yxxe3izjk9zGhbezBPtXKZ5Oz0lstmHM1Lz53YcJQemHsn9xMdfuz0LQfsepvNyCmuNP2ke19qiGS5LD4iBHPMANBBd+lgx4W30YTVXhszMiywnjFjBn377bekFocPHxZt/nJ2GhMbGysCn7Vr14qf+Z7LZKNHj1bW4fUDAgJERkmuc+aZZ1JISMMJnrNMe/fupYKCgmZ/9zPPPCN+l7xxmc3XcHCy7tApsXy6igGRbL//dV+eODk7UyK0MvZlaIjspCVE0B9Ps7////hpr9daIg6GOIvMA3dlOcnM+KLLTDFlhH4IAF3NNdtqMB2RKgGRFnAwxCQlJbk8zj/L5/ieS3XOBAUFUceOHV3Wae41nH9HY+bOnUtFRUXK7dixY+Rr9uaUiAN9eHCgIlJTg6FdY8VVe0lVrRjl0ZxLdURIoNAtATt3ndNXpIA3HC2gFS2UGt1FCqqT48Iskc1omHhfrXmGCHPMANCXjmirFTNEZiM0NFSItJ1vvkZ2l43p2VHV4IRPwmf1c5TNGrXfQz/UPEkxYXTD6T3E8vM/7fVKKJhZVGGZcpnzgFcevqpmp54z+Y7sE1yqAdBXhmhnZnGTSoSe0W1A1KVLF3Gfk5Pj8jj/LJ/j+9xc15N6bW2t6DxzXqe513D+Hbr2H+qlXrmscdmssR8RXKpb5vazegtjy11ZxfTDjiwVWu6tERBJo8SaOpsmQ3IZeBABoC+6J0QI2UV1bT3tzfa8A91f6DYg6tmzpwhYli5dqjzG4mbWBo0fP178zPeFhYWie0yybNkyYQXAWiO5Dnee1dQ0HIy5I40F4fHx3reyawG3ef92WM4vUz8gOqNfJyEU5rb+jPxy5XFkiFomPjKEbj2jl1h+cfE+qm3nVc8Jxxwzq2SIuFMvOixIUx0RSmYA6IsOHTrQUMeg1y0GGvTq14CI/YK2bNkiblJIzcsZGRlih957772iC43F2tzif9111wnPIhZwMwMGDKDzzjuPbr31Vlq/fj2tXr2a7rzzTtFJxusxV199tRBUsz8Rt+cvWLCAXnnlFZo9ezbplZ2ZRULgHB0aJGbDaOHAPLqHPRhctqche1YIl+pWuWliD4qPCKZDeWX05aYTXnoQWSMgYjor5oza6IhOOTyOYMoIgP7KZtsMpCPya0C0YcMGGjFihLgxHKTwMpsxMnPmzKG77rpL+Arx4FgOoLjNng0WJdxWn56eTpMnT6YLLrhAtN47ewxxl9jixYtFsMUu2+ybxK/v7FWkN6R+aGyvjhQUqM1bpJTNnETCRY6J5Ogwa57osGD686Q+YvmVpfupqrbOCw8i6wREWneaNZTMzN+1B4DhJt8fM06nWfunharApEmTWhVacpboySefFLeW4I6yjz/+uNXfwx5Gv/76KxkFqR8ap4F+yHmMx99/2CO8dcqra8VQvoY5ZvAgaomZ47vTe6sOicDmk98y6IYJPd3e5/xZVzJEFphjJukUra0XUZ6cdO/QKwEA/M8wR8lsf24JlVXVejWcnKyuIbIqrMiX7fCn97YPY9WCPolRYrgoi95WH7AHYBBVu6eJuXtyX7H82vIDIph0F96/5dX2rFJyrPldqn2dIYIPEQD6ITEmTBznuCl3xwljZIkQEOkMrrfySZO1KuldojX7PZx9a9xtBlG1e/xhdDdK6xgh5nN9sPqIx+UyDhDUGMViFLT0ImJxuwzkZUcbAEBnBo3HjaEjQkCkU/0Ql8u0Nu6TYzxW7M0V5RyIqt23pr/vXHuW6O2VB6nIcUJ2NyDqaoEZZs7IzA17EalNgWPfd+A5Zij1AqArhioGjcgQAS/0Q1q02zeGPY7CggMoq6iS9mSXULFDQwRRddtcPKwr9UuKEgMM3/n1oFv724r6IecM0UkNJt7LIIvnmAVawPkbACMxXBnhgQwR8JDKmjraeLRA9YGuLcFlmwkOnRKXzWTpIQ6DXduET773T+0vlrlsdrKkyu2AKCXWmgFRnhv7qL0t9xBUA6A/BjuE1ccLKhRHeT2DkpmO2JxRSFW19dQ5OpR6d47yye+UZTMe4wENkWdMHZgkOilY8/XGigNtrm/FlntnHyIWVas9vkOaMkI/BID+iAkLpt6dI8XyNgOUzRAQ6Yi1B/OUUhaLnn0ZEG3KKKCKGnsHVGxEsE9+t9Hh9+jBaelief66DCXgadOl2molM0fbPQf7pVXud+W5AzrMADCGH9EWA5TNEBDpiLWHfKcfkrBjMnezyXmlHIexQzZwjwl9EkQAW11XT//8eb9bc8ys5FLNsMdVRIi9q44789REpuFRMgNAnwwzkGM1AiKdwH42MoL2hX6ouSyRFFRr3d1mtizRA9PsWqIvNh2ngydLW9SHSR8eqwVEWnoRNcwxg0s1ALp2rD5epHrJXG0QEOmEDUcKxERwPlmyx40vkX5EDDrMPGdU93ianJ4ohvK+tGRfs+twJx8THhxIcRYsSco5Y2oLq2XJDHPMANAnA5KjKTiwg/iusrhazyAg0uG4Dl/phyQjusUpgVCc4x54huw4W7gtSwznbbHDLC7M5++vNTJEMGUEQI+EBgXSgOQYQxg0IiCysH5IwgNkz+zXWSzHICBqFwNTYmj6sBSx/OLifS2bMsb7NvunFzpFa+NFBA0RAAZyrD6GgAi0QXFlDW0/7h/9kOSKUalCUD0iLd4vv98M3Delr/AnWronlzYetc+jayqotpZLtdYZIqXLDBoiAHTL0FRjOFYjQ6QDfj98SnR59UiI8JtHDWeIfn94Ct3jGFwKPKdX5yj6v5GpYvm5RXtdBISKS7UFBdVMZw00REfzy8ToDg7ku8RYM9AEwAgMdwirecgray31CgIiHemH/JUdcr6Kx/gD77h7Sl8KCQyg3w6folUH7L5SVjZl1DJD9MXG4+L+jL6d4Z0FgM4vFiNDAoWJ7YHc5jtx9QACIh0NdB3vGKMBjAtngK4ZlyaWn/+pIUtk9QyR1BCp5UPEV5kyIOJyLwBAvwQGdKAhsmymYx0RAiI/U1BWTbuyisXyuF4d/b05QAX+PKmPMCJkq/qfduZQfb2NMh0u1cgQqZMhWn0gT1gZcHfkuQOTVHlNAIAPhNU67jRDQORnfjtszw71TYyixGjoIMwAz6K7aUJPsfzikr2UW1IlnKzZ77JLrDXfY+kTxClzNiH1ls8d2aFLhqeIIcUAAKMYNBaSXkFApBP9kD/a7YF23HpmL4oJC6J9OaX01sqD4rGkmDAKDrTmVy4qNIhCg+x/e16Jd2WzovIa+mlntli+YlQ3VbYPAOCbgGhPVolw7tcj1jw661I/hIDITHAp509n9RbL/157xNLlMobNKKWw+qSXZbNvt56g6tp6MYNvcFe74RsAQN+kxIaJTHFtvU2RiegNBER+JLekkvbnloq24bE9ERCZjRsn9BAHANl9b1VBdVNhdZUq5bIrRnezpOs3AEakQ4cOujdoREDkR6pq6un/RqXSOf0TKR6jB0w55f3Os/soP1s5Q+TiReRFQLQnu1iI1YMCOtCM4XZncACAMRjqCIj4O6xHgvy9AVamW8cI+scVw/y9GUBDrhqbRu/+elj4EPl6aK9uvYi80BB9vsGeHZoyIIkSHK8HADAGw7rpu/UeGSIANB5s+Oa1I+mG03uIjigr4605I+uGvt58QixfMRreQwAYjWGODNGhvDIqqqghvYEMEQA+SBPLVLGVka337Q2Ilu3JFdPt2dbgLMcwYgCAcYiPDBGZ8oxT5bT9eBFN7KsvM2JkiAAAhhBVf7HxmLi/bGRXCrKofQEARmeYjv2IcFQBAPi4ZFbdro7M5XtPimV4DwFgXIbpeIQHAiIAgI9F1Z5niFg7xPPLRqTFUZ/EKA22DgDgC5AhAgBYns6OgKikqtYjp1oekPuZo7vsD6PhTA2AkRmUEiPGGOUUV1F2kX3Go15AhggA4BNiwoMoxKH98URHtOVYIR3ILaWw4AC6aGiyhlsIAPCFP1u/pGhd6ogQEAEAfOZUm6B0mlV77Ex9/uBkig4L1mz7AAC+YbgUVutMR4SACACgWx1RRXUdfbclUyxfMQreQwCYgaE6daxGQAQA0K0XEU+1Z81Ranw4jeuFeX8AmMqx+ngh1dc7hj3qAAREAADdulV/7vAe4pl/AazEBAAYnn5J0RQaFEAllbV0OL+M9AICIgCAH8wZ29YQHTtVTqsP5Ivly0eiXAaAWQgODKDBXe1Zom06ElYjIAIA+DxDdNKNDNH/NtnF1BP6JIhByAAA8zBUMWjUj44IAREAwPcaojZE1awr+MLRXQZnagBM3Gl2HBkit3j88cdFq67zLT09XXm+srKSZs2aRQkJCRQVFUWXX3455eTkuLxGRkYGXXjhhRQREUGJiYn04IMPUm1trbrvLADAI3PGtjRE6w7l0/GCCooODaJpg7pg7wJgMoY5Os12ZhZTdW096QHdZ4gGDRpEWVlZym3VqlXKc/fddx9999139Pnnn9PKlSspMzOTLrvsMuX5uro6EQxVV1fTmjVr6N///jd9+OGH9Oijj/rprwHA2rirIZLeQ9OHp1B4SKBPtg0A4Du6J0RQbHiwCIb25ZSQHtB9QBQUFERdunRRbp06dRKPFxUV0fvvv08vvvginXPOOTRq1Cj64IMPROCzbt06sc7ixYtp165d9N///peGDx9O559/Pj311FP0+uuviyAJAOAfDVFRRU2LV4XFlTX0444ssQzvIQDMSYcOHRQdEbvR6wHdB0T79++nlJQU6tWrF11zzTWiBMZs3LiRampqaMqUKcq6XE5LS0ujtWvXip/5fsiQIZSUlKSsM23aNCouLqadO3e2+DurqqrEOs43AID3xIUHU6CjfT6/rPmy2cKtWVRZUy+GuEqdAQDAvGWzrQiI2mbs2LGixLVo0SJ688036fDhw3TGGWdQSUkJZWdnU0hICMXFuR4wOfjh5xi+dw6G5PPyuZZ45plnKDY2Vrl164aBkgCoAXsJJURKYXV1q95DfxidKq4iAQDmnny/TSeO1UGkY7jEJRk6dKgIkLp3706fffYZhYeHa/Z7586dS7Nnz1Z+5gwRgiIA1Cub5ZZUNSusPpBbQpszCkUWacaIrtjlAJiYYY6S2f7cEiqrqqXIUP+GJLovmTnD2aB+/frRgQMHhJ6IdUCFha61R+4y4+cYvm/cdSZ/lus0R2hoKMXExLjcAADqCqub8yKSYuqz+3emxOgw7HIATExiTBglx4YRT+/YccL/WSJDBUSlpaV08OBBSk5OFiLq4OBgWrp0qfL83r17hcZo/Pjx4me+3759O+Xm5irrLFmyRAQ4AwcO9MvfAIDVaWmeWW1dPX256YRYvmI0ytQAWEpHdNz/wmpdB0QPPPCAaKc/cuSI6B679NJLKTAwkK666iqh7bn55ptFaWv58uVCZH3jjTeKIGjcuHHi/0+dOlUEPjNnzqStW7fSTz/9RI888ojwLuIsEADAj15EjTREK/edpJMlVUJjdE56It4aACzA0G76cazWtYbo+PHjIvjJz8+nzp0708SJE0VLPS8zL730EgUEBAhDRu4M4w6yN954Q/n/HDwtXLiQ7rjjDhEoRUZG0vXXX09PPvmkH/8qAKxNSwNeP99gL5exdohnHQEAzM9wHWWIdB0Qffrpp60+HxYWJjyF+NYSLML+4YcfNNg6AEB76BTdtGSWX1pFP++26/v+gHIZAJZhsENYzc70fBxIcFww+QNchgEA/J4h+npLJtXW24RRW/8u0XhHALAIMWHB1LtzpC7a7xEQAQD8FBDZNUQ2m40+32D3HoIzNQDWQ/oR+duxGgERAMAvAVFBebXoLNtxopj2ZJdQSFAAXTwM3kMAWI1hOtERISACAPiUjpEhxNM7bDaiU2XVijM1T7WPjQjGuwGAhR2rbXxg8BO6FlUDAMwHu1BzUMQlsxOFFfTNlkzxOMplAFiTAcnRNGN4Cg1NjRNawuBA/4zsQUAEAPBL2YwDok/WZ4jJ9+xWO6FPJ7wTAFiQ0KBAevmPI/y9GSiZAQD8pyP6arPdmfr/RqWKzBEAAPgLaIgAAH4b31FTZ1MCIgAA8CcIiAAAfssQMaf17EjdE+w+JAAA4C8QEAEA/DbxnoEzNQBADyAgAgD4LUMUGRJIFwzpgncAAOB30GUGAPA5Z/brRP2ToumK0akUEYLDEADA/+BIBADwOYnRYfTTfWdizwMAdANKZgAAAACwPAiIAAAAAGB5EBABAAAAwPIgIAIAAACA5UFABAAAAADLg4AIAAAAAJYHAREAAAAALA8CIgAAAABYHgREAAAAALA8CIgAAAAAYHkQEAEAAADA8iAgAgAAAIDlQUAEAAAAAMuDgAgAAAAAlifI8nvADWw2m7gvLi7G7gIAAAAMgjxvy/N4ayAgcoOSkhJx361bN2/fGwAAAAD44TweGxvb6jodbO6ETRanvr6eMjMzKTo6mjp06KB69MqB1rFjxygmJkbV17Y62LfYr0YDn1nsWyNSrOPzGIc4HAylpKRQQEDrKiFkiNyAd2JqaippCX+I9PZBMgvYt9ivRgOfWexbIxKj0/NYW5khCUTVAAAAALA8CIgAAAAAYHkQEPmZ0NBQeuyxx8Q9wL41AvjMYt8aEXxusW/bAqJqAAAAAFgeZIgAAAAAYHkQEAEAAADA8iAgAgAAAIDlQUAEAAAAAMuDgMhLnnnmGRozZoxwsU5MTKQZM2bQ3r17XdaprKykWbNmUUJCAkVFRdHll19OOTk5LutkZGTQhRdeSBEREeJ1HnzwQaqtrXVZZ8WKFTRy5EjRLdGnTx/68MMPTf0B9tW+5f3KDuSNb9nZ2WRW1Nq3d999N40aNUp8JocPH97s79q2bRudccYZFBYWJtxsn3vuOTIzvtq3R44cafZzu27dOjIjauzXrVu30lVXXSU+h+Hh4TRgwAB65ZVXmvwuHGtnaLJvdX+s5dEdoP1MmzbN9sEHH9h27Nhh27Jli+2CCy6wpaWl2UpLS5V1br/9dlu3bt1sS5cutW3YsME2btw42+mnn648X1tbaxs8eLBtypQpts2bN9t++OEHW6dOnWxz585V1jl06JAtIiLCNnv2bNuuXbtsr776qi0wMNC2aNEi0759vtq3y5cv5/E1tr1799qysrKUW11dnc2sqLFvmbvuusv22muv2WbOnGkbNmxYk99TVFRkS0pKsl1zzTXid33yySe28PBw29tvv20zK77at4cPHxaf259//tnlc1tdXW0zI2rs1/fff992991321asWGE7ePCg7aOPPhKfRz6eSnCs3aLZvtX7sRYBkcrk5uaKN3zlypXi58LCQltwcLDt888/V9bZvXu3WGft2rXiZz5JBwQE2LKzs5V13nzzTVtMTIytqqpK/DxnzhzboEGDXH7XlVdeKQ4SVkGrfSu/pAUFBTar0p5968xjjz3W7En7jTfesMXHxyv7mnnooYds/fv3t1kFrfatDIg40Lci3u5XyZ///Gfb2WefrfyMY61Ns32r92MtSmYqU1RUJO47duwo7jdu3Eg1NTU0ZcoUZZ309HRKS0ujtWvXip/5fsiQIZSUlKSsM23aNDEwb+fOnco6zq8h15GvYQW02rcSLkskJyfTueeeS6tXryYr0Z596w687plnnkkhISEu+5/T8QUFBWQFtNq3kosvvliUkCZOnEjffvstWQW19iu/jnwNBsda0mzf6v1Yi4BIRerr6+nee++lCRMm0ODBg8VjXBvlk0FcXJzLunyClnVTvnc+Ycvn5XOtrcMn9oqKCjI7Wu5b/mK+9dZb9L///U/cuAY+adIk2rRpE1mB9u5bd3Bn/5sZLfct6zheeOEF+vzzz+n7778XARHraqwQFKm1X9esWUMLFiyg2267TXkM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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# select the annual (calendar-year) means into a plain DataFrame for plotting.\n", "# The statistics services return a GeoDataFrame carrying a site-point geometry,\n", @@ -668,24 +166,9 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:50.257904Z", - "iopub.status.busy": "2026-05-26T16:30:50.257793Z", - "iopub.status.idle": "2026-05-26T16:30:50.260020Z", - "shell.execute_reply": "2026-05-26T16:30:50.259541Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The query URL used to retrieve the data was: https://api.waterdata.usgs.gov/statistics/v0/observationIntervals?computation_type=arithmetic_mean&monitoring_location_id=USGS-02319394&page_size=1000¶meter_code=00060\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print(\"The query URL used to retrieve the data was: \" + x1[1].url)" ] @@ -708,419 +191,9 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:50.261583Z", - "iopub.status.busy": "2026-05-26T16:30:50.261459Z", - "iopub.status.idle": "2026-05-26T16:30:53.015857Z", - "shell.execute_reply": "2026-05-26T16:30:53.015375Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: observationIntervals · 1 page · 2,114 rows · 3,924/4,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
geometrymonitoring_location_idmonitoring_location_namesite_typesite_type_codecountry_codestate_codecounty_codestart_dateend_dateinterval_typevaluepercentilesample_countapproval_statuscomputation_idcomputationparameter_codeunit_of_measureparent_time_series_id
0POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-05-011942-05-31month584.032NaN31approved043dcd51-8a2f-48ca-bb20-20933651f633arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
1POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-06-011942-06-30month2571.333NaN30approved0a96c646-ca99-45de-b8ea-b85954d5e029arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
2POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-07-011942-07-31month400.742NaN31approved88991dcf-0994-4d18-be50-378146f193fearithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
3POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-08-011942-08-31month549.581NaN31approved64fd8688-07e2-4710-8cd4-57ee0e25ff66arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
4POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS450151942-09-011942-09-30month1338.467NaN30approvedc8b93535-460a-4dfd-91ab-a5ec4a16c827arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
...............................................................
2109POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792021-10-012022-09-30water_year20.784NaN351approved6bdfe85c-4494-4143-84ce-e30849f1e68barithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2110POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792022-10-012023-09-30water_year21.121NaN361approvedc4ec6635-83cf-4d54-9fd8-caed23b96a98arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2111POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792023-10-012024-09-30water_year20.383NaN342approvede533a96f-655a-4753-b960-0560b0be2d97arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2112POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792024-10-012025-09-30water_year20.928NaN348approved197d9815-4ecd-4b89-a01e-50e128e4dbd6arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
2113POINT (-83.18014 30.41049)USGS-02319394WITHLACOOCHEE RIVER NR LEE, FLAStreamSTUS120792025-10-012025-12-06water_year20.439NaN62approved6b6ce4c2-4ca8-4f73-86df-f3583f914dc0arithmetic_mean00010degCea8d8089780f48428cff4a5ba344f1aa
\n", - "

2114 rows × 20 columns

\n", - "
" - ], - "text/plain": [ - " geometry monitoring_location_id \\\n", - "0 POINT (-80.14136 33.45378) USGS-02171500 \n", - "1 POINT (-80.14136 33.45378) USGS-02171500 \n", - "2 POINT (-80.14136 33.45378) USGS-02171500 \n", - "3 POINT (-80.14136 33.45378) USGS-02171500 \n", - "4 POINT (-80.14136 33.45378) USGS-02171500 \n", - "... ... ... \n", - "2109 POINT (-83.18014 30.41049) USGS-02319394 \n", - "2110 POINT (-83.18014 30.41049) USGS-02319394 \n", - "2111 POINT (-83.18014 30.41049) USGS-02319394 \n", - "2112 POINT (-83.18014 30.41049) USGS-02319394 \n", - "2113 POINT (-83.18014 30.41049) USGS-02319394 \n", - "\n", - " monitoring_location_name site_type site_type_code country_code \\\n", - "0 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "1 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "2 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "3 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "4 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "... ... ... ... ... \n", - "2109 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "2110 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "2111 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "2112 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "2113 WITHLACOOCHEE RIVER NR LEE, FLA Stream ST US \n", - "\n", - " state_code county_code start_date end_date interval_type value \\\n", - "0 45 015 1942-05-01 1942-05-31 month 584.032 \n", - "1 45 015 1942-06-01 1942-06-30 month 2571.333 \n", - "2 45 015 1942-07-01 1942-07-31 month 400.742 \n", - "3 45 015 1942-08-01 1942-08-31 month 549.581 \n", - "4 45 015 1942-09-01 1942-09-30 month 1338.467 \n", - "... ... ... ... ... ... ... \n", - "2109 12 079 2021-10-01 2022-09-30 water_year 20.784 \n", - "2110 12 079 2022-10-01 2023-09-30 water_year 21.121 \n", - "2111 12 079 2023-10-01 2024-09-30 water_year 20.383 \n", - "2112 12 079 2024-10-01 2025-09-30 water_year 20.928 \n", - "2113 12 079 2025-10-01 2025-12-06 water_year 20.439 \n", - "\n", - " percentile sample_count approval_status \\\n", - "0 NaN 31 approved \n", - "1 NaN 30 approved \n", - "2 NaN 31 approved \n", - "3 NaN 31 approved \n", - "4 NaN 30 approved \n", - "... ... ... ... \n", - "2109 NaN 351 approved \n", - "2110 NaN 361 approved \n", - "2111 NaN 342 approved \n", - "2112 NaN 348 approved \n", - "2113 NaN 62 approved \n", - "\n", - " computation_id computation parameter_code \\\n", - "0 043dcd51-8a2f-48ca-bb20-20933651f633 arithmetic_mean 00060 \n", - "1 0a96c646-ca99-45de-b8ea-b85954d5e029 arithmetic_mean 00060 \n", - "2 88991dcf-0994-4d18-be50-378146f193fe arithmetic_mean 00060 \n", - "3 64fd8688-07e2-4710-8cd4-57ee0e25ff66 arithmetic_mean 00060 \n", - "4 c8b93535-460a-4dfd-91ab-a5ec4a16c827 arithmetic_mean 00060 \n", - "... ... ... ... \n", - "2109 6bdfe85c-4494-4143-84ce-e30849f1e68b arithmetic_mean 00010 \n", - "2110 c4ec6635-83cf-4d54-9fd8-caed23b96a98 arithmetic_mean 00010 \n", - "2111 e533a96f-655a-4753-b960-0560b0be2d97 arithmetic_mean 00010 \n", - "2112 197d9815-4ecd-4b89-a01e-50e128e4dbd6 arithmetic_mean 00010 \n", - "2113 6b6ce4c2-4ca8-4f73-86df-f3583f914dc0 arithmetic_mean 00010 \n", - "\n", - " unit_of_measure parent_time_series_id \n", - "0 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "1 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "2 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "3 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "4 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "... ... ... \n", - "2109 degC ea8d8089780f48428cff4a5ba344f1aa \n", - "2110 degC ea8d8089780f48428cff4a5ba344f1aa \n", - "2111 degC ea8d8089780f48428cff4a5ba344f1aa \n", - "2112 degC ea8d8089780f48428cff4a5ba344f1aa \n", - "2113 degC ea8d8089780f48428cff4a5ba344f1aa \n", - "\n", - "[2114 rows x 20 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "x2 = waterdata.get_stats_date_range(\n", " monitoring_location_id=[\"USGS-02319394\", \"USGS-02171500\"],\n", @@ -1141,407 +214,9 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:30:53.017565Z", - "iopub.status.busy": "2026-05-26T16:30:53.017426Z", - "iopub.status.idle": "2026-05-26T16:30:53.504048Z", - "shell.execute_reply": "2026-05-26T16:30:53.503553Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: observationNormals · 1 page · 756 rows · 3,923/4,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
geometrymonitoring_location_idmonitoring_location_namesite_typesite_type_codecountry_codestate_codecounty_codetime_of_yeartime_of_year_typevaluepercentilesample_countapproval_statuscomputation_idcomputationparameter_codeunit_of_measureparent_time_series_id
0POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-01day_of_year2046.695NaN82approvede7827589-ff6e-472d-9864-1bfe558b9639arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
1POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-02day_of_year2041.866NaN82approved9e8feb48-3652-4bca-82dc-c2f2a65650e5arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
2POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-03day_of_year2080.963NaN82approved35c0af68-80d2-4635-865c-bb224bfb5e9aarithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
3POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-04day_of_year2434.256NaN82approvedeacd3baa-e0ab-4c08-8334-bb3ed3b916c8arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
4POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501501-05day_of_year2597.819NaN83approved19f28744-96cb-4cfa-9d28-5f114b82fcc6arithmetic_mean00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
............................................................
751POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501508month_of_year563.050.084approvedb3fc0cec-1732-4bac-b153-a18bef567453median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
752POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501509month_of_year563.050.084approved8cd55012-5b16-42f7-8cb3-b234aa15f859median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
753POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501510month_of_year563.050.084approveda1b160db-bc3f-44e7-a150-881474373276median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
754POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501511month_of_year561.550.084approved48757820-8d5e-4407-bced-bca2717c6c97median00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
755POINT (-80.14136 33.45378)USGS-02171500SANTEE RIVER NEAR PINEVILLE, SCStreamSTUS4501512month_of_year553.550.084approved9373bc06-0df5-4a86-9590-dfadcdcb6efbmedian00060ft^3/s218ab832dc5a4c4b9fb1d51efa232d15
\n", - "

756 rows × 19 columns

\n", - "
" - ], - "text/plain": [ - " geometry monitoring_location_id \\\n", - "0 POINT (-80.14136 33.45378) USGS-02171500 \n", - "1 POINT (-80.14136 33.45378) USGS-02171500 \n", - "2 POINT (-80.14136 33.45378) USGS-02171500 \n", - "3 POINT (-80.14136 33.45378) USGS-02171500 \n", - "4 POINT (-80.14136 33.45378) USGS-02171500 \n", - ".. ... ... \n", - "751 POINT (-80.14136 33.45378) USGS-02171500 \n", - "752 POINT (-80.14136 33.45378) USGS-02171500 \n", - "753 POINT (-80.14136 33.45378) USGS-02171500 \n", - "754 POINT (-80.14136 33.45378) USGS-02171500 \n", - "755 POINT (-80.14136 33.45378) USGS-02171500 \n", - "\n", - " monitoring_location_name site_type site_type_code country_code \\\n", - "0 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "1 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "2 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "3 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "4 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - ".. ... ... ... ... \n", - "751 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "752 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "753 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "754 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "755 SANTEE RIVER NEAR PINEVILLE, SC Stream ST US \n", - "\n", - " state_code county_code time_of_year time_of_year_type value \\\n", - "0 45 015 01-01 day_of_year 2046.695 \n", - "1 45 015 01-02 day_of_year 2041.866 \n", - "2 45 015 01-03 day_of_year 2080.963 \n", - "3 45 015 01-04 day_of_year 2434.256 \n", - "4 45 015 01-05 day_of_year 2597.819 \n", - ".. ... ... ... ... ... \n", - "751 45 015 08 month_of_year 563.0 \n", - "752 45 015 09 month_of_year 563.0 \n", - "753 45 015 10 month_of_year 563.0 \n", - "754 45 015 11 month_of_year 561.5 \n", - "755 45 015 12 month_of_year 553.5 \n", - "\n", - " percentile sample_count approval_status \\\n", - "0 NaN 82 approved \n", - "1 NaN 82 approved \n", - "2 NaN 82 approved \n", - "3 NaN 82 approved \n", - "4 NaN 83 approved \n", - ".. ... ... ... \n", - "751 50.0 84 approved \n", - "752 50.0 84 approved \n", - "753 50.0 84 approved \n", - "754 50.0 84 approved \n", - "755 50.0 84 approved \n", - "\n", - " computation_id computation parameter_code \\\n", - "0 e7827589-ff6e-472d-9864-1bfe558b9639 arithmetic_mean 00060 \n", - "1 9e8feb48-3652-4bca-82dc-c2f2a65650e5 arithmetic_mean 00060 \n", - "2 35c0af68-80d2-4635-865c-bb224bfb5e9a arithmetic_mean 00060 \n", - "3 eacd3baa-e0ab-4c08-8334-bb3ed3b916c8 arithmetic_mean 00060 \n", - "4 19f28744-96cb-4cfa-9d28-5f114b82fcc6 arithmetic_mean 00060 \n", - ".. ... ... ... \n", - "751 b3fc0cec-1732-4bac-b153-a18bef567453 median 00060 \n", - "752 8cd55012-5b16-42f7-8cb3-b234aa15f859 median 00060 \n", - "753 a1b160db-bc3f-44e7-a150-881474373276 median 00060 \n", - "754 48757820-8d5e-4407-bced-bca2717c6c97 median 00060 \n", - "755 9373bc06-0df5-4a86-9590-dfadcdcb6efb median 00060 \n", - "\n", - " unit_of_measure parent_time_series_id \n", - "0 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "1 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "2 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "3 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "4 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - ".. ... ... \n", - "751 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "752 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "753 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "754 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "755 ft^3/s 218ab832dc5a4c4b9fb1d51efa232d15 \n", - "\n", - "[756 rows x 19 columns]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "x3 = waterdata.get_stats_por(\n", " monitoring_location_id=\"USGS-02171500\",\n", @@ -1567,8 +242,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb b/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb index 31e6edd9..9c3ff4ee 100644 --- a/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb +++ b/demos/hydroshare/USGS_dataretrieval_WaterUse_Examples.ipynb @@ -23,14 +23,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:43:41.521108Z", - "iopub.status.busy": "2026-05-26T17:43:41.520978Z", - "iopub.status.idle": "2026-05-26T17:43:45.257574Z", - "shell.execute_reply": "2026-05-26T17:43:45.256933Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "!pip install dataretrieval" @@ -45,15 +38,8 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:43:45.259977Z", - "iopub.status.busy": "2026-05-26T17:43:45.259818Z", - "iopub.status.idle": "2026-05-26T17:43:45.853921Z", - "shell.execute_reply": "2026-05-26T17:43:45.853378Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "from IPython.display import display\n", @@ -86,15 +72,8 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:43:45.856549Z", - "iopub.status.busy": "2026-05-26T17:43:45.856353Z", - "iopub.status.idle": "2026-05-26T17:43:45.858364Z", - "shell.execute_reply": "2026-05-26T17:43:45.857855Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# [Defunct] pennsylvania = nwis.get_water_use(state=\"PA\")\n", @@ -121,15 +100,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:43:45.860047Z", - "iopub.status.busy": "2026-05-26T17:43:45.859939Z", - "iopub.status.idle": "2026-05-26T17:43:45.861855Z", - "shell.execute_reply": "2026-05-26T17:43:45.861330Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# [Defunct] display(pennsylvania[0])" @@ -144,15 +116,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:43:45.863284Z", - "iopub.status.busy": "2026-05-26T17:43:45.863178Z", - "iopub.status.idle": "2026-05-26T17:43:45.864828Z", - "shell.execute_reply": "2026-05-26T17:43:45.864439Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# [Defunct] print(pennsylvania[0].dtypes)" @@ -169,15 +134,8 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:43:45.866292Z", - "iopub.status.busy": "2026-05-26T17:43:45.866192Z", - "iopub.status.idle": "2026-05-26T17:43:45.867944Z", - "shell.execute_reply": "2026-05-26T17:43:45.867459Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# [Defunct] ohio = nwis.get_water_use(years=[2000, 2005, 2010], state=\"OH\")\n", @@ -194,15 +152,8 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T17:43:45.869175Z", - "iopub.status.busy": "2026-05-26T17:43:45.869084Z", - "iopub.status.idle": "2026-05-26T17:43:45.871070Z", - "shell.execute_reply": "2026-05-26T17:43:45.870527Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Get water use data for livestock (LI) and irrigation (IT)\n", @@ -227,8 +178,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" + "pygments_lexer": "ipython3" } }, "nbformat": 4, diff --git a/demos/peak_streamflow_trends.ipynb b/demos/peak_streamflow_trends.ipynb index e1e72ad4..be3bf0cd 100644 --- a/demos/peak_streamflow_trends.ipynb +++ b/demos/peak_streamflow_trends.ipynb @@ -21,15 +21,8 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:39:18.814744Z", - "iopub.status.busy": "2026-05-26T16:39:18.814536Z", - "iopub.status.idle": "2026-05-26T16:39:44.634069Z", - "shell.execute_reply": "2026-05-26T16:39:44.633533Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -49,199 +42,9 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:39:44.636678Z", - "iopub.status.busy": "2026-05-26T16:39:44.636481Z", - "iopub.status.idle": "2026-05-26T16:39:45.186976Z", - "shell.execute_reply": "2026-05-26T16:39:45.186277Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\r", - "Retrieving: peaks · 1 page · 56 rows · 917/1,000 requests remaining" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
peak_idgeometrytime_series_idmonitoring_location_idparameter_codeunit_of_measurevaluelast_modifiedtimewater_yearyearmonthdaytime_of_daypeak_since
05282b66f-ee19-43d9-b772-936da66a968dPOINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s163002026-04-28 09:40:12.927288+00:001970-04-2019701970420NaNNone
1935a7d88-6e6c-4b80-b6a2-20b909d6e67fPOINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s89102026-04-28 09:40:12.927288+00:001971-02-051971197125NaNNone
269975ecd-959a-46bc-a221-815c947a76b6POINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s92402026-04-28 09:40:12.927288+00:001972-04-2219721972422NaNNone
355fc2bdd-fc45-44d5-b291-6767e5e44ff6POINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s166002026-04-28 09:40:12.927288+00:001973-04-2319731973423NaNNone
4c202f21a-a940-418f-92d6-253598a16dd0POINT (-87.59761 40.10108)1587ac4ee41d4f6b9debe9724e8ddb79USGS-0333900000060ft^3/s195002026-04-28 09:40:12.927288+00:001974-06-2319741974623NaNNone
\n", - "
" - ], - "text/plain": [ - " peak_id geometry \\\n", - "0 5282b66f-ee19-43d9-b772-936da66a968d POINT (-87.59761 40.10108) \n", - "1 935a7d88-6e6c-4b80-b6a2-20b909d6e67f POINT (-87.59761 40.10108) \n", - "2 69975ecd-959a-46bc-a221-815c947a76b6 POINT (-87.59761 40.10108) \n", - "3 55fc2bdd-fc45-44d5-b291-6767e5e44ff6 POINT (-87.59761 40.10108) \n", - "4 c202f21a-a940-418f-92d6-253598a16dd0 POINT (-87.59761 40.10108) \n", - "\n", - " time_series_id monitoring_location_id parameter_code \\\n", - "0 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", - "1 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", - "2 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", - "3 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", - "4 1587ac4ee41d4f6b9debe9724e8ddb79 USGS-03339000 00060 \n", - "\n", - " unit_of_measure value last_modified time \\\n", - "0 ft^3/s 16300 2026-04-28 09:40:12.927288+00:00 1970-04-20 \n", - "1 ft^3/s 8910 2026-04-28 09:40:12.927288+00:00 1971-02-05 \n", - "2 ft^3/s 9240 2026-04-28 09:40:12.927288+00:00 1972-04-22 \n", - "3 ft^3/s 16600 2026-04-28 09:40:12.927288+00:00 1973-04-23 \n", - "4 ft^3/s 19500 2026-04-28 09:40:12.927288+00:00 1974-06-23 \n", - "\n", - " water_year year month day time_of_day peak_since \n", - "0 1970 1970 4 20 NaN None \n", - "1 1971 1971 2 5 NaN None \n", - "2 1972 1972 4 22 NaN None \n", - "3 1973 1973 4 23 NaN None \n", - "4 1974 1974 6 23 NaN None " - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# download annual peaks (discharge, parameter 00060) from a single site\n", "df, md = waterdata.get_peaks(\n", @@ -269,15 +72,8 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:39:45.202721Z", - "iopub.status.busy": "2026-05-26T16:39:45.202576Z", - "iopub.status.idle": "2026-05-26T16:39:45.205354Z", - "shell.execute_reply": "2026-05-26T16:39:45.204876Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "def peak_trend_regression(df):\n", @@ -309,15 +105,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:39:45.207186Z", - "iopub.status.busy": "2026-05-26T16:39:45.207074Z", - "iopub.status.idle": "2026-05-26T16:39:45.210272Z", - "shell.execute_reply": "2026-05-26T16:39:45.209711Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "def peak_trend_analysis(state_names, start_date):\n", @@ -367,15 +156,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:39:45.211913Z", - "iopub.status.busy": "2026-05-26T16:39:45.211814Z", - "iopub.status.idle": "2026-05-26T16:39:45.213762Z", - "shell.execute_reply": "2026-05-26T16:39:45.213237Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Warning: these lines download a large dataset from the web and\n", @@ -396,196 +178,9 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:39:45.215365Z", - "iopub.status.busy": "2026-05-26T16:39:45.215266Z", - "iopub.status.idle": "2026-05-26T16:39:45.228631Z", - "shell.execute_reply": "2026-05-26T16:39:45.228151Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Unnamed: 0agency_cdsite_nostation_nmsite_tp_cddec_lat_vadec_long_vacoord_acy_cddec_coord_datum_cdalt_vaalt_acy_vaalt_datum_cdhuc_cdinterceptp_valueslopestd_error
00USGS2343275ABBIE CREEK NEAR ABBEVILLE ALST31.561835-85.204932UNAD83NaNNaNNaN3130004.0-0.6419110.0158990.0002310.000065
11USGS2360275JUDY CREEK NEAR OZARK ALST31.463224-85.572159UNAD83NaNNaNNaN3140201.0-0.7025690.0046520.0002690.000069
22USGS2360500EAST FORK CHOCTAWHATCHEE R NEAR MIDLAND CITY ALST31.373227-85.477158UNAD83179.10.01NGVD293140201.0-1.1385520.0076980.0002110.000003
33USGS2367400YELLOW RIVER NR SANFORD, ALAST31.317391-86.355788UNAD83NaNNaNNaN3140103.0-0.6113100.0158360.0005110.000013
44USGS2367500LIGHTWOOD KNOT CREEK AT BABBIE ALST31.270725-86.313564UNAD83185.00.01NGVD293140103.0-0.7056760.0152310.0002100.000052
\n", - "
" - ], - "text/plain": [ - " Unnamed: 0 agency_cd site_no \\\n", - "0 0 USGS 2343275 \n", - "1 1 USGS 2360275 \n", - "2 2 USGS 2360500 \n", - "3 3 USGS 2367400 \n", - "4 4 USGS 2367500 \n", - "\n", - " station_nm site_tp_cd dec_lat_va \\\n", - "0 ABBIE CREEK NEAR ABBEVILLE AL ST 31.561835 \n", - "1 JUDY CREEK NEAR OZARK AL ST 31.463224 \n", - "2 EAST FORK CHOCTAWHATCHEE R NEAR MIDLAND CITY AL ST 31.373227 \n", - "3 YELLOW RIVER NR SANFORD, ALA ST 31.317391 \n", - "4 LIGHTWOOD KNOT CREEK AT BABBIE AL ST 31.270725 \n", - "\n", - " dec_long_va coord_acy_cd dec_coord_datum_cd alt_va alt_acy_va \\\n", - "0 -85.204932 U NAD83 NaN NaN \n", - "1 -85.572159 U NAD83 NaN NaN \n", - "2 -85.477158 U NAD83 179.1 0.01 \n", - "3 -86.355788 U NAD83 NaN NaN \n", - "4 -86.313564 U NAD83 185.0 0.01 \n", - "\n", - " alt_datum_cd huc_cd intercept p_value slope std_error \n", - "0 NaN 3130004.0 -0.641911 0.015899 0.000231 0.000065 \n", - "1 NaN 3140201.0 -0.702569 0.004652 0.000269 0.000069 \n", - "2 NGVD29 3140201.0 -1.138552 0.007698 0.000211 0.000003 \n", - "3 NaN 3140103.0 -0.611310 0.015836 0.000511 0.000013 \n", - "4 NGVD29 3140103.0 -0.705676 0.015231 0.000210 0.000052 " - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "final_df = pd.read_csv(\"datasets/peak_discharge_trends.csv\")\n", "final_df.head()" @@ -608,15 +203,8 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2026-05-26T16:39:45.230088Z", - "iopub.status.busy": "2026-05-26T16:39:45.229993Z", - "iopub.status.idle": "2026-05-26T16:39:45.232274Z", - "shell.execute_reply": "2026-05-26T16:39:45.231890Z" - } - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Currently commented out as there isn't an easy way to install mpl_toolkits\n", @@ -678,8 +266,7 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" + "pygments_lexer": "ipython3" }, "vscode": { "interpreter": {