Skip to content

Commit 5b3766a

Browse files
authored
Remove Defunct NWIS Functions (#222)
Remove defunct nwis functions (recreation of PR #216)
1 parent 2c775e4 commit 5b3766a

23 files changed

Lines changed: 322 additions & 2530 deletions

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66

77
## Latest Announcements
88

9-
:mega: **01/16/2025:** `dataretrieval` now features the `waterdata` module,
9+
**02/24/2026** The `get_gwlevels`, `get_discharge_measurements` functions in the `nwis` module are defunct and have been replaced with the `get_field_measurements` function in the `waterdata` module. The `get_pmcodes` function in the `nwis` module has been replaced with the `get_reference_table(collection='parameter_code)` function. Finally, the `get_water_use` function in the `nwis` module is defunct with no current replacement.
10+
11+
:mega: **01/16/2026:** `dataretrieval` now features the `waterdata` module,
1012
which provides access to USGS's modernized [Water Data
1113
APIs](https://api.waterdata.usgs.gov/). The Water Data API endpoints include
12-
daily values, **instantaneous values**, field measurements, time series metadata,
14+
daily values, instantaneous values, field measurements, time series metadata, statistics,
1315
and discrete water quality data from the [Samples database](https://waterdata.usgs.gov/download-samples/#dataProfile=site). This new module replaces the `nwis` module, which provides access to the legacy [NWIS
1416
Water Services](https://waterservices.usgs.gov/). Take a look at the new [`waterdata` module demo notebook](demos/WaterData_demo.ipynb), which walks through an extended example using a majority of the available `waterdata` functions.
1517

@@ -136,7 +138,7 @@ To log messages to a file, you can specify a filename in the
136138
logging.basicConfig(filename='waterdata.log', level=logging.INFO)
137139
```
138140

139-
### NWIS Legacy Services (Deprecated but still functional)
141+
### Legacy NWIS Services (Deprecated but still functional)
140142

141143
The `nwis` module accesses legacy NWIS Water Services:
142144

@@ -219,6 +221,7 @@ print(f"Found {len(flowlines)} upstream tributaries within 50km")
219221
- **Time series metadata**: Information about available data parameters
220222
- **Latest daily values**: Most recent daily statistical summary data
221223
- **Latest instantaneous values**: Most recent high-frequency continuous data
224+
- **Daily, monthly, and annual statistics**: Median, maximum, minimum, arithmetic mean, and percentile statistics
222225
- **Samples data**: Discrete USGS water quality data
223226

224227
### Legacy NWIS Services (Deprecated)
@@ -227,7 +230,6 @@ print(f"Found {len(flowlines)} upstream tributaries within 50km")
227230
- **Site info (site)**: Basic site information
228231
- **Statistics (stat)**: Statistical summaries
229232
- **Discharge peaks (peaks)**: Annual peak discharge events
230-
- **Discharge measurements (measurements)**: Direct flow measurements
231233

232234
### Water Quality Portal
233235
- **Results**: Water quality analytical results from USGS, EPA, and other agencies

dataretrieval/nwis.py

Lines changed: 22 additions & 262 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import pandas as pd
1313
import requests
1414

15-
from dataretrieval.utils import BaseMetadata, format_datetime
15+
from dataretrieval.utils import BaseMetadata
1616

1717
from .utils import query
1818

@@ -35,12 +35,9 @@
3535
PARAMCODES_URL = "https://help.waterdata.usgs.gov/code/parameter_cd_nm_query?"
3636
ALLPARAMCODES_URL = "https://help.waterdata.usgs.gov/code/parameter_cd_query?"
3737

38-
WATERSERVICES_SERVICES = ["dv", "iv", "site", "stat", "gwlevels"]
38+
WATERSERVICES_SERVICES = ["dv", "iv", "site", "stat"]
3939
WATERDATA_SERVICES = [
40-
"measurements",
4140
"peaks",
42-
"pmcodes",
43-
"water_use",
4441
"ratings",
4542
]
4643
# NAD83
@@ -133,7 +130,7 @@ def get_qwdata(
133130
**kwargs,
134131
) -> tuple[pd.DataFrame, BaseMetadata]:
135132
"""
136-
Get water sample data from qwdata service - deprecated, use `get_samples()`
133+
This function is defunct, use `get_samples()`
137134
in the waterdata module.
138135
139136
"""
@@ -150,54 +147,14 @@ def get_discharge_measurements(
150147
**kwargs,
151148
) -> tuple[pd.DataFrame, BaseMetadata]:
152149
"""
153-
Get discharge measurements from the waterdata service.
154-
155-
Parameters
156-
----------
157-
sites: string or list of strings, optional, default is None
158-
start: string, optional, default is None
159-
Supply date in the format: YYYY-MM-DD
160-
end: string, optional, default is None
161-
Supply date in the format: YYYY-MM-DD
162-
ssl_check: bool, optional
163-
If True, check SSL certificates, if False, do not check SSL,
164-
default is True
165-
**kwargs: optional
166-
If supplied, will be used as query parameters
167-
168-
Returns
169-
-------
170-
df: ``pandas.DataFrame``
171-
Times series data from the NWIS JSON
172-
md: :obj:`dataretrieval.utils.Metadata`
173-
A custom metadata object
174-
175-
Examples
176-
--------
177-
.. doctest::
178-
179-
>>> # Get discharge measurements for site 05114000
180-
>>> df, md = dataretrieval.nwis.get_discharge_measurements(
181-
... sites="05114000", start="2000-01-01", end="2000-01-30"
182-
... )
183-
184-
>>> # Get discharge measurements for sites in Alaska
185-
>>> df, md = dataretrieval.nwis.get_discharge_measurements(
186-
... start="2012-01-09", end="2012-01-10", stateCd="AK"
187-
... )
150+
This function is defunct, use `get_field_measurements()`
151+
in the waterdata module.
188152
189153
"""
190-
_check_sites_value_types(sites)
191-
192-
kwargs["site_no"] = kwargs.pop("site_no", sites)
193-
kwargs["begin_date"] = kwargs.pop("begin_date", start)
194-
kwargs["end_date"] = kwargs.pop("end_date", end)
195-
196-
if "format" not in kwargs:
197-
kwargs["format"] = "rdb"
198-
199-
response = query_waterdata("measurements", ssl_check=ssl_check, **kwargs)
200-
return _read_rdb(response.text), NWIS_Metadata(response, **kwargs)
154+
raise NameError(
155+
"`nwis.get_discharge_measurements` has been replaced "
156+
"with `waterdata.get_field_measurements`."
157+
)
201158

202159

203160
def get_discharge_peaks(
@@ -279,88 +236,15 @@ def get_gwlevels(
279236
**kwargs,
280237
) -> tuple[pd.DataFrame, BaseMetadata]:
281238
"""
282-
Queries the groundwater level service from waterservices
283-
284-
Parameters
285-
----------
286-
sites: string or list of strings, optional, default is None
287-
If the waterdata parameter site_no is supplied, it will overwrite the
288-
sites parameter
289-
start: string, optional, default is '1851-01-01'
290-
If the waterdata parameter begin_date is supplied, it will overwrite
291-
the start parameter
292-
end: string, optional, default is None
293-
If the waterdata parameter end_date is supplied, it will overwrite the
294-
end parameter (YYYY-MM-DD)
295-
multi_index: bool, optional
296-
If False, a dataframe with a single-level index (datetime) is returned,
297-
default is True
298-
datetime_index : bool, optional
299-
If True, create a datetime index, default is True
300-
ssl_check: bool, optional
301-
If True, check SSL certificates, if False, do not check SSL,
302-
default is True
303-
**kwargs: optional
304-
If supplied, will be used as query parameters
305-
306-
Returns
307-
-------
308-
df: ``pandas.DataFrame``
309-
Times series data from the NWIS JSON
310-
md: :obj:`dataretrieval.utils.Metadata`
311-
A custom metadata object
312-
313-
Examples
314-
--------
315-
.. doctest::
316-
317-
>>> # Get groundwater levels for site 434400121275801
318-
>>> df, md = dataretrieval.nwis.get_gwlevels(sites="434400121275801")
239+
This function is defunct, use `get_field_measurements()`
240+
in the waterdata module.
319241
320242
"""
321-
_check_sites_value_types(sites)
322-
323-
kwargs["startDT"] = kwargs.pop("startDT", start)
324-
kwargs["endDT"] = kwargs.pop("endDT", end)
325-
kwargs["sites"] = kwargs.pop("sites", sites)
326-
kwargs["multi_index"] = multi_index
327-
328-
response = query_waterservices(
329-
"gwlevels", format="rdb", ssl_check=ssl_check, **kwargs
243+
raise NameError(
244+
"`nwis.get_gwlevels` has been replaced "
245+
"with `waterdata.get_field_measurements()`."
330246
)
331247

332-
df = _read_rdb(response.text)
333-
334-
if datetime_index is True and "lev_tz_cd" in df.columns:
335-
df = format_datetime(df, "lev_dt", "lev_tm", "lev_tz_cd")
336-
elif datetime_index is True and "lev_dt" in df.columns and "lev_tm" in df.columns:
337-
# Fallback when lev_tz_cd is missing (e.g. some modern services)
338-
if "tz_cd" in df.columns:
339-
df = format_datetime(df, "lev_dt", "lev_tm", "tz_cd")
340-
else:
341-
df["datetime"] = pd.to_datetime(
342-
df["lev_dt"] + " " + df["lev_tm"], format="mixed", utc=True
343-
)
344-
345-
# Filter by kwarg parameterCd because the service doesn't do it
346-
if "parameterCd" in kwargs:
347-
pcodes = kwargs["parameterCd"]
348-
if isinstance(pcodes, str):
349-
pcodes = [pcodes]
350-
if "parameter_cd" in df.columns:
351-
df = df[df["parameter_cd"].isin(pcodes)]
352-
elif len(pcodes) == 1:
353-
# If the column is missing (modern service) but we requested one pcode,
354-
# we can safely add it to the dataframe for backward compatibility.
355-
df["parameter_cd"] = pcodes[0]
356-
# No need to filter since we just added it as the only value.
357-
else:
358-
# Multiple pcodes requested but only one returned (or none)
359-
# Add the column but don't fill it if we can't be sure
360-
df["parameter_cd"] = pd.NA
361-
362-
return format_response(df, **kwargs), NWIS_Metadata(response, **kwargs)
363-
364248

365249
def get_stats(
366250
sites: list[str] | str | None = None, ssl_check: bool = True, **kwargs
@@ -793,77 +677,14 @@ def get_pmcodes(
793677
ssl_check: bool = True,
794678
) -> tuple[pd.DataFrame, BaseMetadata]:
795679
"""
796-
Return a ``pandas.DataFrame`` containing all NWIS parameter codes.
797-
798-
Parameters
799-
----------
800-
parameterCd: string or list of strings, default is 'All'
801-
Accepts parameter codes or names
802-
partial: bool, optional
803-
Default is True (partial querying). If False, the function will query
804-
only exact matches, default is True
805-
ssl_check: bool, optional
806-
If True, check SSL certificates, if False, do not check SSL,
807-
default is True
808-
809-
Returns
810-
-------
811-
df: ``pandas.DataFrame``
812-
Data retrieved from the NWIS web service.
813-
md: :obj:`dataretrieval.utils.Metadata`
814-
A custom metadata object
815-
816-
Examples
817-
--------
818-
.. doctest::
819-
820-
>>> # Get information about the '00060' pcode
821-
>>> df, md = dataretrieval.nwis.get_pmcodes(
822-
... parameterCd="00060", partial=False
823-
... )
824-
825-
>>> # Get information about all 'Discharge' pcodes
826-
>>> df, md = dataretrieval.nwis.get_pmcodes(
827-
... parameterCd="Discharge", partial=True
828-
... )
680+
This function is defunct, use
681+
`get_reference_table(collection="parameter-codes")`.
829682
830683
"""
831-
832-
payload = {"fmt": "rdb"}
833-
url = PARAMCODES_URL
834-
835-
if isinstance(parameterCd, str): # when a single code or name is given
836-
if parameterCd.lower() == "all":
837-
payload.update({"group_cd": "%"})
838-
url = ALLPARAMCODES_URL
839-
response = query(url, payload, ssl_check=ssl_check)
840-
return _read_rdb(response.text), NWIS_Metadata(response)
841-
842-
else:
843-
parameterCd = [parameterCd]
844-
845-
if not isinstance(parameterCd, list):
846-
raise TypeError(
847-
"Parameter information (code or name) must be type string or list"
848-
)
849-
850-
# Querying with a list of parameters names, codes, or mixed
851-
return_list = []
852-
for param in parameterCd:
853-
if isinstance(param, str):
854-
if partial:
855-
param = f"%{param}%"
856-
payload.update({"parm_nm_cd": param})
857-
response = query(url, payload, ssl_check=ssl_check)
858-
if len(response.text.splitlines()) < 10: # empty query
859-
raise TypeError(
860-
"One of the parameter codes or names entered does not"
861-
"return any information, please try a different value"
862-
)
863-
return_list.append(_read_rdb(response.text))
864-
else:
865-
raise TypeError("Parameter information (code or name) must be type string")
866-
return pd.concat(return_list), NWIS_Metadata(response)
684+
raise NameError(
685+
"`nwis.get_pmcodes` has been replaced "
686+
"with `get_reference_table(collection='parameter-codes')`."
687+
)
867688

868689

869690
def get_water_use(
@@ -874,71 +695,10 @@ def get_water_use(
874695
ssl_check: bool = True,
875696
) -> tuple[pd.DataFrame, BaseMetadata]:
876697
"""
877-
Water use data retrieval from USGS (NWIS).
878-
879-
Parameters
880-
----------
881-
years: string or list of strings
882-
List or comma delimited string of years. Must be years ending in 0 or
883-
5, or "ALL", which retrieves all available years, default is "ALL"
884-
state: string, optional, default is None
885-
full name, abbreviation or id
886-
counties: string or list of strings
887-
County IDs from county lookup or "ALL", default is "ALL"
888-
categories: string or list of strings
889-
List or comma delimited string of Two-letter category abbreviations,
890-
default is "ALL"
891-
ssl_check: bool, optional
892-
If True, check SSL certificates, if False, do not check SSL,
893-
default is True
894-
895-
Returns
896-
-------
897-
df: ``pandas.DataFrame``
898-
Data from NWIS
899-
md: :obj:`dataretrieval.utils.Metadata`
900-
A custom metadata object
901-
902-
Examples
903-
--------
904-
.. doctest::
905-
906-
>>> # Get total population for RI from the NWIS water use service
907-
>>> df, md = dataretrieval.nwis.get_water_use(
908-
... years="2000", state="RI", categories="TP"
909-
... )
910-
911-
>>> # Get the national total water use for livestock in Bgal/day
912-
>>> df, md = dataretrieval.nwis.get_water_use(years="2010", categories="L")
913-
914-
>>> # Get 2005 domestic water use for Apache County in Arizona
915-
>>> df, md = dataretrieval.nwis.get_water_use(
916-
... years="2005", state="Arizona", counties="001", categories="DO"
917-
... )
698+
This function is defunct and currently has no replacement.
918699
919700
"""
920-
if years and not isinstance(years, list) and not isinstance(years, str):
921-
raise TypeError("years must be a string or a list of strings")
922-
923-
if counties and not isinstance(counties, list) and not isinstance(counties, str):
924-
raise TypeError("counties must be a string or a list of strings")
925-
926-
if categories and not isinstance(categories, (list, str)):
927-
raise TypeError("categories must be a string or a list of strings")
928-
929-
payload = {
930-
"rdb_compression": "value",
931-
"format": "rdb",
932-
"wu_year": years,
933-
"wu_category": categories,
934-
"wu_county": counties,
935-
}
936-
url = WATERDATA_URL + "water_use"
937-
if state is not None:
938-
url = WATERDATA_BASE_URL + state + "/nwis/water_use"
939-
payload.update({"wu_area": "county"})
940-
response = query(url, payload, ssl_check=ssl_check)
941-
return _read_rdb(response.text), NWIS_Metadata(response)
701+
raise NameError("`nwis.get_water_use` is defunct.")
942702

943703

944704
def get_ratings(

0 commit comments

Comments
 (0)