Skip to content

Commit 2aa8648

Browse files
thodson-usgsclaude
andcommitted
feat(waterdata): add named parameters for 20 returned columns
Comparing each collection's /schema with its getter's signature found 20 returned columns that could be passed only through **queryables, so their getters documented neither the column nor the filter: - get_field_measurements: control_condition, day, field_measurements_series_id, measurement_rated, month, reading_type, time_of_day, year - get_peaks: qualifier, time_of_day, value - get_monitoring_locations: revision_created, revision_modified, revision_note - get_combined_metadata: data_gap_interval, reading_type - get_time_series_metadata: data_gap_interval, parameter_description - get_field_measurements_metadata: reading_type - get_channel: channel_location_direction Each is now a named parameter, documented from the description the service publishes for it. day, month and year are typed as integers, as get_peaks already types them. The monitoring-location attributes that every collection accepts as filters but does not return stay in **queryables. Existing calls send the same request as before. A parametrized test checks that each one is sent in the request. It also covers #422's statistics_begin, which no other test sends. The properties lists of get_monitoring_locations and get_channel now name the columns they were missing (the three revision_* columns and channel_location_direction), and get_monitoring_locations joins the documented-columns monitor. get_channel stays out of it, because its list names the output column channel_measurements_id where the schema has id. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent e181c46 commit 2aa8648

5 files changed

Lines changed: 152 additions & 5 deletions

File tree

‎NEWS.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
**09/22/2026:** Twenty columns that the Water Data OGC collections return are now named, documented parameters of the getter that returns them; before, they could be passed only through `**queryables`. `get_field_measurements()`: `control_condition`, `day`, `field_measurements_series_id`, `measurement_rated`, `month`, `reading_type`, `time_of_day`, `year`. `get_peaks()`: `qualifier`, `time_of_day`, `value`. `get_monitoring_locations()`: `revision_created`, `revision_modified`, `revision_note`. `get_combined_metadata()`: `data_gap_interval`, `reading_type`. `get_time_series_metadata()`: `data_gap_interval`, `parameter_description`. `get_field_measurements_metadata()`: `reading_type`. `get_channel()`: `channel_location_direction`. Existing calls send the same request as before.
2+
13
**09/22/2026:** `waterdata.get_continuous()` accepts `method_category`, a column USGS added to the `continuous` collection in September 2026: the RLMS method category code (`STNRD`, `LMTUS`, `EXPER` or `UNKWN`) for the method in effect over an observation's interval. It is returned on every record and is null for time series that have not been categorized. It could already be passed through `**queryables`; it is now a documented parameter. `get_latest_continuous()` is unchanged, because `latest-continuous` does not have the field.
24

35
**09/22/2026:** The Water Data OGC getters now request **v1** of the Water Data APIs (`api.waterdata.usgs.gov/ogcapi/v1`), [released September 2026](https://waterdata.usgs.gov/blog/api-v1-release/); v0 stays online until June 2027. **New:** `WaterdataConfiguration(api_version="v0")`, or `api_version = "v0"` in the `[waterdata]` table of the configuration file, pins v0 during the transition. It changes only the version segment of the OGC path; Samples, Statistics and STAC are versioned separately and have no v1. **Behavior change:** `waterdata.get_time_series_metadata()` returns `begin` and `end` in UTC with a time zone, and no longer returns `begin_utc`, `end_utc`, `state_name` or `hydrologic_unit_code`. **Deprecation:** passing `begin_utc`, `end_utc`, `state`, `state_name` or `hydrologic_unit_code` to that getter, as a filter or in `properties`, emits a `DeprecationWarning` and sends the call to v0; this may be removed on or after 2027-06-01. Use `begin`, `end`, and `get_combined_metadata()` instead. **Behavior change:** `waterdata.get_field_measurements()` returns `time` as a date rather than a datetime, parsed to a tz-naive midnight timestamp as `get_daily()` already does; the time of day is in `time_of_day`. The `field-measurements-metadata` collection has no `time` field and is unaffected.

‎dataretrieval/waterdata/measurements.py‎

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ def get_field_measurements(
2626
monitoring_location_id: str | Iterable[str] | None = None,
2727
parameter_code: str | Iterable[str] | None = None,
2828
observing_procedure_code: str | Iterable[str] | None = None,
29+
control_condition: str | Iterable[str] | None = None,
30+
day: int | list[int] | None = None,
31+
field_measurements_series_id: str | Iterable[str] | None = None,
32+
measurement_rated: str | Iterable[str] | None = None,
33+
month: int | list[int] | None = None,
34+
reading_type: str | Iterable[str] | None = None,
35+
time_of_day: str | Iterable[str] | None = None,
36+
year: int | list[int] | None = None,
2937
properties: str | Iterable[str] | None = None,
3038
field_visit_id: str | Iterable[str] | None = None,
3139
approval_status: str | Iterable[str] | None = None,
@@ -69,6 +77,29 @@ def get_field_measurements(
6977
observing_procedure_code : string or iterable of strings, optional
7078
A short code corresponding to the observing procedure for the field
7179
measurement.
80+
control_condition : string or iterable of strings, optional
81+
The state of the control feature at the time of observation.
82+
day : integer or list of integers, optional
83+
The day of the month the field measurement was taken. If null, the day is
84+
unknown.
85+
field_measurements_series_id : string or iterable of strings, optional
86+
A unique identifier representing a single collection series, corresponding to
87+
the id field in the field-measurements-metadata endpoint. A collection series
88+
is the set of field measurements at one monitoring location for a single
89+
parameter code using a single reading type.
90+
measurement_rated : string or iterable of strings, optional
91+
A qualitative estimate of the quality of a measurement.
92+
month : integer or list of integers, optional
93+
The calendar month the field measurement was taken. If null, the month is
94+
unknown.
95+
reading_type : string or iterable of strings, optional
96+
Distinguishes field-measurement readings from measurements. Readings have a
97+
value of ReferencePrimary; measurements are Discharge or MeanGageHeight.
98+
time_of_day : string or iterable of strings, optional
99+
The time of day the field measurement was taken. If null, the time of day is
100+
unknown. The time column holds only the date.
101+
year : integer or list of integers, optional
102+
The calendar year the field measurement was taken.
72103
properties : string or iterable of strings, optional
73104
The columns to return from the query. See the
74105
field-measurements schema in the OpenAPI reference for the available
@@ -240,6 +271,9 @@ def get_peaks(
240271
month: int | list[int] | None = None,
241272
day: int | list[int] | None = None,
242273
peak_since: int | list[int] | None = None,
274+
qualifier: str | Iterable[str] | None = None,
275+
time_of_day: str | Iterable[str] | None = None,
276+
value: str | Iterable[str] | None = None,
243277
properties: str | Iterable[str] | None = None,
244278
skip_geometry: bool | None = None,
245279
bbox: list[float] | None = None,
@@ -288,6 +322,15 @@ def get_peaks(
288322
peak_since : int or list of ints, optional
289323
Filter on the year since which the peak value has been the record (the API
290324
serves this field as an integer; many rows are ``null``).
325+
qualifier : string or iterable of strings, optional
326+
Any qualifiers associated with a peak, for instance whether a sensor may have
327+
been impacted by ice or whether the value was estimated.
328+
time_of_day : string or iterable of strings, optional
329+
The time of day a peak occurred. If null, the time of day is unknown, as is
330+
common for historical peaks recorded only to the day.
331+
value : string or iterable of strings, optional
332+
The value of the peak. Values are transmitted as strings in the JSON response
333+
to preserve precision.
291334
properties : string or iterable of strings, optional
292335
Subset of columns to return. Defaults to every available property.
293336
skip_geometry : boolean, optional
@@ -393,6 +436,7 @@ def get_channel(
393436
measurement_type: str | Iterable[str] | None = None,
394437
last_modified: str | Iterable[str] | None = None,
395438
channel_measurement_type: str | Iterable[str] | None = None,
439+
channel_location_direction: str | Iterable[str] | None = None,
396440
properties: str | Iterable[str] | None = None,
397441
skip_geometry: bool | None = None,
398442
bbox: list[float] | None = None,
@@ -494,6 +538,8 @@ def get_channel(
494538
Water Data APIs use camelCase "skipGeometry" in CQL2 queries.
495539
channel_measurement_type : string or iterable of strings, optional
496540
The channel measurement type.
541+
channel_location_direction : string or iterable of strings, optional
542+
Location of the measurement from the gage.
497543
properties : string or iterable of strings, optional
498544
The columns to return from the query. Available
499545
options are: geometry, channel_measurements_id, monitoring_location_id,
@@ -503,8 +549,8 @@ def get_channel(
503549
channel_location_distance, channel_location_distance_unit, channel_stability,
504550
channel_material, channel_evenness, horizontal_velocity_description,
505551
vertical_velocity_description, longitudinal_velocity_description,
506-
measurement_type, last_modified, channel_measurement_type. The default
507-
(None) returns all columns.
552+
measurement_type, last_modified, channel_measurement_type,
553+
channel_location_direction. The default (None) returns all columns.
508554
bbox : list of numbers, optional
509555
Only features whose geometry intersects the bounding box are selected.
510556
The bounding box is provided as four or six numbers, depending on

‎dataretrieval/waterdata/metadata.py‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ def get_monitoring_locations(
6767
well_constructed_depth: str | Iterable[str] | None = None,
6868
hole_constructed_depth: str | Iterable[str] | None = None,
6969
depth_source_code: str | Iterable[str] | None = None,
70+
revision_created: str | Iterable[str] | None = None,
71+
revision_modified: str | Iterable[str] | None = None,
72+
revision_note: str | Iterable[str] | None = None,
7073
properties: str | Iterable[str] | None = None,
7174
skip_geometry: bool | None = None,
7275
bbox: list[float] | None = None,
@@ -250,6 +253,14 @@ def get_monitoring_locations(
250253
A code indicating the source of water-level data. A `list of
251254
codes <https://help.waterdata.usgs.gov/code/water_level_src_cd_query?fmt=html>`_
252255
is available.
256+
revision_created : string or iterable of strings, optional
257+
The date a revision statement was created.
258+
revision_modified : string or iterable of strings, optional
259+
The most recent date a revision statement was modified.
260+
revision_note : string or iterable of strings, optional
261+
Text explaining a revision to this location's approved data. Revisions are
262+
also flagged by revision qualifier codes in the data. Explanations from
263+
before 2017 may not be online but can be requested.
253264
properties : string or iterable of strings, optional
254265
The columns to return from the query. Available
255266
options are: geometry, id, agency_code, agency_name,
@@ -265,7 +276,8 @@ def get_monitoring_locations(
265276
contributing_drainage_area, time_zone_abbreviation,
266277
uses_daylight_savings, construction_date, aquifer_code,
267278
national_aquifer_code, aquifer_type_code, well_constructed_depth,
268-
hole_constructed_depth, depth_source_code.
279+
hole_constructed_depth, depth_source_code, revision_created,
280+
revision_modified, revision_note.
269281
bbox : list of numbers, optional
270282
Only features whose geometry intersects the bounding box are selected.
271283
The bounding box is provided as four or six numbers, depending on
@@ -391,6 +403,8 @@ def get_time_series_metadata(
391403
monitoring_location_id: str | Iterable[str] | None = None,
392404
parameter_code: str | Iterable[str] | None = None,
393405
parameter_name: str | Iterable[str] | None = None,
406+
data_gap_interval: str | Iterable[str] | None = None,
407+
parameter_description: str | Iterable[str] | None = None,
394408
properties: str | Iterable[str] | None = None,
395409
statistic_id: str | Iterable[str] | None = None,
396410
hydrologic_unit_code: str | Iterable[str] | None = None,
@@ -444,6 +458,12 @@ def get_time_series_metadata(
444458
available at https://help.waterdata.usgs.gov/codes-and-parameters/parameters.
445459
parameter_name : string or iterable of strings, optional
446460
A human-understandable name corresponding to parameter_code.
461+
data_gap_interval : string or iterable of strings, optional
462+
The time interval threshold used for gap detection in the time series, as an
463+
ISO 8601 duration.
464+
parameter_description : string or iterable of strings, optional
465+
A description of what the parameter code represents, as used by WDFN and other
466+
USGS data dissemination products.
447467
properties : string or iterable of strings, optional
448468
The columns to return from the query.
449469
Available options are: begin, computation_identifier,
@@ -701,6 +721,8 @@ def get_combined_metadata(
701721
well_constructed_depth: str | Iterable[str] | None = None,
702722
hole_constructed_depth: str | Iterable[str] | None = None,
703723
depth_source_code: str | Iterable[str] | None = None,
724+
data_gap_interval: str | Iterable[str] | None = None,
725+
reading_type: str | Iterable[str] | None = None,
704726
properties: str | Iterable[str] | None = None,
705727
skip_geometry: bool | None = None,
706728
bbox: list[float] | None = None,
@@ -791,6 +813,12 @@ def get_combined_metadata(
791813
altitude, vertical/horizontal datum, drainage area, aquifer,
792814
well construction, …); see :func:`get_monitoring_locations` for
793815
descriptions of each.
816+
data_gap_interval : string or iterable of strings, optional
817+
The time interval threshold used for gap detection in the time series, as an
818+
ISO 8601 duration.
819+
reading_type : string or iterable of strings, optional
820+
Distinguishes field-measurement readings from measurements. Readings have a
821+
value of ReferencePrimary; measurements are Discharge or MeanGageHeight.
794822
properties : string or iterable of strings, optional
795823
Subset of columns to return. Defaults to every available
796824
property.
@@ -910,6 +938,7 @@ def get_field_measurements_metadata(
910938
begin: str | Iterable[str] | None = None,
911939
end: str | Iterable[str] | None = None,
912940
last_modified: str | Iterable[str] | None = None,
941+
reading_type: str | Iterable[str] | None = None,
913942
properties: str | Iterable[str] | None = None,
914943
skip_geometry: bool | None = None,
915944
bbox: list[float] | None = None,
@@ -952,6 +981,9 @@ def get_field_measurements_metadata(
952981
interval (``"start/end"``, optionally half-bounded with ``..``),
953982
or an ISO 8601 duration (e.g. ``"P1M"``, ``"PT36H"``). See
954983
:func:`get_time_series_metadata` for the full grammar.
984+
reading_type : string or iterable of strings, optional
985+
Distinguishes field-measurement readings from measurements. Readings have a
986+
value of ReferencePrimary; measurements are Discharge or MeanGageHeight.
955987
properties : string or iterable of strings, optional
956988
Subset of columns to return. Defaults to every available property.
957989
skip_geometry : boolean, optional

‎tests/waterdata_properties_test.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818

1919
#: Getters that list their returned columns, by collection. Written out rather
2020
#: than discovered, so a getter that loses its list fails instead of being
21-
#: skipped. get_channel and get_monitoring_locations also list columns but are
22-
#: left out until their lists match the schema.
21+
#: skipped. get_channel is left out: its list names the output column
22+
#: channel_measurements_id where the schema has id.
2323
_DOCUMENTED = {
2424
"daily": waterdata.get_daily,
2525
"continuous": waterdata.get_continuous,
2626
"latest-continuous": waterdata.get_latest_continuous,
2727
"latest-daily": waterdata.get_latest_daily,
28+
"monitoring-locations": waterdata.get_monitoring_locations,
2829
"time-series-metadata": waterdata.get_time_series_metadata,
2930
}
3031

‎tests/waterdata_test.py‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,72 @@ def test_v0_routing_does_not_write_on_the_callers_configuration(httpx_mock):
12401240
assert other.startswith(f"{_OGC_BASE}/collections/daily")
12411241

12421242

1243+
#: Named parameters for returned columns, by getter and collection, with a value
1244+
#: to send.
1245+
_NEWLY_NAMED = {
1246+
(get_channel, "channel-measurements"): {
1247+
"channel_location_direction": "left bank",
1248+
},
1249+
(get_field_measurements, "field-measurements"): {
1250+
"control_condition": "Clear",
1251+
"day": 5,
1252+
"field_measurements_series_id": "abc123",
1253+
"measurement_rated": "Good",
1254+
"month": 3,
1255+
"reading_type": "Discharge",
1256+
"time_of_day": "17:30:00",
1257+
"year": 2020,
1258+
},
1259+
(get_peaks, "peaks"): {
1260+
"qualifier": "Bd",
1261+
"time_of_day": "17:30:00",
1262+
"value": "847000",
1263+
},
1264+
(get_combined_metadata, "combined-metadata"): {
1265+
"data_gap_interval": "P1D",
1266+
"reading_type": "Discharge",
1267+
},
1268+
(get_monitoring_locations, "monitoring-locations"): {
1269+
"revision_created": "2024-01-01",
1270+
"revision_modified": "2024-01-01",
1271+
"revision_note": "corrected",
1272+
},
1273+
(get_time_series_metadata, "time-series-metadata"): {
1274+
"data_gap_interval": "P1D",
1275+
"parameter_description": "Discharge",
1276+
"statistics_begin": "2000",
1277+
},
1278+
(get_field_measurements_metadata, "field-measurements-metadata"): {
1279+
"reading_type": "Discharge",
1280+
},
1281+
}
1282+
1283+
1284+
@pytest.mark.parametrize(
1285+
("getter", "collection", "parameter", "value"),
1286+
[
1287+
pytest.param(
1288+
getter, collection, parameter, value, id=f"{getter.__name__}-{parameter}"
1289+
)
1290+
for (getter, collection), params in _NEWLY_NAMED.items()
1291+
for parameter, value in params.items()
1292+
],
1293+
)
1294+
def test_newly_named_columns_reach_the_request(
1295+
httpx_mock, getter, collection, parameter, value
1296+
):
1297+
"""A newly named parameter is sent to the service.
1298+
1299+
A call whose parameter is accepted but not forwarded returns unfiltered
1300+
results instead of failing.
1301+
"""
1302+
_mock_items(httpx_mock, collection)
1303+
1304+
getter(monitoring_location_id="USGS-05427718", **{parameter: value})
1305+
1306+
assert _sent(httpx_mock, collection)[0][parameter] == [str(value)]
1307+
1308+
12431309
def test_get_combined_metadata(httpx_mock):
12441310
_mock_items(httpx_mock, "combined-metadata")
12451311

0 commit comments

Comments
 (0)