Skip to content

feat(hex): zoom-reactive color legend for hex layers (#316) - #320

Merged
cboettig merged 1 commit into
mainfrom
feat/hex-layer-legend
Jul 14, 2026
Merged

feat(hex): zoom-reactive color legend for hex layers (#316)#320
cboettig merged 1 commit into
mainfrom
feat/hex-layer-legend

Conversation

@cboettig

Copy link
Copy Markdown
Member

Closes #316.

Problem

Hex tile layers rendered no legend. Two independent blockers:

  1. They bypass registerLayer (which wires the legend via _showLegendIfVisible), so the legend was never even invoked for them.
  2. Even if invoked, deriveContinuousLegend only parses a top-level interpolate/step; hex paint is ['case', …, ['match', ['get','res'], …]], so it returns null.

Change — a hex-specific legend path (not a bent continuous deriver)

  • addHexTileLayer stores valueStats + palette on the layer state, sets legendType: 'hex', and calls _showLegendIfVisible() (mirroring what registerLayer does for curated layers).
  • _hexLegend(state) derives {colors, range, res} from the fixed 3-stop PALETTES (imported from hex-layer-helpers.js, the same source the map ramp uses) and the value domain of the resolution currently on screen.
  • _currentHexRes(state) determines that resolution by sampling the rendered features' res property (the server pyramid serves one resolution per tile keyed off zoom, so this is more honest than reproducing the server's zoom→res formula client-side), with a finest-resolution fallback for the moment right after add / for GeoJSON layers whose features carry no res.
  • _showLegend gets a hex branch: colorbar + min/max labels + an H3 resolution N caption.
  • Zoom-reactive: a single lazily-wired moveend handler (_updateHexLegends) relabels every visible hex legend as the on-screen resolution changes. This matters because the per-res domain scales ~7×/step, so a static range would mismatch the colors at most zoom levels.
  • removeHexTileLayer tears down the legend entry.

Design notes

  • Reuses the exported PALETTES rather than re-parsing the case/match paint — the gradient is [c0,c1,c2] (min→max), matching buildFillColorExpression.
  • Values format as grouped integers / 3-significant-digit floats (_fmtLegendValue).

Testing

  • New test/map-manager.hex-legend.test.js (jsdom, 9 tests): _hasLegend for hex; _currentHexRes dominant-res sampling + finest fallback; _hexLegend palette+domain; _showLegend DOM (colorbar/labels/res note); _updateHexLegends relabel on zoom; removal cleanup; value formatting.
  • Full suite green (525 passed).
  • map-manager.js is browser-bound; visual check on a deployed app recommended (colorbar + label updates while zooming a hex layer).

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 58.91% 5400 / 9165
🔵 Statements 58.89% 5400 / 9169
🔵 Functions 65.54% 194 / 296
🔵 Branches 83.22% 1206 / 1449
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
app/map-manager.js 54.87% 82.52% 46.15% 54.93% 57-148, 155-159, 165-392, 401-445, 459-481, 522-558, 568-581, 589-602, 666-667, 684-685, 842-851, 879-880, 886-889, 898-908, 914-921, 977-980, 988-992, 995-1011, 1017-1018, 1024-1027, 1096-1097, 1117-1119, 1141-1146, 1152-1155, 1165-1183, 1190-1194, 1202-1295, 1302-1342, 1366-1368, 1380-1394, 1440-1445, 1457-1503, 1551-1552, 1619-1638, 1641-1656, 1666-1668, 1768-1775, 1837-1838, 1871-1875, 428-462
Generated in workflow #175 for commit 615d709 by the Vitest Coverage Report Action

Hex tile layers rendered no legend: they bypass registerLayer (which wires
the legend) and style with a per-res case/match paint that
deriveContinuousLegend can't parse.

Add a hex-specific legend path instead of bending the continuous deriver:
- addHexTileLayer stores valueStats + palette on the layer state, sets
  legendType 'hex', and calls _showLegendIfVisible (as registerLayer does
  for curated layers).
- _hexLegend derives {colors, range, res} from the fixed 3-stop PALETTES and
  the value domain of the resolution currently on screen; _currentHexRes
  samples the rendered features' res property (the server pyramid serves one
  resolution per tile keyed off zoom) with a finest-res fallback.
- _showLegend renders a colorbar + min/max labels + an 'H3 resolution N'
  caption. A single lazy moveend handler (_updateHexLegends) relabels visible
  hex legends as the on-screen resolution changes (~7x/step scaling).
- removeHexTileLayer tears down the legend entry.

Covered by test/map-manager.hex-legend.test.js (jsdom, 9 tests).
@cboettig
cboettig force-pushed the feat/hex-layer-legend branch from 01bda70 to 615d709 Compare July 14, 2026 18:30
@cboettig
cboettig merged commit 11be9a9 into main Jul 14, 2026
1 check passed
@cboettig
cboettig deleted the feat/hex-layer-legend branch July 14, 2026 18:57
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.

Hex layers: render a color legend

1 participant