Skip to content

Forecast layers: pick a model run, scrub valid time - #473

Open
BhattaraiSijan wants to merge 7 commits into
feat/url-replacement-kindsfrom
feat/forecast-data
Open

BhattaraiSijan wants to merge 7 commits into
feat/url-replacement-kindsfrom
feat/forecast-data

Conversation

@BhattaraiSijan

@BhattaraiSijan BhattaraiSijan commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Forecast layers: pick a model run, scrub valid time

Builds on the core PR that adds urlReplacements kinds and the layers:configChanged broadcast. Merge that first; this PR's base is its branch.

A forecast has two clocks: when the model ran and what moment it describes. MMGIS has one slider. This collapses the first clock into a choice. A forecast layer's card in the Layer Manager gets a "Model run" dropdown; picking a run pins the layer to it, and the existing timeline scrubs valid time over that run's window. After the pick it is an ordinary time-enabled tile layer: coverage gate, no-data warning, Timeline band and row navigation all work unchanged.

Built against the live NAQFC store served by titiler-multidim. Runs and lead range are read from the service's coordinates endpoints; nothing is hand-listed. No new layer type.

What the plugin does

  • A layer with variables.forecast is a forecast layer. On refresh the plugin reads the newest N runs (forecast.runs, default 10) and the lead range from the store, and pins a layer with no run yet to the newest so it never asks for tiles without one. The endpoints are derived from the tile URL: the service before /tiles/ and the first url= store.
  • Picking a run writes the run into a {reftime} replacement, an elapsed {lead} replacement counting from it in forecast.leadStep units (default PT1H; P1D, P1M for daily or monthly models), and the run's window into the layer's data times. The clock is moved into the window only if it sits outside it.
  • The card shows the runs newest first, named by hour for sub-daily steps and by day or month otherwise, with a lead readout such as +18 h beside the select.
  • A block saved with enabled: false is not a forecast, which is what the Configure switch writes when left off. A hand-written block with no enabled key still is.

Authoring

url: ...?url=s3://.../o3_conus&url=s3://.../o3_ak&variable=ozcon&sel=reference_time=nearest::{reftime}&sel=lead=nearest::{lead}&colormap_name=viridis&rescale=0,80
time: { enabled: true, type: "requery", format: "%Y-%m-%dT%H:%M:%SZ" }
variables: { forecast: { enabled: true, runs: 10, leadStep: "PT1H" } }

Documented on the tile layer page. Configure gets a "Forecast runs" row on the tile layer's Time tab (Forecast Layer switch, Lead Step, Runs To Offer, optional Runs URL and Leads URL) and kind, value, from, step fields on the URL tab's replacement editor, so none of this needs hand-edited JSON.

Verified

  • Unit coverage for the pure helpers, the apply/select/ensure handlers against a fake bus, the card, and the adapter end to end with a stubbed service. Full suite and TypeScript check pass.
  • Live against the NAQFC ozone store: the card lists the newest runs, tiles render, scrubbing changes sel=lead while reference_time stays fixed, picking an older run moves the Timeline band and the tiles, scrubbing past 72 h shows the no-data warning and stops requests.

Follow-ups

  • Core's own month stepping overflows Jan 31 + P1M to Mar 3; the plugin clamps, core does not yet.
  • Run discovery for STAC-served forecasts, where runs live only in item ids.
  • Rescale from /statistics for skewed fields such as PM2.5.

🤖 Generated with Claude Code

Pure helpers for coordinates endpoints, newest runs, calendar-aware lead steps, run windows, and labels. A block saved with enabled false is not a forecast.
Runs and leads are read once from the service. Applying a run writes the reftime and lead replacements and the run's data window; a user's pick also moves the clock into the window when it sits outside.
A Model run select, newest first, named by hour or day per the lead step, with the lead from the run to the selected time beside it.
Rows read their runs on refresh and pin the newest when none is set. Tool var forecastRuns sets the default count.
A Forecast runs row on the tile layer's Time tab, and kind, value, from, and step on the URL tab's replacement editor.
@BhattaraiSijan
BhattaraiSijan marked this pull request as draft September 22, 2026 18:26
BhattaraiSijan and others added 2 commits September 22, 2026 13:27
## Configure: author a forecast layer without editing JSON

Follows the forecast layers PR; its base is that branch. Until now the
`variables.forecast` block could only be written by hand, since the tile
layer editor has no free editor for `variables`.

**Time tab, new "Forecast runs" row** under "Data Time Extent":

- **Forecast Layer**, a switch. Its description tells the author to
write `{reftime}` and `{lead}` into the Source URL.
- **Lead Step**, one lead unit as an ISO 8601 duration, `PT1H` by
default.
- **Runs To Offer**, empty defers to the Layer Manager tool's Forecast
Runs.
- **Runs URL** and **Leads URL**, empty means derived from the Source
URL's service and first `url=` store.

**URL tab**: the replacement editor gains **Kind** (service, value,
elapsed) plus **Value**, **From**, and **Step**, so a hand-authored
literal or elapsed entry survives a save instead of being stripped.

A layer whose switch is left off saves `enabled: false`, which the Layer
Manager reads as "not a forecast".

**Verified**

- The metaconfig parses; the diff is additions plus one description
line, with the file's own formatting kept.
- Round trip: open a NAQFC layer, the switch shows on, save, reload, the
card still lists runs. Open a plain layer, leave the switch off, save,
nothing forecast-related appears on its card.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Runs To Offer is asked per layer where the forecast is defined; a second, mission-wide knob duplicated it. Empty means 10.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@BhattaraiSijan
BhattaraiSijan marked this pull request as ready for review September 22, 2026 18:52

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant