[Feat] HeatmapLayer refactor
@felixpalmer is already working on this.
Since Feb 20, 2025.
- Dominant language
- TypeScript
- Stars
- 14.6k
- Forks
- 2.3k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 42
Description
Target Use Case
The current HeatmapLayer has a number of issues, in particular:
- Performance: the aggregation is throttled to only happen once a second leading to jarring results when zooming
- Code is hard to understand
In the deck.gl/carto module there is an alternative heatmap implementation: https://deck.gl/docs/api-reference/carto/heatmap-tile-layer which uses a different approach to rendering:
- A ordinary aggregation layer (in this case a
QuadbinTileLayer) is rendered first - The heatmap is applied using a postprocess effect
This approach has a number of benefits:
- Performance The heatmap is updated on every frame (see video)
- Composability The underlying rendering layer can be swapped out - for example H3 could be used for aggregation
- Debugging The heatmap effect can be disabled and the underlying rendering layer debugged normally (the existing heatmap layer renders offscreen and is tedious to debug)
- Support for tile layers there is no reason the underlying rendering layer cannot be a tile layer
- Transitions/animations as the effect works on every frame, transitions/animations are automatically supported
The proposal is to take the ideas from the above layer and migrate the current HeatmapLayer to use a similar approach. In addition to supporting an already aggregated layer, it should support point data so as to provide back-compatibility with the existing HeatmapLayer
Proposal
The key enabling technique that lets the deck.gl/carto/HeatmapTileLayer work is the ability to render the underlying layer into an offscreen buffer. It does this with a pair of modifiers:
- RTTModifier Modifier that marks a layer for Render-to-Target rendering. Resulting layer must be used as a sublayer of a layer created with
PostProcessModifier - PostProcessModifier Modifier that returns the a modified Layer, which applies a postprocess effect to all subLayers created using
RTTModifier
The usage is then:
const heatmap: ShaderModule = ...;
class RTTSolidPolygonLayer extends RTTModifier(SolidPolygonLayer);
const PostProcessQuadbinTileLayer = PostProcessModifier(QuadbinTileLayer, heatmap);
new PostProcessQuadbinTileLayer({
...
{ cell: { fill: { type: RTTSolidPolygonLayer } } }
});
This works, but it would be cleaner if deck.gl core had an official way to draw a layer to a render-target and the apply a postprocess effect.
Proposed API
- A prop is added to the
CompositeLayerclass:postProcessEffectwhich accepts aShaderModule, likeinkfor example just like with the current PostProcessEffect. - When the prop is present the layer accepts all the
propsof theShaderModuleand forwards them onto the effect, acting in a similar manner to aLayerExtension.
Thus to implement a TileLayer with brighness/contrast:
import {brightnessContrast} from '@luma.gl/shadertools';
const layer = new TileLayer({
data: 'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png',
renderSubLayers: props => new BitmapLayer(props, {...}),
postProcessEffect: brightnessContrast,
brightness: 1.0,
contrast: 1.0
});
HeatmapLayer implementation
Using the above approach the new HeatmapLayer will be implemented as CompositeLayer, where:
- The
postprocessEffectwill be aShaderModulewhich applies the heatmap effect renderSubLayerswill draw a extended GridLayer
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.