Skip to content

Releases: openwatersio/station-metadata

v5.3.1 — a slug for Sea Level

Choose a tag to compare

@clarkbw clarkbw released this 08 Sep 01:47

Allocates sea-level for noaa/8655875 (Atlantic, NC), which enters the bundled tide catalogue with @neaps/tide-database 0.9.20260907, and tombstones long-key-lake for noaa/8723887, which leaves in the same revision.

The tombstone is a retirement, not a deletion: it stops a later station inheriting a URL that used to mean something else, and if Long Key Lake returns — it left through an upstream catalogue revision rather than a decision here — buildSlugTable hands its own slug back rather than minting a fresh one.

Unblocks openwatersio/slackwater-ios#315.

Full changelog: v5.3.0...v5.3.1

v5.3.0 — slugs for subordinates of non-primary bins

Choose a tag to compare

@clarkbw clarkbw released this 07 Sep 09:59

143 current slugs for the NOAA subordinate stations whose reference is a non-primary bin (slackwater-ios#269). Reference-only bin records are skipped by every CLI command and never slugged. Nothing moved, nothing departed.

v5.2.0 — slugs for the NOAA subordinate current stations

Choose a tag to compare

@clarkbw clarkbw released this 04 Sep 17:13
1c27379

1,549 current slugs allocated for the NOAA subordinate current stations slackwater-ios#270 lifts the harmonic-only filter for. Every existing slug is unchanged; nothing departed, so no tombstones. Data only, no API change. Via #37.

v5.1.1

Choose a tag to compare

@clarkbw clarkbw released this 03 Sep 20:13
3c45a73

Two releases' worth of change, cut together: 5.1.0 was merged without a tag.

Tide coverage: 3,823 → 5,842 slugs (#34)

Allocates a permanent slug for the 2,017 NOAA subordinate tide stations that openwatersio/slackwater-ios#229 admits, plus chs-cap-des-rosiers and chs-riviere-madeleine. Nothing moved and nothing was tombstoned; every existing slug is unchanged. 1,907 of the new slugs took the bare name, 112 the region rung, none the station-id rung.

Three same-name pairs sit about a kilometre apart, each a NOAA row beside an existing CHS or TICON station: Mill Bay, Kumeon Bay and Pointe-à-Pitre. They are left as allocated. Merging any of them onto the existing slug is a formerSlugs change later.

The shared-slug contract, stated and checked (#36)

Four slugs are held by two ids each on purpose: tide point-atkinson, vancouver, victoria and current boundary-pass. Each is a curated identity beside the provider's own row for the same water, merged in 4.1.0 so the pair is one URL. That contract lived only in registry comments, so it kept being re-reported as a bug.

  • A test in seed-registry.test.js now holds the shape in CI, against the committed table: a slug held by two ids must have exactly one registry-owned id, and that record must carry the retired slug in formerSlugs.
  • The catalogue-backed resolver test lost an assertion that had been failing on main since 4.1.0, unseen, because it needs the provider catalogues and CI has none. It reported the deliberate merges as slug theft.
  • The 100 m sweep in slugs can see registry-only stations again. They were merged into the catalogue without a position, so the pair finder skipped them, which hid the curated half of every pair.
  • CONTRIBUTING.md regains its allocation section and gains "Two ids may share one slug". The stale data/slugs.lock.json references are gone from both YAML headers.

Upgrading

data/slugs.json grows to roughly 6,709 rows and loads eagerly with the package root. Consumers that route /tide/<slug> get 2,019 new tide pages; a consumer with hard-coded station counts will need them updated.

v5.0.1 — a curated context names the water, not the town

Choose a tag to compare

@clarkbw clarkbw released this 02 Sep 14:45
360b97e

Dodd Narrows reads Northumberland Channel instead of Nanaimo, and NAS Whidbey Island reads Strait of Juan de Fuca instead of Oak Harbor. A curated context names the water or landmark a station sits in; a town is what the derived tier already says, so Nanaimo next to Sidney, BC read as a derived label missing its province. Both towns stay in cities for search.

Validation now rejects a context that is one of the record's own cities, in both the registry and corrections validators. Data and validation only, no API change. Via #33.

v5.0.0 — a derived context is stated plainly, without the "~"

Choose a tag to compare

@clarkbw clarkbw released this 02 Sep 14:27
89be9c7

Every derived context loses its leading ~: "~Sidney, BC" is now "Sidney, BC". The glyph hedged every derived label identically whether the place was 2 km off or 39, which told a reader nothing they could act on. derived: true is unchanged and still says how a context was arrived at.

Breaking: a consumer sniffing context.startsWith("~") to detect a derived label must read the derived flag instead. Fixes #29 via #30. Also in this release: inlandMetres is about 9× faster on inland points (#31) and the README is split into a front door and a contributor guide (#32).

v4.1.2 — a trailing state abbreviation stays caps

Choose a tag to compare

@clarkbw clarkbw released this 31 Aug 14:42
a932158

cleanName downcased the state NOAA writes at the end of a name — "USCG Station Ny", "Riverdale, N.y." — on 153 of the 3,823 tide-corpus stations. A trailing USPS code now stays as written, dotted or not, position-guarded so "LA PUSH" still calms to "La Push". Fixes #26 via #27.

v4.1.1 — both Mile Point buoys carry their buoy number

Choose a tag to compare

@clarkbw clarkbw released this 31 Aug 03:59
5d31bdb

The one rename 4.1.0's slug sweep surfaced and deliberately deferred (#25). Same
window argument as that release: mile-point was free, and permanent as soon as
anything consumed it.

The asymmetry

id name region slug
noaa/jx0302 Mile Point LB 20 ~Atlantic Beach, FL mile-point-lb-20
noaa/jx0303 Mile Point LB 22 mile-pointmile-point-lb-22

Two lighted buoys 4.4 m apart in the St Johns River, genuinely distinct —
different flood and ebb directions, different M2 amplitudes. NOAA files their
buoy numbers inconsistently: one in the name, one in the region. The ladder
reads the name, so LB 20 got a slug that identifies it and LB 22 took the
generic name for the whole place — /currents/mile-point read as the Mile
Point station while being specifically LB 22.

Both now carry their number, and mile-point belongs to neither.

Reserved, not just vacated. mile-point is recorded in formerSlugs, so a
consumer building a redirect map serves it, and it can never be handed to a
future station.

Name and context corrected too. jx0303's region field held "LB 22" — a
buoy number, not a place — so the correction gives it name: Mile Point LB 22
and context: St Johns River, matching how NOAA names its sibling.

Unchanged

4,690 rows before and after; the slug table diff is exactly one line. No slug
published before 4.0.0 has ever moved.

v4.1.0 — one slug per station, not one per catalogue row

Choose a tag to compare

@clarkbw clarkbw released this 31 Aug 03:45
5fb17b7

Four of the slugs 4.0.0 published were second names for water that already had
one. This retires them and adds the check that would have caught them.

Every slug published before 4.0.0 is untouched. The four names retired here
were allocated in 4.0.0 and nothing consumed them: no page, no sitemap row, no
link.

The four

apart kept retired
1.5 m boundary-pass turn-point
39.8 m vancouver vancouver-bc
42.3 m victoria victoria-harbour
63.3 m point-atkinson point-atkinson-west-vancouver-bc

Each pair is one station entered twice — a curated identity plus the provider's
own catalogue row. chs-victoria already carried "victoria harbour" as an
alias, so the curated entry knew about its own duplicate. Both ids in each pair
now resolve to the kept slug.

The retired names redirect rather than 404. Each is recorded in
formerSlugs on the surviving record, so a consumer building a redirect map
from the registry serves them. None is reallocated, then or later.

Why a uniqueness check could not have found this

The allocation ladder manufactures uniqueness. A duplicate loses the base slug
to whichever id sorts first and falls to the region rung, so vancouver and
vancouver-bc were both allocated and both read as careful disambiguation.
The slugs are unique — uniqueness passing is not evidence of anything.
Position is the only signal that survives the ladder.

New

findNearbyPairs, haversineMetres, NEARBY_METRES. A position sweep for
duplicate identities within a kind. station-metadata slugs now reports pairs
within 100 m after writing, so the next allocation surfaces them while the
names are still free.

It reports and never fails. noaa/jx0302 and noaa/jx0303 are 4.4 m apart and
genuinely distinct lighted buoys, with different flood directions and different
M2 amplitudes — distance cannot tell a duplicate from a close pair, so a person
decides.

Indexed rather than scanned: bucketed into ~0.01° cells and compared against
adjacent cells, it sweeps all 4,690 stations in 11 ms, where all-pairs would
be ~7.3M distance calculations. A test asserts the bound.

checkSlugTable takes an optional fourth argument, the slugs recorded in
formerSlugs. A slug may move when its old value is recorded — the rule
registry.yaml has documented since before the table existed, now enforced. A
move with no record is still reported.

Unchanged

4,690 slugs, 3,823 tide and 867 current. The table still covers exactly the
catalogue it was generated against, and no slug published before 4.0.0 has ever
moved.

v4.0.0 — every station gets a permanent slug

Choose a tag to compare

@clarkbw clarkbw released this 30 Aug 21:11
b42bf03

A slug is now allocated once and published, rather than derived from a station's name on every read. A name proposes a slug at allocation; after that the table is the record and the name is free to change. That is what makes an over-the-air name correction safe: it can no longer move a link already sitting in someone's group chat.

Breaking

The lock API is gone. buildSlugsLock, readSlugsLock and checkSlugs are replaced by buildSlugTable, emptyTable, readSlugTable and checkSlugTable. data/slugs.lock.json is replaced by data/slugs.json and data/slug-tombstones.json.

resolve().slug reads the published table. It previously derived toSlug(name), which had drifted: across the bundled catalogue, 194 stations resolved to a slug published for a different station — Aberdeen, Scotland returned aberdeen, which belongs to Aberdeen, Washington. A station absent from the table now returns "" rather than a derived fallback, because a fallback re-creates that collision in a narrower form.

The CLI requires its catalogues. slugs and check-slugs take repeatable --tides and --currents flags, and refuse to run without both kinds. A station missing from the input is indistinguishable from one that departed, and a departure tombstones a slug permanently.

What ships

data/slugs.json4,690 slugs, 3,823 tide and 867 current, partitioned by kind.

  • Kind is a namespace. A tide and a current station at one place may both hold dodd-narrows; they become different URLs. Uniqueness is enforced within a kind.
  • Allocation is order-independent. Candidates sort by station id, so catalogue ordering cannot change who wins a contested name. Adding stations never moves an existing slug.
  • A slug is never reused. A departed station's slug moves to slug-tombstones.json and is never reallocated. Freeing it would let a future station inherit an old link and show different water — for anyone timing a transit, worse than a dead link.
  • The artifact is reproducible. No wall-clock field, and catalogue digests are order-independent, so repeated builds from one catalogue are byte-identical.

CI now fails a pull request if a published slug moved since the previous release tag.

Coverage

These 4,690 are the harmonic stations. NOAA's subordinate stations — 2,244 tide and 1,705 current — are not yet in the bundled catalogue and so hold no slugs. They allocate when the generators start emitting them; existing slugs are unaffected.