Skip to content

Conversation

@NathanMOlson
Copy link
Collaborator

@NathanMOlson NathanMOlson commented May 20, 2025

Resolves #5666 (Hypsometric Tint from terrain-RGB tiles).

Demo here: https://nathanmolson.github.io/color_relief

The shader uses a binary search to find the adjacent color stops from the color ramp, which means that larger color palettes take longer to render (O(log(n))).

While #5742 passes the color ramp to the shader using uniforms, this PR passes the color ramp to the shader using textures. This ensures that at least 64 color stops will be supported on all hardware. You can see the difference between these two approaches here.

The elevation stops are packed into an RGBA value using the raster DEM encoding scheme. This means that the minimum distance between stops is set by the quantization of the DEM data (0.1m for mapbox encoding, 1/256 m for terrarium).

Examples:

cr_linz
cr_geo
cr_iso
cr_rainbow
cr_cat

  • Confirm your changes do not include backports from Mapbox projects (unless with compliant license) - if you are not sure about this, please ask!
  • Briefly describe the changes in this PR.
  • Link to related issues.
  • Include before/after visuals or gifs if this PR includes visual changes.
  • Write tests for all new functionality.
  • Document any changes to public APIs.
  • Post benchmark scores.
  • Add an entry to CHANGELOG.md under the ## main section.

@NathanMOlson
Copy link
Collaborator Author

Do you have an example available that shows the performance when modifying the ramp? I have users who wish to set the high and low breakpoints when they inspect zoomed-in areas.

@larsmaxfield I haven't added any support for dynamically modifying the ramp. Can you tell me more about how you would expect to use that? Would you be using map.setStyle() to modify the ramp? or some other mechanism?

@larsmaxfield
Copy link
Contributor

I would expect to modify the ramp similar to how I can change existing layer paint properties. For example, to tighten the range of a monochrome colorramp via map.setPaintProperty():

const monochrome = [
  "interpolate",
  ["linear"],
  ["elevation"],
  0.0, 'black',
  0.1, 'black',
  3000, 'white',
  3001, 'white'
];

...

monochrome[5] = 2000;  // Tighten range from 0.1–3000 to 2000–2100
monochrome[7] = 2100;
map.setPaintProperty('hyspometric-layer', 'color-relief-color': monochrome);

Or is this already possible?

@larsmaxfield
Copy link
Contributor

User-wise the essential idea is:

image

My users specifically are inspecting painting heightmaps. They want to see height difference in local areas where the change is very small compared to the full data range.

For mapping, I can imagine a dynamic colorramp could help give an impression of flood level.

@NathanMOlson
Copy link
Collaborator Author

@larsmaxfield I've added the capability to modify the colorramp dynamically using map.setPaintProperty(). In my testing, it happens "instantaneously". Thanks for this suggestion, I think it has really made this feature better.

My demo now uses map.setPaintProperty() to change between colorramps (previously it was removing the layer and adding a new one). I think this PR will now 100% support your use case. Check it out and let me know what you think.

@larsmaxfield
Copy link
Contributor

Excellent. And indeed very fast. Thank you!

@HarelM
Copy link
Collaborator

HarelM commented May 21, 2025

Added a few comments:
@mwilsnd feel free to review as this is your suggestion about using texture I believe.

@mwilsnd
Copy link
Collaborator

mwilsnd commented May 21, 2025

Looking good, thanks for taking the time to make these changes! I'm hopeful our days having to consider WebGL 1 are numbered, I encourage you to join the discussion about this at our next Web meeting.

I'll defer final approval to @HarelM.

@HarelM
Copy link
Collaborator

HarelM commented May 21, 2025

Looks good overall. Added a final question but approved otherwise...

@HarelM HarelM enabled auto-merge (squash) May 21, 2025 21:45
@HarelM HarelM merged commit bd885b5 into maplibre:main May 21, 2025
23 checks passed
ToHold pushed a commit to ToHold/maplibre-gl-js that referenced this pull request Oct 31, 2025
* Basic color relief layer working, with a hardcoded colormap

* use texture for color-relief colormap

* add color relief benchmark

* fix border rendering error and ugliness

* do lookup table in shader

* fix merge error

* fix initialization of _featureFilter with stytle-spec update (global state addition)

* remove dead code (pulling colormap from texture)

* remove more dead code related to rendering color relief from texture

* add unit tests for ColorReliefStyleLayer

* add render tests and global opacity handling

* Add a color relief example

* add render test with color relief and hillshade

* fix lint

* fix benchmark and update changelog

* updat to use latest version of style spec

* add "hypsometric" to spelling list

* update expectedBytes

* improve function name

* remove "as any", simplify colorramp creation, add comment

* remove colorramp requirement length = 2^N + 1

* Remap the color-relief color ramp if the length exceeds the WebGL capabilities.

* add unit test for getColorRamp()

* handle empty and single-color color ramps

* account for presence of other uniforms in calculation of maxLength for color ramp

* return arrays in _updateColorRamp()

* sinplify getColorRamp()

* add (messy) type to createColorReliefLayerSpec() argument

* fix colorRampLength

* add color relief layer benchmarks

* use defined type

* add DEMData::pack() function and unit tests

* version of color relief that uses textures instead of uniforms for colorramp

* get the right size limit for color ramp

* update expectedBytes

* fix off-by-one error

* use texture lookup instead of shader interpolation

* function nameing and comment formating

* defer color ramp creation until getColorRamp

* update unit tests

* remove unneeded shader #define

* fix colorRampLength calculation

* use first available textures

* fix expectedBytes

* enable color ramp to be dynamically changes using map.setPaintProperty()

* reduce indentation

* remove unneeded member variable colorRamp and combine _createColorRamp() and getColorRamp()

* fix build

* add RGBAImage::setPixel()

* fix lint

---------

Co-authored-by: Harel M <[email protected]>
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.

Hypsometric Tint from terrain-RGB tiles

4 participants