Skip to content

Commit

Permalink
Heatmap - expose additional properties (visgl#6158)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorDykhta authored Sep 3, 2021
1 parent 017d960 commit 5fa5dde
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 23 deletions.
12 changes: 12 additions & 0 deletions docs/api-reference/aggregation-layers/heatmap-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ Operation used to aggregate all data point weights to calculate a pixel's color

The weight of each data object is distributed to all the pixels in a circle centered at the object position. The weight that a pixel receives is inversely proportional to its distance from the center. In `'SUM'` mode, pixels that fall into multiple circles will have the sum of all weights. In `'MEAN'` mode, pixels that fall into multiple circles will have their weight calculated as the weighted average from all the neighboring data points. And the weight of the pixel determines its color.

##### `weightsTextureSize` (Number, optional)

* Default: `2048`

`weightsTextureSize` specifies the size of weight texture. Smaller texture sizes can improve rendering performance. Heatmap aggregation calculates the maximum weight value in the texture and the process can take 50-100 ms for 2048x2048 texture, but only 5-7ms for 512x512 texture. Smaller texture sizes lead to visible pixelation.

##### `debounceTimeout` (Number, optional)

* Default: `500`

`debounceTimeout` is an interval in milliseconds during which changes to the viewport don't trigger aggregation. Large datasets combined with a large `radiusPixels` can cause freezes during user interactions due to aggregation updates. Setting positive debounceTimeout delays aggregation updates and prevents freezes during the interaction. As a side effect, the user has to wait to see updated results after the end of the interaction.

### Data Accessors

##### `getPosition` ([Function](/docs/developer-guide/using-layers.md#accessors), optional)
Expand Down
57 changes: 34 additions & 23 deletions modules/aggregation-layers/src/heatmap-layer/heatmap-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,7 @@ import {
getTextureParams
} from './heatmap-layer-utils';
import {Buffer, Texture2D, Transform, getParameters, FEATURES, hasFeatures} from '@luma.gl/core';
import {
AttributeManager,
COORDINATE_SYSTEM,
log,
_mergeShaders as mergeShaders
} from '@deck.gl/core';
import {AttributeManager, COORDINATE_SYSTEM, log} from '@deck.gl/core';
import TriangleLayer from './triangle-layer';
import AggregationLayer from '../aggregation-layer';
import {defaultColorRange, colorRangeToFlatArray} from '../utils/color-utils';
Expand All @@ -44,8 +39,6 @@ import vs_max from './max-vs.glsl';
import fs_max from './max-fs.glsl';

const RESOLUTION = 2; // (number of common space pixels) / (number texels)
const SIZE_2K = 2048;
const ZOOM_DEBOUNCE = 500; // milliseconds
const TEXTURE_OPTIONS = {
mipmaps: false,
parameters: {
Expand All @@ -71,7 +64,9 @@ const defaultProps = {
threshold: {type: 'number', min: 0, max: 1, value: 0.05},
colorDomain: {type: 'array', value: null, optional: true},
// 'SUM' or 'MEAN'
aggregation: 'SUM'
aggregation: 'SUM',
weightsTextureSize: {type: 'number', min: 128, max: 2048, value: 2048},
debounceTimeout: {type: 'number', min: 0, max: 1000, value: 500}
};

const REQUIRED_FEATURES = [
Expand Down Expand Up @@ -112,6 +107,10 @@ export default class HeatmapLayer extends AggregationLayer {
return;
}
super.updateState(opts);
this._updateHeatmapState(opts);
}

_updateHeatmapState(opts) {
const {props, oldProps} = opts;
const changeFlags = this._getChangeFlags(opts);

Expand Down Expand Up @@ -139,7 +138,6 @@ export default class HeatmapLayer extends AggregationLayer {

this.setState({zoom: opts.context.viewport.zoom});
}
/* eslint-enable max-statements,complexity */

renderLayers() {
if (!this.state.supported) {
Expand Down Expand Up @@ -262,7 +260,9 @@ export default class HeatmapLayer extends AggregationLayer {

_setupTextureParams() {
const {gl} = this.context;
const textureSize = Math.min(SIZE_2K, getParameters(gl, gl.MAX_TEXTURE_SIZE));
const {weightsTextureSize} = this.props;

const textureSize = Math.min(weightsTextureSize, getParameters(gl, gl.MAX_TEXTURE_SIZE));
const floatTargetSupport = hasFeatures(gl, FEATURES.COLOR_ATTACHMENT_RGBA32F);
const {format, type} = getTextureParams({gl, floatTargetSupport});
const weightsScale = floatTargetSupport ? 1 : 1 / 255;
Expand All @@ -276,18 +276,25 @@ export default class HeatmapLayer extends AggregationLayer {
}
}

_createWeightsTransform(shaderOptions = {}) {
getShaders(type) {
return super.getShaders(
type === 'max-weights-transform'
? {
vs: vs_max,
_fs: fs_max
}
: {
vs: weights_vs,
_fs: weights_fs
}
);
}

_createWeightsTransform(shaders = {}) {
const {gl} = this.context;
let {weightsTransform} = this.state;
const {weightsTexture} = this.state;
weightsTransform?.delete();
const shaders = mergeShaders(
{
vs: weights_vs,
_fs: weights_fs
},
shaderOptions
);

weightsTransform = new Transform(gl, {
id: `${this.id}-weights-transform`,
Expand All @@ -303,16 +310,19 @@ export default class HeatmapLayer extends AggregationLayer {
const {gl} = this.context;
this._createTextures();
const {textureSize, weightsTexture, maxWeightsTexture} = this.state;
this._createWeightsTransform();

const weightsTransformShaders = this.getShaders('weights-transform');
this._createWeightsTransform(weightsTransformShaders);

const maxWeightsTransformShaders = this.getShaders('max-weights-transform');
const maxWeightTransform = new Transform(gl, {
id: `${this.id}-max-weights-transform`,
_sourceTextures: {
inTexture: weightsTexture
},
_targetTexture: maxWeightsTexture,
_targetTextureVarying: 'outTexture',
vs: vs_max,
_fs: fs_max,
...maxWeightsTransformShaders,
elementCount: textureSize * textureSize
});

Expand Down Expand Up @@ -496,6 +506,7 @@ export default class HeatmapLayer extends AggregationLayer {

_debouncedUpdateWeightmap(fromTimer = false) {
let {updateTimer} = this.state;
const {debounceTimeout} = this.props;

if (fromTimer) {
updateTimer = null;
Expand All @@ -506,7 +517,7 @@ export default class HeatmapLayer extends AggregationLayer {
} else {
this.setState({isWeightMapDirty: false});
clearTimeout(updateTimer);
updateTimer = setTimeout(this._debouncedUpdateWeightmap.bind(this, true), ZOOM_DEBOUNCE);
updateTimer = setTimeout(this._debouncedUpdateWeightmap.bind(this, true), debounceTimeout);
}

this.setState({updateTimer});
Expand Down

0 comments on commit 5fa5dde

Please sign in to comment.