committed 02:28PM - 21 Aug 26 UTC
Hubeau's single `dynamic` resolution is replaced by the five intervals its netwo…rk uses:
`5_minutes`, `6_minutes`, `10_minutes`, `15_minutes` and `hourly`. A request for `dynamic/data/...`
no longer resolves, and `Resolution.DYNAMIC` is gone with it -- no provider is dynamic any more.
Unlike Pegelonline's declared `equidistance`, Hubeau publishes the interval nowhere: not in the
station referential, not on the observations, and the v2 API defines no field for one. It is
measured instead, from the timestamps a station has just published. That works because the network
transmits on a grid: of 3018 stations reporting over six hours, 2987 resolved to one of the five
intervals (5 min for 1643, 10 for 903, 15 for 251, 60 for 120, 6 for 33), and re-measuring a
45-station sample over 48 hours named all 45 the same way.
Two hours of the whole network are read when the station list is built, which names every station
transmitting at least every fifteen minutes -- seven in eight of them -- for two pages and about
thirty seconds. The slower and quieter stations are asked about by name instead, in batches, over a
longer window: stage first, discharge only for whatever is still missing, and never both at once,
since interleaving two grids of one station halves the spacing and would name the interval wrong.
The window is closed at both ends and anchored to a six-hour boundary, so it and every URL built
from it stay the same between calls and the cache can hold them. A cold station list costs about a
minute; a warm one is instant.
A station with nothing to measure is listed under no resolution rather than under a guessed one,
and returns as soon as it transmits again. So is one transmitting every 20 or 30 minutes, which no
resolution covers; that is reported once per process rather than filed under a neighbouring
interval.
In exchange `ts_complete` works, having been short-circuited for a dynamic resolution, and the
interpolation search radius scales by resolution instead of falling back to a factor of 1.0. The
per-station frequency probe the values class ran before every request is gone with the guesswork.
Three fixes came out of building it, each of which had been hiding a slice of the network:
- The station referential answers with a page of 1000 of its 4150 stations and a cursor to the
rest, and the query named no page size and followed no cursor, so three quarters of the French
gauges were missing from the station list and unreachable through it -- including by
`filter_by_station_id`, which filters against that list.
- The station list kept only codes beginning with a letter. Metropolitan codes begin with the
letter of their hydrographic basin and the codes of Guadeloupe, Martinique, Guyane, La Réunion
and Mayotte begin with a digit, so all 176 overseas gauges were excluded -- 86 of them
transmitting, now serving stage and discharge from four of the five departments. The filter
guarded nothing: every code the referential publishes is a well-formed ten characters.
- `ts_complete` completes onto the grid a resolution implies by an exact join, so a gauge reporting
at seven minutes past the hour -- a good third of Hubeau's hourly stations -- matched no row and
came back as a column of nulls. It still does, but says so now. Counted over the grid's own span,
since collection runs before the frame is trimmed to the request, and read off the grid rather
than off the localized window bounds, which are in the station's own timezone where the values
are UTC.
Breaking: `dynamic/data/...` no longer resolves, `Resolution.DYNAMIC` and `ResolutionType` are
removed, and the station list is both far larger and no longer contains stations that have
published nothing recent.