Skip to content

Commit 7ab54d8

Browse files
committed
Chunk by time to avoid the API's 1100-day limit.
Don't rely on truncation alone. Dested manually with 6 years of data
1 parent eba168d commit 7ab54d8

1 file changed

Lines changed: 22 additions & 13 deletions

File tree

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

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

33
import java.time.Instant;
4+
import java.time.temporal.ChronoUnit;
45
import java.util.ArrayList;
56
import java.util.HashMap;
67
import java.util.List;
@@ -33,6 +34,7 @@ public class UsgsWaterDataApi {
3334
* Maximum number of responses the API returns per request
3435
*/
3536
static final int PAGE_LIMIT = 50000;
37+
static final long MAX_WINDOW_DAYS = 1000;
3638

3739
static final String ROOT_URL = "https://api.waterdata.usgs.gov/ogcapi/v0/collections/";
3840
static final String LOCATIONS_URL = ROOT_URL + "monitoring-locations/items?f=csv&lang=en-US&limit=50000&offset=0&agency_code=USGS&state_code=%s&site_type_code=%s";
@@ -102,17 +104,20 @@ private static List<InstantaneousValue> fetchContinuousValues(String timeSeriesI
102104
return TestSite.generateContinuousValues(startDate, endDate);
103105

104106
List<InstantaneousValue> all = new ArrayList<>();
105-
String chunkStart = normalizeToInstant(startDate);
106-
String normalizedEnd = normalizeToInstant(endDate);
107+
Instant rangeEnd = Instant.parse(normalizeToInstant(endDate));
108+
Instant chunkStart = Instant.parse(normalizeToInstant(startDate));
107109
Instant lastSeen = null;
108110
boolean continuation = false;
109-
while (true) {
110-
String url = String.format(CONTINUOUS_URL_ID, timeSeriesId, chunkStart, normalizedEnd);
111-
String csv = WebUtility.getPage(url);
112-
if (csv == null || csv.isBlank()) break;
111+
while (chunkStart.isBefore(rangeEnd)) {
112+
// Cap the request at the API's maximum time envelope.
113+
Instant windowEnd = chunkStart.plus(MAX_WINDOW_DAYS, ChronoUnit.DAYS);
114+
if (windowEnd.isAfter(rangeEnd)) windowEnd = rangeEnd;
113115

114-
List<InstantaneousValue> page = CsvFile.fromString(csv).mapRows(InstantaneousValue::fromRow);
115-
if (page.isEmpty()) break;
116+
String url = String.format(CONTINUOUS_URL_ID, timeSeriesId, chunkStart.toString(), windowEnd.toString());
117+
String csv = WebUtility.getPage(url);
118+
List<InstantaneousValue> page = (csv == null || csv.isBlank())
119+
? Collections.emptyList()
120+
: CsvFile.fromString(csv).mapRows(InstantaneousValue::fromRow);
116121

117122
for (int i = 0; i < page.size(); i++) {
118123
InstantaneousValue iv = page.get(i);
@@ -129,12 +134,16 @@ private static List<InstantaneousValue> fetchContinuousValues(String timeSeriesI
129134
all.add(iv);
130135
lastSeen = iv.time;
131136
}
132-
133-
if (page.size() < PAGE_LIMIT) break;
134-
if (lastSeen == null || chunkStart.equals(lastSeen.toString())) break;
135-
chunkStart = lastSeen.toString();
136137
continuation = true;
137-
logger.info("Continuous query returned a full page; fetching next chunk from " + chunkStart);
138+
139+
if (page.size() >= PAGE_LIMIT && lastSeen != null && lastSeen.isAfter(chunkStart)) {
140+
// Window was truncated at the page limit; resume from its last point.
141+
chunkStart = lastSeen;
142+
logger.info("Continuous query returned a full page; fetching next chunk from " + chunkStart);
143+
} else {
144+
// Window exhausted; advance to the next time window.
145+
chunkStart = windowEnd;
146+
}
138147
}
139148
return all.isEmpty() ? Collections.emptyList() : all;
140149
}

0 commit comments

Comments
 (0)