Layer editor: fill a tile layer from a VEDA STAC collection - #338
Draft
BhattaraiSijan wants to merge 3 commits into
Draft
Layer editor: fill a tile layer from a VEDA STAC collection#338BhattaraiSijan wants to merge 3 commits into
BhattaraiSijan wants to merge 3 commits into
Conversation
…yer fields) Pure, I/O-free mapping from a VEDA STAC collection document to MMGIS tile layer fields: render-driven tile URL and legend (agreeing by construction), spatial extent to boundingBox, and the temporal extent written as a time policy - an ongoing collection (null extent end) gets dataEndTime 'now' plus the collection's granularity and periodicity (dashboard:time_interval / is_periodic), per #332's vocabulary. The caller chooses render and time mode; nothing is guessed - renders-less collections are refused with a named error. 21 specs on fixtures recorded from the live dev catalogs, including the ongoing-extent case. Engine half of #333.
Two admin-only routes beside the existing STAC ones: POST /api/stac/vedastac/inspect returns the facts a human decides from (renders with parameters, temporal coverage with null-end-as-ongoing, granularity/periodicity, preselections); POST /api/stac/vedastac/fill maps their choices to TileLayer fields via the pure engine. At most two upstream requests per fill (collection, plus the colormap only when a legend is possible), injectable fetch with timeouts, validated inputs, warnings surfaced in the response. Server-side so the layer editor needs no CORS against the catalog. Fake-fetch specs cover the facts shape, choice pass-through, and every degradation path. Backend half of #333.
…ction Two-step flow in the tile layer editor's Actions row: Look Up fetches collection-level facts and shows what the collection offers - its renders (one distinct option reads as a fact, several become a dropdown, none disables filling) and its temporal coverage with granularity - then the admin's two choices (render, time-enabled vs static, both preselected) fill the form in one update. Ongoing collections arrive with end 'now' per #332, so the layer stays current as data grows. Identity, analysis, and untouched fields keep their values; everything stays editable. UI half of #333.
BhattaraiSijan
marked this pull request as ready for review
August 26, 2026 20:14
BhattaraiSijan
marked this pull request as draft
September 8, 2026 15:57
BhattaraiSijan
added a commit
that referenced
this pull request
Sep 10, 2026
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`/`dataEndTime` may now be a **policy** re-evaluated on demand: `now`, or `now ± <ISO-8601 duration>` (`now - P1D`, `now + P5D`). Concrete datetimes behave exactly as before. Per veda-ui (our semantics standard), `now` resolves to the raw current moment — never rounded. But raw `now` is 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 × interval` for 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 (or `null`) — for one layer by id, or for all layers keyed by UUID, in the same call shapes as `layers:getCogCapabilities` and `layers:getBounds`. It resolves at the moment of asking, so an open-ended `now` is 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, and `start + N × interval` is computed as one calendar operation, so a monthly cadence stays anchored to the start's day-of-month. `isPeriodic` was dropped from an earlier revision of this PR as unused (review feedback) — correctly so: `interval` alone carries the fact, and its presence is what marks a layer periodic. #338's VEDA source can fill it automatically from `dashboard:time_interval`; authors can type it by hand today. ## Commits 1. **The policy resolver** — one pure function: policy string in, resolved ISO datetime out, `null` for anything unparseable. 2. **The Timeline consumes it** — replacing its hand-parsed inline `'now'` special case. 3. **Resolution moves into core** — the resolver relocates to `Basics/TimeControl_/layerTimePolicy.ts`; `Layers_` registers `layers: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 did `new Date("now")` and produced `NaN`. 4. **Periodic flooring** — `resolveTemporalExtent` floors a periodic layer's end to its last data step. Constant-time: N is seeded from a millisecond approximation, then settled by calendar math. 5. **Core wires through it** — `layers:getTemporalExtent` and the Layers tool answer cadence-aware; the Timeline gets it with zero changes. 6. **Configure** — a Data Time Interval field beside the data start/end times in the tile and vector layer forms. ## Testing - 28 unit specs with an injected clock: all three policy forms, sub-day and calendar-unit offsets, the 7-day-cadence-ends-three-days-ago scenario, ends exactly on / off a step, month-end anchoring, end-before-start clamping, and every failure mode (`P0D` included) degrading to an unsnapped extent or null. Full suite green (124 files, 1790 tests); typecheck clean. - Verified live: an ongoing layer configured with end `now` draws its timeline bar reaching the current date (screenshot in #332), while a closed-extent layer's bar correctly stops at its real end — and the `now` config 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](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #333 (part of epic #331). Builds on the time-policy vocabulary from #332's PR (independent code; best merged after it so the written
nowpolicies render).Summary
Adding a VEDA dataset meant hand-collecting facts a machine can read — tile endpoint, render parameters, temporal coverage, bbox, description — and typing them into the layer form. The tile layer editor's Actions row now has VEDA STAC Source: a two-step dialog that looks a collection up, shows what it offers, and fills the layer from the collection's own metadata.
2024-12-18 → ongoing · P1D).dashboardrender per VEDA convention; a span or open end suggests time-enabled).datetime={starttime}/{endtime}, static omits the filter), a legend sampled from the render's colormap across its rescale range (legend and tiles agree by construction), bounding box, description, and the time block — where an ongoing collection (null extent end) getsdataEndTime: "now"plusinterval/isPeriodic, so the layer stays current as data grows.The system decides nothing silently: collections without renders are refused with a clear message (no asset guessing), band counts are never inferred, analysis support and layer identity (uuid) are untouched, and every filled field stays editable.
Commits
scripts/lib/vedaStacLayer.js), 21 specs on fixtures recorded from the live catalogs.POST /api/stac/vedastac/inspect+/fillbeside the existing STAC routes (admin-only, validated inputs, at most two upstream requests, warnings surfaced); fetch orchestration with injectable fetch, 6 fake-fetch specs.Testing
27 unit specs; full suite green. Verified live against both dev catalogs (screenshots in #333):
sentinel2-nbr-daily): concrete dates,P1D, gray legend matching the tiles.landsat-ndvi-daily) → fact line; multi-render (opera-displacement-daily,opera-dswx-daily,sentinel-1-coherence-yearlywithP1Y) → dropdown with the VEDA default preselected.blackmarble-all-vars-daily-addMetadata): fills with endnow— its timeline bar reaches the current date, while closed collections' bars correctly stop at their real ends.Not yet exercised (follow-up verification planned): the no-renders refusal, no-temporal-extent collections, very large render lists, missing
dashboard:time_interval, and the error paths (bad id, unreachable raster root) — all covered by unit specs, pending eyes-on checks.🤖 Generated with Claude Code