Repository navigation
Sync development to production (2026-11-06) - #604
Merged
Merged
Conversation
A hovered polygon keeps its fill: the fill sub-layer gets a zero-alpha highlight, so only outlines and lines take the highlight colour. Add a Hover Highlight Stroke Width option. When set, the outline and line sub-layers are drawn by HighlightWidthPathLayer, a PathLayer whose vertex shader widens the highlighted path to that many pixels.
…ghlight geometry.pickingColor holds raw byte values in DECKGL_FILTER_SIZE; the picking module normalizes it only later, in DECKGL_FILTER_COLOR, so the highlight test never matched and the hovered stroke kept its width.
Replace HighlightWidthPathLayer with HighlightLineWidthExtension, set on the MVTLayer itself. Extensions on a composite layer pass down to every sub-layer alongside the ClipExtension MVTLayer adds per tile; the extension injects its shader only into PathLayers, so it no longer depends on GeoJsonLayer's sub-layer ids.
…579) ## Summary A layer's time series does not always run at the layer's own cadence. On Air4US every AQS layer declares `time.interval: PT1H` for its hourly map tables, while its time series is drawn from the daily tables. The range card's pickers were hardcoded to the hour for everyone, so a daily series offered a clock that meant nothing (the Air4US review asked for the hour to go). The timeseries block now declares its own **Cadence**: `hour` (the default, today's behavior), `day`, `month` or `year`, authored in Configure's Timeseries row next to Default Range. The card's Start and End pickers step at it, using the Timeline DateSelector's matching mode. A picked Start opens its step and a picked End closes it, so a day runs `T00:00:00` to `T23:59:59`, a month its first to its last day, a year Jan 1 to Dec 31; End never passes its ceiling (today). Hour mode is unchanged: picks to the minute, `…:00` and `…:59`. An unreadable value warns and keeps the hour picker, as an unreadable Default Range does. This is the first of two slices. The second carries the cadence in the chart-series payload so the chart's tooltip and zoom-window labels print at the same precision, replacing the date-only tooltip from #569. ## What's in the diff - `FetchTimeseries/lib/timeseries.ts`: `cadence?` on `TimeseriesConfig`; `parseCadence`, `cadenceStart`, `cadenceEnd`, `DEFAULT_TIME_MODE`. - `FetchTimeseries/FetchTimeseriesTool.tsx`: the mode is read where the range is seeded for a layer and handed to the card. - `FetchTimeseries/lib/components/RangeCard.tsx`: `timeMode` prop, passed to both pickers; picks open and close their step. - `configure/src/metaconfigs/layer-vector-config.json`, `layer-vectortile-config.json`: a "Cadence" dropdown after "Default Range". - `FetchTimeseries/README.md`. - Tests: `fetchTimeseries.spec.js` (`parseCadence`, step open and close for the four modes, year end), `fetchTimeseriesTool.spec.js` (day cadence shows days and runs picks over whole days; month cadence runs 1st to last and caps at today; unreadable cadence warns and keeps the hour picker). ## Testing - `npm run test:unit` green (210 files, 3600 tests) and `tsc --noEmit` clean. - Manual, on the Air4US mission: in Configure, open the PM2.5 layer's Time tab and set Cadence to "day" in the Timeseries row; save. On the map, click a site and press the time series action. Expect Start and End as days only (`Sep 25, 2025`, no clock). Pick a Start day: the request filter reads `datetime >= '<day>T00:00:00'`. Pick an End day: `datetime <= '<day>T23:59:59'`. Set Cadence to "month": the pickers show `September 2026`, and End picked as the current month stops at today. ## Not in this PR - Slice 2: the payload carries the cadence and the chart follows it. - The seeded range is not aligned to the cadence: a month cadence seeds Start at `2025-09-25`, shown as `Sep 2025`, until the first pick. - Hour mode still picks to the minute; whether an hourly series should snap to the hour is open. - The mission config: the five AQS layers need `cadence: day`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
) ## Summary From the Air4US review of the station time series: every site's CSV download was named after the variable alone (`24-HR_BLK_AVG.csv`), so two sites' files could not be told apart and the pollutant was nowhere in the name. The file is now named after the card: title, section heading and variable, `Washington_Georgia_Site_1_AQS_Surface_PM2.5_Concentration_24-HR_BLK_AVG.csv`. Long, but every part is what the card shows, and on the AQS layers the pollutant only exists in the heading (the variable is the sample duration). Each part is reduced to word characters, dots and dashes. Names are NFKD-normalized first, so `PM₂.₅` survives as `PM2.5` rather than `PM_._`. A missing part is skipped, and so is a part that repeats the one before it: an ungrouped series is labelled with the layer name, which is also the heading, and the file should not say it twice. ## What's in the diff - `SeriesChart/lib/chartData.ts`: `csvFileName(parts)`. - `SeriesChart/lib/components/SeriesChartPanel.tsx`: the card builds the name from `payload.title`, `payload.subtitle` and the picked series' label and hands it to the footer; `downloadCsv` takes the name. - `SeriesChart/README.md`. - Tests: `seriesChartData.spec.js` (the three parts with subscripts, missing and repeated parts, accents, fallback). ## Testing - `npm run test:unit` green (210 files, 3600 tests) and `tsc --noEmit` clean. - Manual, on the Air4US mission with `Air4US_v182_timeseries-title.config.json`: click a PM2.5 site in Washington County, GA, press the time series action, Download CSV. Expect `Washington_Georgia_Site_1_AQS_Surface_PM2.5_Concentration_24-HR_BLK_AVG.csv` (or `…_1_HOUR.csv` for that variable). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
… date A side pinned to a date clones the live deck layer and overrides only its source, so it inherited the primary map's visibility. A layer with no data at the global instant is hidden on the primary map, and its clone stayed hidden on a side pinned to a date it does have data for: Landsat at Jan 11, 2025 against Jan 6. The pinned patch now carries the layer's visibility at the pinned date, by the rule TimeControl applies at the global instant: on, and not out of coverage. Layers the coverage gate does not reach keep the live layer's visibility.
deck.gl stops drawing a layer marked visible: false but still runs its lifecycle, so a hidden tile layer handed to a comparison side fetched a viewport of tiles it never drew. Each side now leaves hidden clones out, as the primary map's sync already does.
A side-by-side pane's map is built with its style still loading, and mapbox-gl throws from getStyle() until it lands. Entering side-by-side straight away threw out of MapComparison.enable before the divider, captions and enabled flag went up, so Exit returned early and the map stayed split until reload. - DeckGLAdapter: _anchorBelowLabels sends a still-loading map's layers unanchored; the pane's style.load re-sends them under the labels. - MapComparison: enable raises the divider, captions and enabled flag even when the engine throws; disable also tears down a split the engine reports as on; setLayout re-marks the divider either way. Closes #589
…unners ubuntu-latest moved from Ubuntu 24.04 to 26.04 on 2026-10-26. Playwright 1.57.0 has no Chromium build for 26.04, so every e2e job has failed at "Install Playwright Browsers" since. Support arrived in Playwright 1.61.
…nners (#593) ## Line breakdown | Category | Lines added | % | |---|---|---| | Generated (`package-lock.json`) | 26 | 96.3% | | Config/build (`package.json`) | 1 | 3.7% | This PR upgrades Playwright from 1.57.0 to 1.64.1, because GitHub moved `ubuntu-latest` to Ubuntu 26.04 on 2026-10-26 and 1.57.0 has no Chromium build for it, so every e2e job has failed at "Install Playwright Browsers" since then. ## What this does - **E2e jobs run again:** on `ubuntu-latest`, `npx playwright install --with-deps chromium` succeeds and the smoke, eventbus-integration and deployment-mode specs run against Chromium. - **Playwright is 1.64.1 everywhere:** `@playwright/test`, `playwright` and `playwright-core` are all pinned to 1.64.1 in the lockfile, so `npx playwright --version` reports 1.64.1 after `npm ci`. - **Node 20 is now the floor:** Playwright's engine requirement moves from >=18 to >=20, which CI, the Dockerfile (`node:20-slim`) and `.nvmrc` (20.11.1) already meet. - **Nothing else changes:** the workflow file, `playwright.config.js` and all test code are untouched. ## Decisions to review - **Bump Playwright instead of pinning the runner to `ubuntu-24.04`:** this ships a new Chromium build and seven minor releases of Playwright changes, so an e2e failure after merge could come from the upgrade rather than the app. Leaving the job on `ubuntu-latest` also means the next runner image change can break CI the same way.
#591) ## Summary Fixes #588. Comparing Jan 11, 2025 against Jan 6, 2025 on the Disasters mission, Landsat Natural Color was blank on the Jan 6 side even though Jan 6 has data. Its Jan 6 tiles were fetched successfully but never drawn. Swapping the dates did the same to AVIRIS-3. A comparison side pinned to a date clones the live deck layer and overrides only its source. It therefore inherited the primary map's visibility, and the primary map hides a time-enabled layer at an instant it has no data for. The pinned side now decides visibility from the layer's coverage at its own date, by the same rule TimeControl applies to the primary map: on, and not out of coverage. Layers the coverage gate never hides (`dynamicExtent`, controlled) keep the live layer's visibility. Untimed layers and layers whose source cannot be pinned are untouched. The second commit stops each side from handing deck.gl the layers it hides. deck.gl does not draw a `visible: false` layer, but it still runs the layer's lifecycle. Before this commit a hidden tile layer on a side fetched a viewport of tiles it never drew: about 20 per hidden layer every time a comparison was set up. The primary map's sync already leaves hidden layers out, and the sides now do the same. ## What's in the diff - `Map_/comparisonTimePins.ts`: each pinned patch carries `visible`, from `L_.layers.on` and `evaluateLayerDataCoverage` on the pinned config, gated by `isCoverageGated`. - `MapEngines/Adapters/DeckGLAdapter.ts`: `_comparisonClonesFor` drops clones that are `visible: false` on their side (swipe canvases and side-by-side panes). - Tests: - `comparisonTimePins.spec.js`: in coverage at the pin, out of coverage at the pin, no declared coverage, switched off, not reached by the gate. - `deckGLComparisonProps.spec.js`: a hidden layer is left out of its swipe side and its side-by-side pane. ## Testing - `npm run test:unit` green (210 files, 3606 tests) and `tsc --noEmit` clean for the changed files. - Manual, on the Disasters mission over Los Angeles with Landsat Natural Color and AVIRIS-3 False Color Early Look RGB on: 1. Set the timeline to Jan 11, 2025, open Compare date and pick Jan 6, 2025. 2. Expect Landsat imagery on the Jan 6 side and AVIRIS-3 on the Jan 11 side. 3. Swap the sides: each layer follows its date. 4. Since the comparison started, the only tile requests are AVIRIS for Jan 11 and Landsat for Jan 6. There are none for AVIRIS on Jan 6 or Landsat on Jan 11. ## Not in this PR - #589: choosing side-by-side before a date leaves the map split with no divider, and Exit can't clear it. - #590: the panel keeps showing a comparison after something else ends it. - 503s from the tile server under comparison load. - A time-enabled layer that has never been drawn on the main map may be missing from both sides (noted in #588).
…#592) Closes #589 Choosing side-by-side before a date left the map split with no divider, and Exit couldn't clear it. The new panes' styles were still loading, so `getStyle()` threw and `MapComparison.enable` stopped before the divider went up. - `DeckGLAdapter`: a map whose style is still loading gets its layers unanchored; they're placed under the labels once the style loads. - `MapComparison`: the divider and the enabled state go up even if the engine throws, and Exit also clears a split the engine still has on. Unit tests cover both. To check by hand: Compare date → side-by-side → pick Date 2, then Exit.
…fills - Skip the hover line width in the picking pass so the hover target matches the styled stroke and neighbouring features stay hoverable. - Give the polygon fill a one-unit-alpha highlight instead of zero, which kept luma's highlight blend from dividing 0 by 0 on transparent fills. - Keep the fill tint for polygons whose outline would not show on hover (weight or opacity 0), so they still give hover feedback.
…507) ## Purpose With hover highlight on, a hovered vector tile polygon tinted fill and outline together, so a highlight strong enough to see on the outline also washed over the whole shape. This keeps the fill as styled and highlights only the stroke, and adds an option for the hovered stroke's width. ## Proposed Changes - [CHANGE] The highlight colour applies to outlines and lines only. Each tile's GeoJsonLayer draws a polygon's fill and outline as separate sub-layers; the `polygons-fill` sub-layer gets a zero-alpha `highlightColor` through `_subLayerProps`, which deck.gl blends in as nothing. - [ADD] **Hover Highlight Stroke Width** (`style.hoverHighlightWidth`), a deck.gl-only field on vector tile layers. When set, the MVTLayer gets `HighlightLineWidthExtension` and a `highlightLineWidth` prop. - [ADD] `HighlightLineWidthExtension`, a deck.gl `LayerExtension` that adds a `DECKGL_FILTER_SIZE` shader hook to PathLayers: a vertex whose picking colour marks it as highlighted (`isVertexHighlighted`, the same test the colour highlight uses) is drawn at the configured pixel width. It follows the hover without rebuilding the layer or regenerating width attributes. - [ADD] Tests for the untinted fill, the extension and width being set, and widths that should be ignored (empty, text, 0, negative). ## Issues - Closes #508 ## Testing - `npx vitest run`: 187 files, 3113 tests pass. - Live, on this branch's dev build, AirQuality mission, HMS Smoke Plume Polygon on Aug 20, 2026, deck.gl engine: every tile's sub-layers carry `HighlightLineWidthExtension` alongside MVTLayer's `ClipExtension`. Hovering a plume with a 12px opaque highlight draws a thick outline in the highlight colour, and the plume's fill stays as styled. ## Notes for review - **Set on the MVTLayer, not through `_subLayerProps`.** Extensions on a composite layer pass down to every sub-layer, and MVTLayer appends its per-tile `ClipExtension` to them. Setting `extensions` through `_subLayerProps` instead would replace the ones the parent hands down, dropping that clipping. The extension returns no shader for non-path layers, so fills and points compile unchanged. - **The picking colour is normalized in the hook.** `geometry.pickingColor` holds raw byte values at `DECKGL_FILTER_SIZE`; the picking module normalizes it only later, in `DECKGL_FILTER_COLOR`. Without normalizing, the highlight test never matches. - **Points are unchanged.** `ScatterplotLayer` draws a point's fill and stroke in one sub-layer, so a hovered point still tints whole and keeps its styled stroke width. - **The default highlight is faint on strokes.** With no colour configured, the highlight is black at 10% opacity, which was chosen for fills. On a 1–2px outline it barely shows. Missions using hover highlight will likely want to set a colour.
## Summary Second half of the series cadence (#579). That PR declares the cadence in the layer's timeseries block and makes the range pickers step at it. The chart still printed every time as a date only, hardcoded in #569 for the daily Air4US series, so an hourly series lost its clock and a monthly one showed a day that meant nothing. The cadence now travels on the chart-series payload, and the chart prints the tooltip title and the zoom window's end dates at that precision: `Feb 28, 2026, 14:00` for hour, `Feb 28, 2026` for day, `Feb 2026` for month, `2026` for year. A payload without one prints the date, today's behavior. Axis ticks are untouched. FetchTimeseries sends `cadence` only when the layer declares a readable one. An unset layer keeps both of today's defaults, hourly pickers and a date-only chart, rather than switching the chart to clocks. ## What's in the diff - `_shared/types/chartSeries.ts`: `ChartCadence` and `cadence?` on the payload; the guard rejects a value outside the four words, like any other malformed payload. - `FetchTimeseries/lib/timeseries.ts`: `buildPayload` copies the parsed config cadence onto the payload, lowercased, when there is one. - `SeriesChart/lib/chartData.ts`: `formatTooltipTime(ms, cadence)` over one formatter per cadence; `buildChart` and `buildChartOption` take the cadence and hand it to the tooltip and the window labels. - `SeriesChart/lib/components/SeriesChartPanel.tsx`: the canvas receives `payload.cadence`. - READMEs for both plugins. - Tests: `chartSeries.spec.js` (accepted word, rejected word), `seriesChartData.spec.js` (each cadence's format, the hourly tooltip title, window dates by month and by hour), `fetchTimeseries.spec.js` (declared cadence rides, unreadable or absent is not sent), `fetchTimeseriesTool.spec.js` (the emitted payload carries it, and nothing when unset). ## Merge order Stacked on #579 and targets its branch; retarget to development once #579 merges. Independent of the CSV name PR, though both touch `chartData.ts` and the panel on nearby lines, so whichever lands second gets a small rebase. ## Testing - `npm run test:unit` green (210 files, 3602 tests) and `tsc --noEmit` clean. - Manual, on the Air4US mission: with the PM2.5 layer's Cadence set to "day" (from #579), click a site and press the time series action. Expect the tooltip titled `Feb 28, 2026` and date-only window labels, as today. Set Cadence to "hour", save, reload, press again: the tooltip reads `Feb 28, 2026, 00:00` and the window labels carry the clock. Set it to "month": `Feb 2026`. ## Not in this PR - Axis ticks still pick their own format from the visible span. - Other fetchers (the AOI statistics chart) send no cadence and keep the date-only chart until they set one. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## What In the Event theme of the LayerFilter panel, a picked activation now shows an **EGIS services** link beside its date range. It opens the activation's EGIS portal group in a new tab. Activations without a URL render exactly as before. ## How - Activations declare one optional flat property, `properties.egis_url`. Presence alone decides display: no link array, no show/hide flag. - `buildEntryDisplays` passes it through as `egisUrl`, kept only when it is an `http(s)` string. Anything else (`javascript:`, numbers, empty) is treated as absent. - `FilterPanel` renders the anchor in the existing entry meta row after the date range and the ACTIVE pill, `target="_blank" rel="noopener noreferrer"`, with a masked open-in-new glyph. The row now also appears for an entry that has a URL but no dates. - Styles: dates take `margin-right: auto` and the row is `justify-content: flex-end`, so ACTIVE and the link sit right-aligned together. Theme tokens only. - No parser or engine change: `parseCatalog` already carries unknown properties through. - Catalogue field description in `config.json` and SCHEMA.md §5 document the property. ## Config Six Disasters activations get an `egis_url` (CA Fire, Helene, Milton, Winter Wx, Sinlaku, Venezuela). Texas Flood has no EGIS group. Hurricane Isaias is not in the catalogue yet and needs its own entry before it can carry its URL. ## Tests - `__tests__/entryDisplay.spec.ts`: http(s) passthrough, absent → null, rejected values. - `__tests__/FilterPanel.spec.tsx`: anchor href/target/rel/text, no anchor without a URL, meta row for URL without dates. - Full unit suite: 3630 passed. `npm run typecheck` clean for LayerFilter. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## What On the deck.gl engine, a click that opens the FeaturePopup card now also outlines the clicked feature on the map: a white casing under a primary-color ring, no fill, click-transparent, drawn through the existing `map:createLayer` handler the way AOI draws its selection. Points get a ring around the marker. The outline outlives the card. Pressing an action (Fetch timeseries, Analyze) or the close button leaves it in place, so the feature the chart or analysis is about stays marked. It moves on the next click that opens a card and clears on a click that opens none (empty space, or a layer without the popup opt-in). Teardown removes it. Nothing in core changed. Leaflet is untouched (the plugin is inactive there and the per-feature click path already restyles the clicked shape). ## Why With the card closed and the chart open there was no way to tell which station the chart described. ## Known limit Vector-tile features come back from the pick as a tile-grid copy, so the outline drifts off the dot after zooming in and a polygon crossing a tile edge is only outlined on the clicked tile's piece. Tracked separately: the engine-side highlight that renders from the tiles on screen. ## Tests - New `src/essence/Tools/FeaturePopup/__tests__/FeaturePopupTool.spec.js`: casing-then-ring creation, replacement under the same ids, the three removal paths, survival across dismiss and action, removal on destroy. - Full unit suite green (211 files, 3605 tests). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary - Layer info popover body text (`.blocks-layer-legend__info-popover`) uses `--theme-font-size-2xs` (0.88rem fallback) instead of `--theme-font-size-3xs` (0.75rem), so layer descriptions are easier to read. ## Notes - The popover title and markdown headings are also 2xs, so the title is set apart only by its semibold weight and ink color. ## Test plan - [ ] Open the Layer Manager, click a layer's info button, and confirm the description text renders at the larger size in each brand.
sandesh-sp
temporarily deployed
to
development
November 5, 2026 17:30 — with
GitHub Actions
Inactive
sandesh-sp
temporarily deployed
to
development
November 5, 2026 17:30 — with
GitHub Actions
Inactive
The card was anchored to the click location, which sits anywhere inside the rendered marker and drifts off the point as the map zooms in. A point feature now anchors to its own geometry, a multipoint to the member nearest the click; lines and polygons keep the click. Vector tiles snap positions to a grid that coarsens at lower zooms, so a point read zoomed out is still off once zoomed in. After each move, and once the clicked layer has loaded every tile in view, the point is looked up again in the tiles drawn now and the card and pin take its finer position. A layer can instead name the properties holding each feature's exact latitude and longitude (Latitude Property / Longitude Property in Configure), which the card and pin anchor to directly. Core additions on the event bus: - map:queryRenderedFeatures: features drawn near a lat/lng, as plain GeoJSON - map:movePopup: re-anchor the open card without reopening it - layer:viewportLoaded: a deck.gl vector tile layer has every tile the view needs The deck.gl adapter's queryRenderedFeatures now picks through the overlay on a basemap, where it previously always returned nothing, and takes a radius for point queries.
deck.gl fires onViewportLoad each time the drawn tile set changes and has fully loaded, cached views included, so it alone covers every view worth re-reading. Drop the map:moveend look, the settled flag and zoomRead: a look now runs when the map is zoomed in past the point's zoom, and a match sets that zoom. Trim the comments, Configure tooltips and plugin description, drop a try/catch in MapPopup_.move that cannot fire, and merge overlapping re-anchor tests.
Follow-up to #603. The link beside a picked activation's dates read "EGIS services"; it now reads "View GIS Services" in the panel, the config description and the schema doc, and the test asserts the new text.
BhattaraiSijan
temporarily deployed
to
development
November 5, 2026 21:08 — with
GitHub Actions
Inactive
BhattaraiSijan
temporarily deployed
to
development
November 5, 2026 21:08 — with
GitHub Actions
Inactive
The ACTIVE pill sat between the date range and the View GIS Services link, and broke mid-word when all three shared the row. It is now a dot-and-word status grouped with the dates, in the theme primary color, that drops under the dates as one unit when space runs short.
Multi-select filters drop the checkbox rows for the same rows single select uses: counts beside the label, every held value highlighted and ticked, and an All row that clears the selection. Picking a value toggles it and keeps the menu open.
#620) Pins the View GIS Services link to the theme primary color in its visited, active, hover and focus states so it no longer changes color after being clicked.
sandesh-sp
temporarily deployed
to
development
November 6, 2026 16:13 — with
GitHub Actions
Inactive
sandesh-sp
temporarily deployed
to
development
November 6, 2026 16:13 — with
GitHub Actions
Inactive
…es (#622) Closes #621 ## What Picking an ongoing event in the Event theme showed an ACTIVE pill wedged between the date range and the View GIS Services link. When all three shared the row, the pill broke mid-word ("ACTI / VE"). The pill is replaced with a compact status (a pulsing dot + "Active") grouped with the dates on the left: - Dates and status wrap together as one unit; the status never breaks and drops under the dates when the row is short of room. - The GIS link stays on the right, aligned with the dates line. - Colors come from the theme primary tokens; the pulse is disabled under `prefers-reduced-motion`. ## Testing - `npx vitest run src/essence/Tools/LayerFilter` — 13 passed. - Checked in the DisastersTool mission with an entry set to `end_datetime: null` (active + EGIS link): status sits under the dates, link level with the dates. An entry with a link only looks unchanged. - Not checked at the panel's minimum width. ## Notes - To reproduce, the picked entry needs both an `egis_url` and a null/future `end_datetime`.
## What Multi-select filter menus (e.g. Air4US Pollutant) now render like single-select ones (e.g. Disasters Hazard) instead of native checkbox rows. - **Same rows.** Each value uses the single-select row: count badge beside the label rather than pushed hard right. - **Selection marking.** Every held value gets the selected background and the tick; `aria-pressed` carries the state for assistive tech now that the checkbox is gone. - **All row.** An "All …" row heads the menu, ticked when nothing is held; clicking it clears the selection. - **Toggle in place.** Picking a value adds or removes it and keeps the menu open for the next pick. Single select still closes on pick. The unused `&__option` checkbox-row styles are removed. ## Testing - Checked in the running app: Air4US Pollutant menu matches the Disasters Hazard menu; picking CO then NO₂ kept the menu open, ticked and highlighted both, and the trigger read "CO, NO₂ (2)". - LayerFilter specs green (3 files, 13 tests).
## What On the deck.gl engine, the FeaturePopup card and pin point at the clicked feature's own position rather than wherever the click landed inside the marker. - **Point anchor.** A point anchors to its geometry, a multipoint to the member nearest the click. Lines and polygons keep the click. - **Re-anchoring on zoom.** Vector tiles snap positions to a grid that coarsens at lower zooms. When the clicked layer has loaded the tiles for a view zoomed in past the anchor's zoom, the point is looked up again and the card and pin take its finer position. - **Exact position from properties.** A layer can name the properties holding each feature's exact latitude and longitude (Latitude Property / Longitude Property in Configure). The card and pin anchor there, with no re-anchoring. Core additions on the event bus: - `map:queryRenderedFeatures`: features drawn near a lat/lng, as plain GeoJSON - `map:movePopup`: re-anchor the open card without reopening it - `layer:viewportLoaded`: a deck.gl vector tile layer's drawn tiles have all loaded The deck.gl adapter's `queryRenderedFeatures` picks through the overlay on a basemap, where it used to return nothing, and takes a radius for point queries. ## Why The card was anchored to the click location, which sits anywhere inside the rendered marker and drifts off the point as the map zooms in. A point clicked zoomed out on a vector tile layer was also off by the tile's snapping error. ## Notes - `layer:viewportLoaded` fires each time the drawn tile set changes and has fully loaded, cached views included, so it is the only trigger for re-anchoring. - `map:queryRenderedFeatures` reads `geometry.coordinates` out explicitly: on a vector tile pick a JSON round-trip of the geometry drops them. - Latitude/Longitude Property are offered on plain vector layers too, for parity, though GeoJSON geometry is already exact. ## Tests - `FeaturePopupTool.spec.js`: point and multipoint anchors, exact-property anchors, re-anchoring on `layer:viewportLoaded` (other layers ignored, no look without a zoom gain, the radius measured from the last found zoom), actions carrying the re-anchored position, the pin moving alone once the card closes. - `MapPopup_.spec.ts`, `deckGLAdapter.spec.js`, `LeafletAdapter.spec.js`: `movePopup` and radius queries. 267 tests across the four suites pass. - Manual, Air4US AQS PM₂.₅ layer: clicked a station at zoom 5, zoomed to 12; the card and pin moved from (-109.56116, 40.46367) to (-109.56148, 40.46471), the station's recorded position.
sandesh-sp
temporarily deployed
to
development
November 6, 2026 16:30 — with
GitHub Actions
Inactive
sandesh-sp
temporarily deployed
to
development
November 6, 2026 16:30 — with
GitHub Actions
Inactive
A time series split by groupBy can give each series its own display name and chart title. Buttons keep the raw value; legend, hover text, footer chip and CSV header read the label, and the card title follows the picked button. Code half of Air-Quality-Portal/aq-arch#212.
…ies (#624) ## What A time series split by `groupBy` can now give each series its own display name and its own chart title. The duration buttons keep their raw values, the legend, hover text, footer chip and CSV header read the configured label, and the card title switches with the picked button. Code half of Air-Quality-Portal/aq-arch#212. The per-layer config (URL filters, `yKey`, the `groups` rows) is applied separately in Configure. ## How New optional `groups` on `variables.timeseries`, one row per `groupBy` value: ```json "groups": [ { "value": "1 HOUR", "label": "First Daily Maximum", "title": "First Daily Maximum AQS Surface CO Concentration" } ] ``` - **Contract** (`_shared/types/chartSeries.ts`): `ChartSeries` gains optional `displayLabel` and `title`. `label` is unchanged and still the uniqueness key, so the guard is untouched. - **FetchTimeseries** (`lib/timeseries.ts`): `buildPayload` matches each series' key to a `groups` row (exact, then case-insensitive; malformed rows ignored) and stamps `displayLabel`/`title`. The truncation note rides on the series title too. When any row carries a title, the payload `subtitle` becomes the feature title (site) instead of the layer name, since the group title already names the layer. - **SeriesChart**: header uses `picked.title ?? payload.title`; footer chip, tooltip series name and CSV header use `displayLabel ?? label`; buttons and dropdown keep `label`. CSV file name follows the same fallbacks. - **Configure**: `Series Labels` objectarray (`value`, `label`, `title`) under the Timeseries row in both layer-vector and layer-vectortile metaconfigs, so the key survives a save. - Docs: FetchTimeseries README table + config example, SeriesChart README contract note. Title priority: 1) picked series' group title, 2) the layer's Title template filled from the feature, 3) Title Property → name → title → id → layer name, as before. Layers without `groups` render exactly as today (no new keys on the payload). ## Tests - `tests/unit/fetchTimeseries.spec.js` (+5): matched row stamps both fields and moves the site to the subtitle; no groups → unchanged shape; label-only row; case-insensitive match + malformed rows ignored; truncation note on the series title. - `tests/unit/seriesChartAdapter.spec.js` (+2): buttons keep raw labels while header and footer follow the pick; a series without a title falls back to the payload title and its label. - Full unit suite: 3650 passed. `tsc --noEmit` clean for the touched files. ## Live check (after merge to a dev instance) Add a `Series Labels` row to one AQS layer in Configure, click a site, open Time Series: title and footer switch per button, tooltip name matches the footer, CSV header uses the label, buttons unchanged, export retains `groups`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
BhattaraiSijan
temporarily deployed
to
development
November 6, 2026 17:25 — with
GitHub Actions
Inactive
BhattaraiSijan
temporarily deployed
to
development
November 6, 2026 17:26 — with
GitHub Actions
Inactive
… map Anchored HTML overlays such as the measure label reposition on the engine's move event, which the deck.gl adapter only raised once the camera settled. The basemap and side-by-side pane camera handlers now emit move during a gesture and moveend at its end. Closes #625
… map (#626) ## What The measure distance label, and any other HTML overlay pinned to a map point, now moves with the deck.gl map on every frame of a pan, zoom or fly-to. Before this change it froze at its old screen position and only jumped into place once the map settled. Closes #625 ## How `DeckGLAdapter.addOverlay` repositions its node on the engine's `move` and `moveend` events, but the deck.gl adapter only ever raised `moveend`: - `_onBasemapMove` kept `_viewState` in sync on every frame but emitted nothing. It now emits `move` after updating `_viewState`, so the overlay projects against the current camera. - `_onPaneCameraChange` (side-by-side panes) emitted only when settled. It now emits `move` for frames during a gesture and `moveend` at the end. `moveend` is unchanged, so listeners for camera-settled fire exactly as before. The bus event `map:move`, which `Map_` forwards from the engine's `move`, fires on deck.gl too now; it had no subscribers before. ## Tests - Full unit suite: 3673 passed. There are no unit tests for the adapter. - Live check (DisastersTool, deck.gl): pinned a probe overlay and ran an animated `map:fitBounds`. The map raised 21 `map:move` events alongside 3 `map:moveend`, and the overlay's `left` updated mid-animation instead of only at the end. - Not yet checked by hand: dragging with the Measure tool, and the side-by-side comparison view.
sandesh-sp
temporarily deployed
to
development
November 6, 2026 19:34 — with
GitHub Actions
Inactive
sandesh-sp
temporarily deployed
to
development
November 6, 2026 19:34 — with
GitHub Actions
Inactive
sandesh-sp
approved these changes
Nov 6, 2026
This branch was previously deployed
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.
Promotes
developmenttoproduction. Everything merged since the last sync (#572, 2026-10-15): seventeen PRs, 51 files, undersrc/,tests/,configure/srcanddocs/, plus a devDependency bump. No deploy, workflow or environment files change.What changed
style.hoverHighlightWidthhour,day,month,year). The range card's Start and End pickers step at it; a picked day runs 00:00:00 to 23:59:59 UTC, a month its first to last dayFeb 28, 2026daily,Feb 28, 2026, 14:00hourly,Feb 2026monthly)map:queryRenderedFeatures,map:movePopupandlayer:viewportLoadedto the event bus@playwright/test1.57 to 1.64.1 so Chromium installs on Ubuntu 26.04 runners (devDependency only)egis_urlshows a "View GIS Services" link, opened in a new tab, beside its date range; the link keeps the theme primary color after it is clickedConfig changes required after this is live
The new keys are read by the new build only, and the Configure modal on the current production build drops any key its metaconfig does not list. So make the Configure edits in steps 1 and 3 after this deploys.
Air4US: set Cadence to
dayon the five AQS timeseries layers: PM₂.₅, O₃, CO, NO₂, SO₂ (Layers → layer → Timeseries → Cadence). Without it the pickers step hourly against daily series and the chart tooltip falls back to date-only. Leave PM₁₀ and the two HMS layers alone; their timeseries blocks have no URL.Disasters: add
egis_urlto the seven catalogue activations in the LayerFilter tool's Catalogue (GeoJSON) field, one per entry'sproperties:202501_Fire_CA→https://gis.earthdata.nasa.gov/portal/home/group.html?id=c3a8e74d6c374e4db0e4f9185f44d876#content202409_Hurricane_Helene→https://gis.earthdata.nasa.gov/portal/home/group.html?sortField=added&sortOrder=desc&id=c59e5358c422416cbad77c736524b1fc#content202410_Hurricane_Milton→https://gis.earthdata.nasa.gov/portal/home/group.html?id=2267e598c75e41629ec137f388d72a37#content202507_Flood_TX→https://gis.earthdata.nasa.gov/portal/home/group.html?sortField=title&sortOrder=asc&id=f514ef4d04264021b95f899323e19b85#content202601_WinterWx_US→https://gis.earthdata.nasa.gov/portal/home/group.html?id=461a5be76a2c4e6db6ec470657a6f9d6#content202604_Typhoon_Sinlaku→https://gis.earthdata.nasa.gov/portal/home/group.html?sortField=added&sortOrder=desc&id=6ed41cdc783d4c68b08b66d923ac1386#content202606_Earthquake_Venezuela→https://gis.earthdata.nasa.gov/portal/home/group.html?sortField=added&sortOrder=desc&id=58996c58a2714887adf8f5cee0900619#contentThis is a targeted edit of the catalogue JSON, not a whole-config import. The field is free JSON, so the key survives the old build and can be added before or after deploy; the link appears once the new build is live.
Optional:
style.hoverHighlightWidthon vector tile layers with Hover Highlight on (Air4US HMS Fire Detection, HMS Smoke Plume). Empty keeps the styled width.variables.featurePopup.latitudeProperty/longitudeProperty) on point layers whose features carry their exact coordinates as properties. Empty keeps the geometry anchor with zoom re-anchoring.No config is needed for #580, #581, #587, #591, #592, #593, #606, #610, #611, #619, #620, #622 or #623.