feat(map): color map symbols with the viridis palette - #350
Conversation
Preview DeploymentPreview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app Note: This preview uses the staging API endpoints. |
|
FYSA @TylerAdamMartinez |
There was a problem hiding this comment.
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
viridisutilities (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.
Preview DeploymentPreview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app Note: This preview uses the staging API endpoints. |
Preview DeploymentPreview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app Note: This preview uses the staging API endpoints. |
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.
de6ec24 to
35f3a6f
Compare
Preview DeploymentPreview URL: https://preview-bdms-349-viridis-map-colors-auejgdbofq-uc.a.run.app Note: This preview uses the staging API endpoints. |
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. ExportsviridisColor,viridisSamples,viridisGradient, and the ramp endpoints. Output matches R'sviridis(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).#ff4d4dand#2563ebhighlights.InteractiveSatelliteMap,AssociatedSitesMapCard) andBatchRouteMap.Acceptance criteria
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:
#B42222single-marker pickers in the location and thing forms.#007bffproject-boundary polygon inInteractiveSatelliteMap.mapSelection's neutral black/white selection stroke.Testing
npx tscclean; Biome clean on touched files.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.🤖 Generated with Claude Code