Skip to content

Commit 272fbff

Browse files
authored
Add Peaks and Ratings (#5)
* Using new /peaks endpoint (first iteration) * Add test with 5-minute data * Use Consistent UTC for USGS data import. Fix progress bar for USGS imports
1 parent 6d75923 commit 272fbff

13 files changed

Lines changed: 355 additions & 685 deletions

File tree

Readme.md

Lines changed: 63 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# usgs-water-api
22

3-
A Java library for retrieving hydrologic data (daily values, peaks, continous (15-minute), time-series metadata, monitoring locations) from the USGS Water Data API.
3+
A Java library for retrieving hydrologic data (daily values, peaks, continous (~15-minute), time-series metadata, monitoring locations) from the USGS Water Data API.
44

55

66
```gradle
@@ -63,6 +63,31 @@ implementation("org.opendcs:usgs-waterdata-api:0.3.*")
6363

6464
eastfenderTS.printToConsole(5);
6565

66+
// Read annual peak flow and stage (one value per water year)
67+
System.out.println("\nRead Annual Peaks, Boise River near Featherville");
68+
var peakMetadata = UsgsWaterDataApi.getTimeSeriesMetadata("USGS-13186000");
69+
70+
// Peaks metadata is marked with computationIdentifier "Max At Event Time"
71+
var flowMeta = TimeSeriesMetadata.filter(peakMetadata)
72+
.parameterCode(Parameter.DISCHARGE).computation("Max At Event Time")
73+
.findFirst().orElseThrow();
74+
var stageMeta = TimeSeriesMetadata.filter(peakMetadata)
75+
.parameterCode(Parameter.STAGE).computation("Max At Event Time")
76+
.findFirst().orElseThrow();
77+
78+
TimeSeries<InstantaneousValue> peakFlow = UsgsWaterDataApi.getAnnualPeaks(flowMeta);
79+
TimeSeries<InstantaneousValue> peakStage = UsgsWaterDataApi.getAnnualPeaks(stageMeta);
80+
81+
System.out.println("Peak " + peakFlow.getParameterName() + " (" + peakFlow.getUnitOfMeasure() + "):");
82+
peakFlow.printToConsole(5);
83+
System.out.println("Peak " + peakStage.getParameterName() + " (" + peakStage.getUnitOfMeasure() + "):");
84+
peakStage.printToConsole(5);
85+
86+
// Read the stage-discharge rating curve (raw RDB text)
87+
System.out.println("\nRead Rating Curve, Snake River near Moran, WY");
88+
String ratings = UsgsWaterDataApi.getRatings("USGS-13011000");
89+
ratings.lines().limit(20).forEach(System.out::println);
90+
6691
```output.txt
6792
Read Daily Mean Discharge, Boise River at Parma
6893
Station: USGS-13213000
@@ -108,6 +133,42 @@ Reading East Fender time-series... (current data)
108133
2026-04-10T15:15:00Z = 19.0
109134
2026-04-10T15:30:00Z = 19.0
110135
2026-04-10T15:45:00Z = 19.0
136+
137+
Read Annual Peaks, Boise River near Featherville
138+
Peak Discharge (ft^3/s):
139+
1945-05-05T00:00:00Z = 2930.0
140+
1946-04-27T00:00:00Z = 4210.0
141+
1947-05-09T00:00:00Z = 4300.0
142+
1948-05-29T00:00:00Z = 4750.0
143+
1949-05-17T00:00:00Z = 3880.0
144+
Peak Gage height (ft):
145+
1945-05-05T00:00:00Z = 5.43
146+
1946-04-27T00:00:00Z = 6.66
147+
1947-05-09T00:00:00Z = 6.75
148+
1948-05-29T00:00:00Z = 6.87
149+
1949-05-17T00:00:00Z = 6.15
150+
151+
Read Rating Curve, Snake River near Moran, WY
152+
# //UNITED STATES GEOLOGICAL SURVEY http://water.usgs.gov/
153+
# //NATIONAL WATER INFORMATION SYSTEM http://water.usgs.gov/data.html
154+
# //DATA ARE PROVISIONAL AND SUBJECT TO CHANGE UNTIL PUBLISHED BY USGS
155+
# //RETRIEVED: 2026-05-08 22:20:02
156+
# //WARNING
157+
# //WARNING The stage-discharge rating provided in this file should be
158+
# //WARNING considered provisional and subject to change. Stage-discharge
159+
# //WARNING ratings change over time as the channel features that control
160+
# //WARNING the relation between stage and discharge vary. Users are
161+
# //WARNING cautioned to consider carefully the applicability of this
162+
# //WARNING rating before using it for decisions that concern personal or
163+
# //WARNING public safety or operational consequences.
164+
# //FILE TYPE="NWIS RATING"
165+
# //DATABASE NUMBER=01 DESCRIPTION=" Standard data base for this site."
166+
# //STATION AGENCY="USGS " NUMBER="13011000 " TIME_ZONE="MST" DST_FLAG=Y
167+
# //STATION NAME="SNAKE RIVER NEAR MORAN, WY"
168+
# //LABEL="Discharge (ft^3/s)"
169+
# //PARAMETER CODE="00060"
170+
# //RATING SHIFTED="20260508222002 MST"
171+
# //RATING ID="1.0" TYPE="STGQ" NAME="stage-discharge" AGING=????
111172
```
112173

113174

@@ -134,96 +195,4 @@ To save API responses to `~/usgs.waterdata/` for inspection, add the JVM flag:
134195
-Dusgs.debug=true
135196
```
136197

137-
Files are named from the response `Content-Disposition` header. Duplicate filenames get an incrementing suffix (e.g. `daily.csv`, `daily1.csv`, `daily2.csv`).
138-
139-
140-
# TODO
141-
142-
- Allow user to specify what paramters they want such as 'Flow'
143-
- Use the /combined-metadata endpoint instead (For list of sites with daily data) and filter on data_type. You'll get one row per data collection rather than one row per site
144-
- keep querys below MAX size..
145-
- document -Djava.net.useSystemProxies=true
146-
- USGS to do: /ogcapi/v0/collections/peaks and /stac/v0/collections/ratings
147-
148-
Station Name=HYDER AK
149-
Stream Name=SALMON R
150-
Station ID=15008000
151-
Version Name=USGS
152-
Latitude=56.0259971857126
153-
Longitude=-130.06687006967235
154-
Elevation=286
155-
Coord Datum=NAD27
156-
PARAMETERS=ALL
157-
PARAMETERS=Flow,Stage (new feature)
158-
159-
- put Site-types in config somewhere:
160-
- Internally use MonitoringLocation instead of legacy UsgsStation
161-
- get period of record from api (start with Daily)
162-
- get more site types.
163-
- when reading regular time series , check dates for ordering ()
164-
165-
gs-w_waterdata_support@usgs.gov
166-
167-
168-
Reference and Examples
169-
170-
171-
172-
USGS 13037500 SNAKE RIVER NR HEISE ID
173-
174-
Parameter types
175-
00060 Discharge cfs 1Day
176-
00065 Gage Height ft
177-
178-
https://api.waterdata.usgs.gov/ogcapi/v0/collections/parameter-codes/items
179-
180-
181-
monitoring locations
182-
183-
curl -X 'GET' \
184-
'https://api.waterdata.usgs.gov/ogcapi/v0/collections/monitoring-locations/items?f=csv&lang=en-US&limit=10000&skipGeometry=true&offset=0&agency_code=USGS&state_code=06&site_type_code=ST' \
185-
-H 'accept: application/geo+json'
186-
187-
https://api.waterdata.usgs.gov/ogcapi/v0/collections/site-types/items
188-
Streams ST:
189-
Canal: ST-Canal
190-
Tidal Stream: ST-TS
191-
Lake, Reservoir: LK
192-
https://api.waterdata.usgs.gov/ogcapi/v0/collections/monitoring-locations/items?f=csv&lang=en-US&limit=10000&skipGeometry=false&offset=0&agency_code=USGS&state_code=06&site_type_code=ST
193-
194-
195-
196-
Time series metadata
197-
198-
https://api.waterdata.usgs.gov/ogcapi/v0/collections/time-series-metadata/items?limit=2000&state_name=Utah
199-
200-
201-
202-
203-
daily data
204-
old: https://waterservices.usgs.gov/nwis/dv?sites=10059500&startDT=2025-02-26&endDT=2026-02-28&format=rdb
205-
206-
207-
https://api.waterdata.usgs.gov/ogcapi/v0/collections/daily/items?f=csv&lang=en-US&limit=1000&properties=time,value,unit_of_measure&skipGeometry=true&sortby=time&offset=0&monitoring_location_id=USGS-11463500&parameter_code=00060%2C00065&statistic_id=00003&time=2018-02-12T00%3A00%3A00Z%2F2018-03-18T12%3A31%3A12Z
208-
209-
210-
211-
https://api.waterdata.usgs.gov/ogcapi/v0/collections/time-series-metadata/items?f=csv&lang=en-US&limit=10&skipGeometry=false&offset=0&monitoring_location_id=USGS-13037500
212-
213-
214-
215-
15 Minute data...
216-
217-
/collections/continuous/items
218-
219-
220-
221-
States
222-
223-
https://api.waterdata.usgs.gov/ogcapi/v0/collections/states/items?f=csv&lang=en-US&limit=10000&skipGeometry=false&offset=0
224-
225-
226-
227-
curl -X 'GET' \
228-
'https://api.waterdata.usgs.gov/ogcapi/v0/collections/time-series-metadata/items?f=csv&lang=en-US&limit=10&skipGeometry=true&offset=0&parameter_code=00060%2C00065&statistic_id=00030&state_name=Idaho' \
229-
-H 'accept: application/geo+json'
198+
Files are named `<collection>_<id>` after the request, e.g. `daily_4993ed5a....csv`, `time-series-metadata_USGS-13037500.csv`, or `ratings_13011000.rdb`. Duplicate filenames get an incrementing suffix (e.g. `daily_....csv`, `daily_...1.csv`).

src/main/java/org/opendcs/Demo.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,30 @@ public static void main(String[] args) throws Exception {
5858
start, end);
5959

6060
eastfenderTS.printToConsole(5);
61+
62+
// Read annual peak flow and stage (one value per water year)
63+
System.out.println("\nRead Annual Peaks, Boise River near Featherville");
64+
var peakMetadata = UsgsWaterDataApi.getTimeSeriesMetadata("USGS-13186000");
65+
66+
// Peaks metadata is marked with computationIdentifier "Max At Event Time"
67+
var flowMeta = TimeSeriesMetadata.filter(peakMetadata)
68+
.parameterCode(Parameter.DISCHARGE).computation("Max At Event Time")
69+
.findFirst().orElseThrow();
70+
var stageMeta = TimeSeriesMetadata.filter(peakMetadata)
71+
.parameterCode(Parameter.STAGE).computation("Max At Event Time")
72+
.findFirst().orElseThrow();
73+
74+
TimeSeries<InstantaneousValue> peakFlow = UsgsWaterDataApi.getAnnualPeaks(flowMeta);
75+
TimeSeries<InstantaneousValue> peakStage = UsgsWaterDataApi.getAnnualPeaks(stageMeta);
76+
77+
System.out.println("Peak " + peakFlow.getParameterName() + " (" + peakFlow.getUnitOfMeasure() + "):");
78+
peakFlow.printToConsole(5);
79+
System.out.println("Peak " + peakStage.getParameterName() + " (" + peakStage.getUnitOfMeasure() + "):");
80+
peakStage.printToConsole(5);
81+
82+
// Read the stage-discharge rating curve (raw RDB text)
83+
System.out.println("\nRead Rating Curve, Snake River near Moran, WY");
84+
String ratings = UsgsWaterDataApi.getRatings("USGS-13011000");
85+
ratings.lines().limit(20).forEach(System.out::println);
6186
}
6287
}

src/main/java/org/opendcs/usgs/waterdata/InstantaneousValue.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package org.opendcs.usgs.waterdata;
22

33
import java.time.Instant;
4+
import java.time.LocalDate;
5+
import java.time.LocalTime;
6+
import java.time.ZoneOffset;
47
import java.time.format.DateTimeFormatter;
8+
import java.time.format.DateTimeParseException;
59
public class InstantaneousValue {
610

711
public Instant time;
@@ -23,11 +27,33 @@ private static Instant parse(String timeStr) {
2327

2428
static InstantaneousValue fromRow(DataTable table, int row) {
2529
InstantaneousValue v = new InstantaneousValue();
26-
v.time = parse(table.get(row, "time"));
30+
v.time = parse(table.get(row, "time"));
2731
v.value = table.getDouble(row, "value", UsgsWaterDataApi.UNDEFINED_DOUBLE);
2832
return v;
2933
}
3034

35+
/**
36+
* Builds a value from a peaks-collection row, combining time and time_of_day.
37+
*/
38+
static InstantaneousValue fromPeakRow(DataTable table, int row) {
39+
InstantaneousValue v = new InstantaneousValue();
40+
v.time = parsePeakTime(table.get(row, "time"), table.get(row, "time_of_day"));
41+
v.value = table.getDouble(row, "value", UsgsWaterDataApi.UNDEFINED_DOUBLE);
42+
return v;
43+
}
44+
45+
static Instant parsePeakTime(String dateStr, String timeStr) {
46+
LocalDate date = LocalDate.parse(dateStr);
47+
if (timeStr != null && !timeStr.isBlank()) {
48+
try {
49+
return date.atTime(LocalTime.parse(timeStr.trim())).toInstant(ZoneOffset.UTC);
50+
} catch (DateTimeParseException e) {
51+
// fall through to date-only
52+
}
53+
}
54+
return date.atStartOfDay().toInstant(ZoneOffset.UTC);
55+
}
56+
3157
@Override
3258
public String toString() {
3359
return time + " = " + value;

src/main/java/org/opendcs/usgs/waterdata/PeakFlowService.java

Lines changed: 0 additions & 124 deletions
This file was deleted.

0 commit comments

Comments
 (0)