Layer time can be open-ended ("now") and granularity-aware - #337
Layer time can be open-ended ("now") and granularity-aware#337BhattaraiSijan wants to merge 11 commits into
Conversation
…uration offsets) A layer's dataStartTime/dataEndTime may now be a policy re-evaluated at load instead of a fixed timestamp, so configs for growing collections never go stale. One pure resolver evaluates 'now' and 'now +/- <ISO duration>'; per veda-ui the result is the raw current moment, never rounded. Unparseable values resolve to null so consumers keep their fallbacks. Spec'd with an injected clock. Mechanism half of #332.
The per-layer timeline bar previously hand-parsed data times with an inline 'now' special case for the end only. It now resolves both ends through the time-policy resolver, so open-ended and offset policies render correctly on either end and reach the current moment on every load. The bus layer-config type documents the policy vocabulary plus the carried granularity/periodicity facts (interval, isPeriodic). Completes #332's visible half.
The Data Start/End Time field descriptions said ISO 8601 datetime only, so an admin had no way to discover that now, or now offset by an ISO 8601 duration, is a legal value. The tile and vector layer forms both say so now. Description text only; no behaviour change. Completes #332's documentation criterion.
Nothing in this PR writes or reads either field;
The now / now ± duration vocabulary is a core-owned meaning of dataStartTime/dataEndTime. Core resolves it at call time behind one provider; the Timeline plugin reads dates and no longer imports a resolver. The Layers tool's set-to-extent action goes through the same path, which it previously did not (a 'now' produced NaN there).
e7760a4 to
111be6a
Compare
…to feat/open-ended-layer-time
| import F_ from '../../Basics/Formulae_/Formulae_' | ||
| import L_ from '../../Basics/Layers_/Layers_' | ||
| import Map_ from '../../Basics/Map_/Map_' | ||
| import { resolveTimePolicy } from '../../Basics/TimeControl_/layerTimePolicy' |
There was a problem hiding this comment.
Yes its the legacy Layers tool.
| if (extent?.start != null) start = new Date(extent.start) | ||
| if (extent?.end != null) end = new Date(extent.end) |
There was a problem hiding this comment.
these should be resolved based on the periodicity - not always current date
for example, if a data is updated every 7 days and it started ten days ago, the end time needs to be 3 days ago not today
There was a problem hiding this comment.
also shouldn't this come from layers, using temporalExtentFor? since it already does the resolving, we don't need to do it in two places
There was a problem hiding this comment.
Both points addressed.
-
A layer can now declare time.interval and the extent's end snaps to the last step from the start date — so your example (7-day data, started 10 days ago) resolves to 3 days ago, not today. It's settable in Configure. No separate isPeriodic — per your earlier comment, having an interval is what makes a layer periodic. The snapping happens in core behind layers:getTemporalExtent, so the Timeline and every other consumer get it without knowing the cadence exists.
-
extentshere ismmgisGetTemporalExtents()(line 180), which is the bus call to layers:getTemporalExtent, and that provider is temporalExtentFor. This line is just converting the returned ISO string to a Date for drawing.
…d-layer-time # Conflicts: # src/essence/Basics/Layers_/Layers_.js
A layer may declare time.interval (an ISO-8601 duration): data exists only at start-anchored steps, so resolveTemporalExtent answers the last step at or before the resolved end - a 7-day cadence begun ten days ago ended three days ago, not now.
layers:getTemporalExtent and the Layers tool's set-time-to-extent action answer through resolveTemporalExtent, so every consumer - the Timeline included, unchanged - sees the extent end on the last real data step.
A Data Time Interval field (ISO 8601 duration) beside the data start/end times; the bus contract documents that the cadence is already folded into resolved extents, so plugins never see it.
…to feat/open-ended-layer-time
Closes #332 (part of epic #331)
Summary
Time-enabled layers could only declare fixed data start/end timestamps — but for growing collections the end is unknowable at authoring time, so every config went stale the day after it was written. A layer's
dataStartTime/dataEndTimemay now be a policy re-evaluated on demand:now, ornow ± <ISO-8601 duration>(now - P1D,now + P5D). Concrete datetimes behave exactly as before.Per veda-ui (our semantics standard),
nowresolves to the raw current moment — never rounded. But rawnowis only half the truth for a periodic collection: a product that publishes every 7 days and started ten days ago has its newest data three days ago, not today. So a layer may also declare its cadence,time.interval(an ISO-8601 duration:P1D,P7D,P1M), and the extent's end floors to the last start-anchored step —start + N × intervalfor the largest N that fits. Data exists only on the grid; the extent no longer claims otherwise. This matches how veda-ui reaches the same answer (raw open end + start-anchored periodic steps).Core owns this vocabulary. The config keeps what the author wrote; core resolves it behind one provider,
layers:getTemporalExtent, which answers{ start, end }as plain ISO datetimes (ornull) — for one layer by id, or for all layers keyed by UUID, in the same call shapes aslayers:getCogCapabilitiesandlayers:getBounds. It resolves at the moment of asking, so an open-endednowis always fresh — and the cadence flooring happens behind the provider too, so a plugin never sees a policy string, never sees an interval, and never imports a resolver.Resolution degrades, never breaks: an unparseable policy or interval, a zero cadence (
P0D), a missing start to anchor to — each is ignored and consumers keep their fallbacks. Calendar units step by UTC date components, not milliseconds, andstart + N × intervalis computed as one calendar operation, so a monthly cadence stays anchored to the start's day-of-month.isPeriodicwas dropped from an earlier revision of this PR as unused (review feedback) — correctly so:intervalalone carries the fact, and its presence is what marks a layer periodic. #338's VEDA source can fill it automatically fromdashboard:time_interval; authors can type it by hand today.Commits
nullfor anything unparseable.'now'special case.Basics/TimeControl_/layerTimePolicy.ts;Layers_registerslayers:getTemporalExtent; the Timeline reads extents instead of resolving;_shared/time/removed. The Layers tool's "set global time to layer extent" action goes through the same path — it previously didnew Date("now")and producedNaN.resolveTemporalExtentfloors a periodic layer's end to its last data step. Constant-time: N is seeded from a millisecond approximation, then settled by calendar math.layers:getTemporalExtentand the Layers tool answer cadence-aware; the Timeline gets it with zero changes.Testing
P0Dincluded) degrading to an unsnapped extent or null. Full suite green (124 files, 1790 tests); typecheck clean.nowdraws its timeline bar reaching the current date (screenshot in Layer time can be open-ended ("now") and granularity-aware #332), while a closed-extent layer's bar correctly stops at its real end — and thenowconfig needs no edits, ever.Mission-level time (
initialstart/initialend) keeps its existing seconds-offset syntax; unifying it with ISO durations is possible future work.🤖 Generated with Claude Code