Listening for events…

YearLocationdex framework (5th dex family)

Mike, 2026-06-26. Both the concept and the name are his: he described the shape as "a Yeardex of Locationdexes" and named the family YearLocationdex — the name says its two axes outright (year × location), so one broad family covers the whole space instead of splitting into week/month/year variants (Mike: "I want as few dex categories as possible, so the name describes what I am looking for"). The fifth dex family, after Eventdex (slot = event), Yeardex (slot = year), Locationdex (slot = place), and CelestialObjectDex (slot = object).

A YearLocationdex tracks each place across the years — a Yeardex crossed with a Locationdex. The slot is a (place, period) cell. Year is the top time unit, and a year holds its finer periods inside it (a year is ~52–54 weeks; drought is one cell per county per USDM week, those weeks rolling up under each year). So the family name commits to "year × location" while the source's native cadence (weekly here, monthly or annual elsewhere) nests within the year.

When a source is a YearLocationdex, not a single-axis dex

Ask: does the measurement exist for every (place, period) pair on a regular grid?

  • A drought map redraws every US county every week → county × (year→week) grid.
  • A station network's annual value per station → station × year grid.
  • A county climate normal per month → county × (year→month) grid.

If instead each record is a one-off happening (slot = event), a fixed sensor's running series (slot = place, time is just a column), or a per-year subject with no place axis (slot = year), it is one of the four single-axis families. The test is regular coverage on both axes — a full place × time grid, not a sparse log.

Storage — one flat place-sorted spine, plus a by-year index

Decided for drought (Mike, 2026-06-26: "which one combines the most simplicity with the fastest access to data"). Do not physically nest two families or keep two sorted copies. Store the grid as ONE flat spine-parquet:

  • rows = place × period, one row per cell, columns = the cell's measured value(s).
  • Sorted by place (then period) so one place's whole history reads contiguously — the dominant read ("show me this county's drought history").
  • A small by-year index JSON alongside gives the time cross-section directly (per-year counts + span) without a second 4M-row sorted copy. Year is the natural top key precisely because the finer periods nest under it.
  • Spine-parquet, never file-per-slot: the grid is millions of cells (drought = 4.2M); file-per-slot would bloat the shared storehouse_index. Each YearLocationdex kind gets its own base dir (data/yearlocation_storehouse/<kind>/), kept out of the event index.

This is "both axes first-class" done cheaply: place axis is the physical sort, year axis is the index. A derived single-axis read (e.g. a drought-episode Eventdex, one slot per continuous spell) can be built on top later; it is not the primary store.

Multi-value cells ("data is data")

A cell may carry more than one measured value at different coverage spans. Carry them all; null where a value's span does not reach (feedback_data_is_data_partial_coverage). Do not drop a richer value because it covers fewer years than another.

Bright line

Same as every family: measured reality only (feedback_measured_reality_only). A grid of assessed/observed conditions is IN (USDM, on the FEMA administrative-record precedent); a grid of modeled values (SPEI's modeled PET, any reanalysis grid) is OUT.

Kinds

Kind Slot Source Cells Storage Commit
drought county × week USDM (NCEI/NIDIS CDC archive) 4,198,880 (3,220 counties × 2000–2024 weeks) data/yearlocation_storehouse/drought/drought_spine.parquet + by-year index ca5ff26
nla_water_quality lake site × survey year EPA NARS — National Lakes Assessment 3,572 (1,157 in 2007 + 1,286 in 2012 + 1,129 in 2022) data/yearlocation_storehouse/nla_water_quality/…_spine.parquet + by-year index 0364a98
ncca_water_quality coastal site × survey year EPA NARS — National Coastal Condition Assessment 1,093 (2015: GreatLakes 361 / NE 253 / Gulf 237 / West 124 / SE 118) data/yearlocation_storehouse/ncca_water_quality/…_spine.parquet + by-year index 861dc7e
nrsa_water_quality river/stream site × survey cycle EPA NARS — National Rivers and Streams Assessment 5,904 (2013-14: 2,069 + 2018-19: 1,919 + 2023-24: 1,916) data/yearlocation_storehouse/nrsa_water_quality/…_spine.parquet + by-year index e8d17de
nwca_wetland_chemistry wetland site × survey cycle EPA NARS — National Wetland Condition Assessment 2,849 (2011: 1,129 + 2016: 1,054 + 2021: 666) data/yearlocation_storehouse/nwca_wetland_chemistry/…_spine.parquet + by-year index 778dda5

The EPA 20-reservoir cyanobacteria dataset (Smucker et al. 2021) was first prototyped here as a reservoir×year YearLocationdex (91c7a27), then Mike chose reservoir-as-place (2026-06-28), so it moved to the Locationdex family — 20 reservoir slots each embedding their annual series. See docs/locationdex-framework.md / docs/scope-reservoir-cyano-water-quality-locationdex.md.

nla_water_quality notes: raw per-site water chemistry / clarity / chlorophyll measurements IN; the survey's design-based condition estimates (population weights) + MMI indices OUT. Place axis is sparse across cycles (NLA re-samples a fresh probability set each cycle, so site IDs differ between 2007 and 2012). Scope freeze: docs/scope-nla-water-quality-yearlocationdex.md.

ncca_water_quality notes: the coastal NARS sibling of NLA (estuarine + Great Lakes folded into one kind via the region column). Same IN/OUT split (raw measurements IN; the POPESTIMATES population-weight files OUT). Total N / total P scaled mg/L→µg/L to share NLA's ntl_ugl/ptl_ugl units; conductivity correctly high (estuarine salinity). 2015 cycle only for now; NCCA 2010 is a deferred backfill. Scope freeze: docs/scope-ncca-water-quality-yearlocationdex.md.

nrsa_water_quality notes: the rivers-and-streams NARS sibling — the first NARS kind with a real multi-cycle year axis (2013-14 / 2018-19 / 2023-24, 2008-09 deferred). Carries the full NLA vocabulary minus Secchi (rivers aren't Secchi-sampled). UNIT ASYMMETRY vs NLA/NCCA: total N ×1000 (mg/L→µg/L) but total P is already µg/L (no scale) — getting it backwards inflates river TP 1000×. utf8-lossy parse (latin-1 bytes in site files); chlorophyll lives in a separate widewchl file for the 2013-14 cycle. Scope freeze: docs/scope-nrsa-water-quality-yearlocationdex.md.

nwca_wetland_chemistry notes: the wetlands NARS sibling, and the one that breaks the water-only pattern. A wetland's headline measurement is its SOIL; only ~55% of sites have standing surface water (631–675 per cycle) but ~92% have soil chemistry (~1,035), so a *_water_quality framing would silently drop the ~400 soil-only sites each cycle — a "data is data" violation (feedback_data_is_data_partial_coverage). Mike's call (2026-06-27): ONE wetland kind carrying BOTH a shared-vocab water block (ptl_ugl/ntl_ugl/cond_uscm/ph/ chla_ugl) AND a wetland-specific soil block (soil_carbon_pct/soil_nitrogen_pct/ soil_sulfur_pct/soil_ph_h2o/soil_cec_cmolkg); a site with only one block is still carried. Real 3-cycle year axis (2011/2016/2021). Per-cycle schema drift handled by adapters: 2011 water is bare columns + a separate chla file, 2016/2021 water is <ANALYTE>_RESULT; 2011 soil is per-LAYER so the per-site summary takes the SURFACE horizon (shallowest depth), 2016 soil is a standardized-depth core (one row per site), 2021 released no soil → soil block null. UNIT ASYMMETRY matches NRSA: total N ×1000 (mg/L→µg/L), total P already µg/L; soil C/N/S are percent, CEC cmol(+)/kg. Held out: design-based condition estimates + vegetation MMI/index scores. Slot also carries hyd_cls (INLAND/TIDAL). Scope freeze: docs/scope-nwca-wetland-chemistry-yearlocationdex.md.

Cell values for drought: in_drought_d1 (binary D1+ flag, 2000–2024) and area_pct_d1 (percent of county area in D1+, 2000–2021; null 2022–2024 until backfill). Scope freeze: docs/scope-drought-yearlocationdex.md.

Deferred

  • Live edge for drought. No current-year county file exists (NCEI posts the annual file after the fact); the live usdm_drought PG feed is state-level (coarser). The live edge is a separate decision — likely a county-AOI pull from the USDM data services API.
  • County centroids for mapping (a county is a polygon; v1 keys on FIPS + name + state).
  • Per-week D0–D4 split (the historical archive carries only the D1+ threshold).
Live Feed