visgl / visgl/deck.gl

[Feat] HeatmapLayer refactor

Open
#9,451 1 comment 6 reactions 2 assignees View on GitHub

@felixpalmer is already working on this.

Since Feb 20, 2025.

feature
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 CompositeLayer class: postProcessEffect which accepts a ShaderModule, like ink for example just like with the current PostProcessEffect.
  • When the prop is present the layer accepts all the props of the ShaderModule and forwards them onto the effect, acting in a similar manner to a LayerExtension.

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 postprocessEffect will be a ShaderModule which applies the heatmap effect
  • renderSubLayers will draw a extended GridLayer

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.