Skip to content

feat(map): color map symbols with the viridis palette - #350

Open
jirhiker wants to merge 3 commits into
stagingfrom
BDMS-349-viridis-map-colors
Open

feat(map): color map symbols with the viridis palette#350
jirhiker wants to merge 3 commits into
stagingfrom
BDMS-349-viridis-map-colors

Conversation

@jirhiker

@jirhiker jirhiker commented Aug 21, 2026

Copy link
Copy Markdown
Member

Closes BDMS-349.

What

Map symbol colors were an ad hoc mix of Material palette hues and ColorBrewer ramps. Neither was colorblind-safe, and several hues (brown, olive, dark blue) sank into satellite imagery. Everything now derives from a single viridis ramp.

  • src/constants/viridis.ts — samples the reference ramp (viridisLite::viridis(10)) and interpolates between anchors. Exports viridisColor, viridisSamples, viridisGradient, and the ramp endpoints. Output matches R's viridis(7) almost exactly.
  • src/constants/mapColors.ts — assigns each of the 23 categorical layers an evenly spaced ramp position, and names the shared symbol colors (stroke, highlight, no-data fallback).
  • Classed layers (TDS, depth to water, water elevation) and their legend gradients sample the ramp in order, so darker always reads as lower.
  • The depth-to-water trend scale uses the ramp endpoints against its teal midpoint for the diverging declining/stable/rising cases.
  • No-data features keep a neutral gray, deliberately off-ramp so they cannot be mistaken for a real class.
  • Highlighted/selected symbols use ramp yellow over a dark purple stroke — the strongest contrast pair inside the palette. Replaces the old #ff4d4d and #2563eb highlights.
  • Same constants applied to the satellite card maps (InteractiveSatelliteMap, AssociatedSitesMapCard) and BatchRouteMap.

Acceptance criteria

  • Map points use the viridis color palette.
  • Point colors are easier to differentiate from each other — with a caveat, see below.
  • Point colors are easier to distinguish from the satellite basemap. Every symbol keeps its white stroke, and the ramp avoids the browns and grays of aerial photography.

Known limit

A sequential ramp can only carry so many categories. With 23 layers the gap between adjacent entries is a few percent of the ramp (~10/255 per channel), so two or three layers at once read clearly but enabling every layer still yields near neighbors. Layer ordering interleaves related families to keep commonly co-viewed layers far apart. This is inherent to viridis rather than to the implementation, and it is documented in the module header.

Out of scope

Left alone deliberately, happy to fold in if wanted:

  • #B42222 single-marker pickers in the location and thing forms.
  • The #007bff project-boundary polygon in InteractiveSatelliteMap.
  • mapSelection's neutral black/white selection stroke.

Testing

  • npx tsc clean; Biome clean on touched files.
  • 17 new tests in src/test/utils/viridis.test.ts — ramp endpoints, clamping of out-of-range and non-finite positions, monotonic luminance, gradient stops, minimum pairwise separation between layer colors, and that the no-data gray stays off the ramp.
  • Full unit run: 280 passed, 0 failed.
  • Not run: the integration contract tests, which need the prism mock server on :4010.
  • No browser verification — the map page is behind auth.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown

Preview Deployment

Preview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app

Note: This preview uses the staging API endpoints.

@jirhiker
jirhiker requested review from lizalino and a lite review from Copilot August 21, 2026 04:47
@jirhiker

Copy link
Copy Markdown
Member Author

FYSA @TylerAdamMartinez

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR standardizes Ocotillo map symbol styling by replacing ad hoc layer colors with a single viridis-based ramp, improving colorblind safety and contrast against satellite imagery while keeping classed layers’ “low-to-high” semantics consistent.

Changes:

  • Introduces viridis utilities (viridisColor, evenly spaced samples, and CSS gradients) and centralizes shared map styling constants (layer colors, no-data, highlight, stroke).
  • Updates OGC layer legends and classed styling (TDS, depth-to-water, trend, water elevation) to derive colors/gradients from viridis.
  • Applies the shared constants across map UIs (main map view, satellite card maps, and batch route map) and adds focused Vitest coverage for palette behavior.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/constants/viridis.ts New viridis ramp utilities (sampling, interpolation, CSS gradient) and exported endpoints.
src/constants/mapColors.ts New centralized map color constants for categorical layers, highlights, symbol strokes, and no-data.
src/utils/ogcLayerUtils.ts Replaces hard-coded class colors/legend gradients with viridis-derived samples/gradients.
src/hooks/useThingLayers.tsx Updates OGC layer definitions and water elevation color mapping/legend to use shared viridis constants.
src/hooks/useOGCLayer.ts Switches default layer color and symbol stroke to shared constants.
src/hooks/useLayer.tsx Switches symbol stroke color to shared constant.
src/pages/ocotillo/map/list.tsx Updates highlight styling and no-data fallback to shared constants.
src/pages/ocotillo/thing/components/BatchRouteMap.tsx Applies shared layer color and stroke constants to route map markers/labels.
src/components/card/InteractiveSatelliteMap.tsx Applies shared layer/highlight/stroke constants to satellite card map rendering.
src/components/ContactShow/AssociatedSitesMapCard.tsx Applies shared layer/stroke constants to associated-sites map markers.
src/test/utils/viridis.test.ts Adds unit tests covering ramp behavior, gradients, luminance monotonicity, and layer color spacing/off-ramp no-data.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/constants/viridis.ts
@github-actions

Copy link
Copy Markdown

Preview Deployment

Preview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app

Note: This preview uses the staging API endpoints.

@github-actions

Copy link
Copy Markdown

Preview Deployment

Preview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app

Note: This preview uses the staging API endpoints.

jirhiker and others added 3 commits August 21, 2026 08:59
BDMS-349. Map symbol colors were an ad hoc mix of Material palette hues
and ColorBrewer ramps. Neither was colorblind-safe, and several hues
(brown, olive, dark blue) sank into satellite imagery.

Everything now derives from a single viridis ramp:

- src/constants/viridis.ts samples the reference ramp
  (viridisLite::viridis(10)) and interpolates between anchors, exposing
  viridisColor/viridisSamples/viridisGradient plus the ramp endpoints.
- src/constants/mapColors.ts assigns every categorical layer an evenly
  spaced position on the ramp, and names the shared symbol colors
  (stroke, highlight, no-data fallback).
- Classed layers (TDS, depth to water, water elevation) and their legend
  gradients sample the ramp in order, so darker always reads as lower.
- The depth-to-water trend scale uses the ramp endpoints against its
  teal midpoint for the diverging declining/stable/rising cases.
- No-data features keep a neutral gray, deliberately off-ramp so they
  cannot be mistaken for a real class.
- Highlighted/selected symbols use ramp yellow over a dark purple
  stroke, the strongest contrast pair available inside the palette.

A sequential ramp can only carry so many categories: with 23 layers the
gap between adjacent entries is a few percent of the ramp, so enabling
every layer at once still yields near neighbors. Ordering interleaves
related families to keep commonly co-viewed layers far apart.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
viridisColor rejected every non-finite position to the dark end, so
+Infinity returned the low color instead of the high one. The infinities
are ordered, so they now clamp like any other out-of-range value; only
NaN, which has no position on the ramp, falls to the low end.
@jirhiker
jirhiker force-pushed the BDMS-349-viridis-map-colors branch from de6ec24 to 35f3a6f Compare August 21, 2026 15:59
@github-actions

Copy link
Copy Markdown

Preview Deployment

Preview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app

Note: This preview uses the staging API endpoints.

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.

2 participants