@deck.gl/carto 9.0.11 → 9.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cartoRasterTile-worker.js +1 -1
- package/dist/cartoSpatialTile-worker.js +1 -1
- package/dist/cartoVectorTile-worker.js +1 -1
- package/dist/dist.dev.js +389 -32
- package/dist/index.cjs +366 -33
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/layers/heatmap.d.ts +36 -0
- package/dist/layers/heatmap.d.ts.map +1 -0
- package/dist/layers/heatmap.js +128 -0
- package/dist/layers/post-process-utils.d.ts +27 -0
- package/dist/layers/post-process-utils.d.ts.map +1 -0
- package/dist/layers/post-process-utils.js +130 -0
- package/dist/layers/quadbin-heatmap-tile-layer.d.ts +21 -0
- package/dist/layers/quadbin-heatmap-tile-layer.d.ts.map +1 -0
- package/dist/layers/quadbin-heatmap-tile-layer.js +83 -0
- package/dist/layers/schema/carto-raster-tile-loader.js +1 -1
- package/dist/layers/schema/carto-spatial-tile-loader.js +1 -1
- package/dist/layers/schema/carto-vector-tile-loader.js +1 -1
- package/dist.min.js +88 -7
- package/package.json +5 -4
- package/src/index.ts +3 -0
- package/src/layers/heatmap.ts +196 -0
- package/src/layers/post-process-utils.ts +180 -0
- package/src/layers/quadbin-heatmap-tile-layer.ts +122 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"version": "9.0.
|
|
6
|
+
"version": "9.0.13",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"@loaders.gl/schema": "^4.2.0",
|
|
49
49
|
"@loaders.gl/tiles": "^4.2.0",
|
|
50
50
|
"@luma.gl/constants": "^9.0.11",
|
|
51
|
+
"@luma.gl/core": "^9.0.11",
|
|
52
|
+
"@luma.gl/shadertools": "^9.0.11",
|
|
51
53
|
"@math.gl/web-mercator": "^4.0.0",
|
|
52
54
|
"@types/d3-array": "^3.0.2",
|
|
53
55
|
"@types/d3-color": "^1.4.2",
|
|
@@ -69,8 +71,7 @@
|
|
|
69
71
|
"@deck.gl/extensions": "^9.0.0",
|
|
70
72
|
"@deck.gl/geo-layers": "^9.0.0",
|
|
71
73
|
"@deck.gl/layers": "^9.0.0",
|
|
72
|
-
"@loaders.gl/core": "^4.2.0"
|
|
73
|
-
"@luma.gl/core": "^9.0.0"
|
|
74
|
+
"@loaders.gl/core": "^4.2.0"
|
|
74
75
|
},
|
|
75
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "39b76f3cd7d6041dd36b93dc3e91b740ed2c57f9"
|
|
76
77
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {default as H3TileLayer} from './layers/h3-tile-layer';
|
|
2
2
|
import {default as _PointLabelLayer} from './layers/point-label-layer';
|
|
3
3
|
import {default as QuadbinTileLayer} from './layers/quadbin-tile-layer';
|
|
4
|
+
import {default as QuadbinHeatmapTileLayer} from './layers/quadbin-heatmap-tile-layer';
|
|
4
5
|
import {default as RasterTileLayer} from './layers/raster-tile-layer';
|
|
5
6
|
import {default as VectorTileLayer} from './layers/vector-tile-layer';
|
|
6
7
|
const CARTO_LAYERS = {
|
|
7
8
|
H3TileLayer,
|
|
8
9
|
_PointLabelLayer,
|
|
9
10
|
QuadbinTileLayer,
|
|
11
|
+
QuadbinHeatmapTileLayer,
|
|
10
12
|
RasterTileLayer,
|
|
11
13
|
VectorTileLayer
|
|
12
14
|
};
|
|
@@ -15,6 +17,7 @@ export {
|
|
|
15
17
|
H3TileLayer,
|
|
16
18
|
_PointLabelLayer,
|
|
17
19
|
QuadbinTileLayer,
|
|
20
|
+
QuadbinHeatmapTileLayer,
|
|
18
21
|
RasterTileLayer,
|
|
19
22
|
VectorTileLayer
|
|
20
23
|
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type {ShaderPass} from '@luma.gl/shadertools';
|
|
2
|
+
import {random} from '@luma.gl/shadertools';
|
|
3
|
+
import {Color} from '@deck.gl/core';
|
|
4
|
+
const glsl = (s: TemplateStringsArray) => `${s}`;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @filter Heatmap
|
|
8
|
+
* @param radiusPixels Blur radius in pixels, controls smoothness of heatmap
|
|
9
|
+
* @param colorDomain Domain to apply to values prior to applying color scale
|
|
10
|
+
* @param color1-6 Colors to use in color scale
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = glsl`\
|
|
14
|
+
uniform heatmapUniforms {
|
|
15
|
+
float radiusPixels;
|
|
16
|
+
vec2 colorDomain;
|
|
17
|
+
vec3 color1;
|
|
18
|
+
vec3 color2;
|
|
19
|
+
vec3 color3;
|
|
20
|
+
vec3 color4;
|
|
21
|
+
vec3 color5;
|
|
22
|
+
vec3 color6;
|
|
23
|
+
float opacity;
|
|
24
|
+
} heatmap;
|
|
25
|
+
|
|
26
|
+
// Controls quality of heatmap, larger values increase quality at expense of performance
|
|
27
|
+
const float SUPPORT = 8.0;
|
|
28
|
+
const vec4 STOPS = vec4(0.2, 0.4, 0.6, 0.8);
|
|
29
|
+
|
|
30
|
+
vec3 colorGradient(float value) {
|
|
31
|
+
vec3 c1;
|
|
32
|
+
vec3 c2;
|
|
33
|
+
vec2 range;
|
|
34
|
+
if (value < STOPS.x) {
|
|
35
|
+
range = vec2(0.0, STOPS.x);
|
|
36
|
+
c1 = heatmap.color1; c2 = heatmap.color2;
|
|
37
|
+
} else if (value < STOPS.y) {
|
|
38
|
+
range = STOPS.xy;
|
|
39
|
+
c1 = heatmap.color2; c2 = heatmap.color3;
|
|
40
|
+
} else if (value < STOPS.z) {
|
|
41
|
+
range = STOPS.yz;
|
|
42
|
+
c1 = heatmap.color3; c2 = heatmap.color4;
|
|
43
|
+
} else if (value < STOPS.w) {
|
|
44
|
+
range = STOPS.zw;
|
|
45
|
+
c1 = heatmap.color4; c2 = heatmap.color5;
|
|
46
|
+
} else if (value < 1.0 ) {
|
|
47
|
+
range = vec2(STOPS.w, 1.0);
|
|
48
|
+
c1 = heatmap.color5; c2 = heatmap.color6;
|
|
49
|
+
} else {
|
|
50
|
+
// Fade out to white
|
|
51
|
+
range = vec2(1.0, 10.0);
|
|
52
|
+
c1 = heatmap.color6; c2 = vec3(255.0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
float f = (value - range.x) / (range.y - range.x);
|
|
56
|
+
return mix(c1, c2, f) / 255.0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const vec3 SHIFT = vec3(1.0, 256.0, 256.0 * 256.0);
|
|
60
|
+
float unpack(vec3 color) {
|
|
61
|
+
return 255.0 * dot(color, SHIFT);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
vec4 heatmap_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) {
|
|
65
|
+
float accumulator = 0.0;
|
|
66
|
+
|
|
67
|
+
// Randomize the lookup values to hide the fixed number of samples
|
|
68
|
+
float offset = 0.5 * random(vec3(12.9898, 78.233, 151.7182), 0.0);
|
|
69
|
+
|
|
70
|
+
// Gaussian normalization parameters
|
|
71
|
+
const float sigma = SUPPORT / 3.0;
|
|
72
|
+
const float a = -0.5 / (sigma * sigma);
|
|
73
|
+
const float w0 = 1.0 / (2.0 * 3.141592653589793 * sigma * sigma); // 2D normalization
|
|
74
|
+
|
|
75
|
+
for (float t = -SUPPORT; t <= SUPPORT; t++) {
|
|
76
|
+
for (float s = -SUPPORT; s <= SUPPORT; s++) {
|
|
77
|
+
vec2 percent = (vec2(s, t) + offset - 0.5) / SUPPORT;
|
|
78
|
+
vec2 delta = percent * heatmap.radiusPixels / texSize;
|
|
79
|
+
vec4 offsetColor = texture(source, texCoord + delta);
|
|
80
|
+
|
|
81
|
+
// Unpack float
|
|
82
|
+
float value = unpack(offsetColor.rgb);
|
|
83
|
+
|
|
84
|
+
// Gaussian
|
|
85
|
+
float weight = w0 * exp(a * (s * s + t * t));
|
|
86
|
+
|
|
87
|
+
accumulator += value * weight;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Apply domain
|
|
92
|
+
float f = (accumulator - heatmap.colorDomain[0]) / (heatmap.colorDomain[1] - heatmap.colorDomain[0]);
|
|
93
|
+
|
|
94
|
+
// Color map
|
|
95
|
+
vec4 color = vec4(0.0);
|
|
96
|
+
color.rgb = colorGradient(f);
|
|
97
|
+
|
|
98
|
+
color.a = smoothstep(0.0, 0.1, f);
|
|
99
|
+
color.a = pow(color.a, 1.0 / 2.2);
|
|
100
|
+
color.a *= heatmap.opacity;
|
|
101
|
+
|
|
102
|
+
return color;
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const defaultColorRange: Color[] = [
|
|
107
|
+
[255, 255, 178],
|
|
108
|
+
[254, 217, 118],
|
|
109
|
+
[254, 178, 76],
|
|
110
|
+
[253, 141, 60],
|
|
111
|
+
[240, 59, 32],
|
|
112
|
+
[189, 0, 38]
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
export type HeatmapProps = {
|
|
116
|
+
/**
|
|
117
|
+
* Radius of the circle in pixels, to which the weight of an object is distributed.
|
|
118
|
+
*
|
|
119
|
+
* @default 30
|
|
120
|
+
*/
|
|
121
|
+
radiusPixels?: number;
|
|
122
|
+
/**
|
|
123
|
+
* Controls how weight values are mapped to the `colorRange`, as an array of two numbers [`minValue`, `maxValue`].
|
|
124
|
+
*
|
|
125
|
+
* @default [0, 1]
|
|
126
|
+
*/
|
|
127
|
+
colorDomain?: [number, number];
|
|
128
|
+
/**
|
|
129
|
+
* Specified as an array of colors [color1, color2, ...].
|
|
130
|
+
*
|
|
131
|
+
* @default `6-class YlOrRd` - [colorbrewer](http://colorbrewer2.org/#type=sequential&scheme=YlOrRd&n=6)
|
|
132
|
+
*/
|
|
133
|
+
colorRange: Color[];
|
|
134
|
+
opacity: number;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type HeatmapUniforms = {
|
|
138
|
+
radiusPixels?: number;
|
|
139
|
+
colorDomain?: [number, number];
|
|
140
|
+
color1?: [number, number, number];
|
|
141
|
+
color2?: [number, number, number];
|
|
142
|
+
color3?: [number, number, number];
|
|
143
|
+
color4?: [number, number, number];
|
|
144
|
+
color5?: [number, number, number];
|
|
145
|
+
color6?: [number, number, number];
|
|
146
|
+
opacity?: number;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const heatmap: ShaderPass<HeatmapProps, HeatmapUniforms> = {
|
|
150
|
+
name: 'heatmap',
|
|
151
|
+
uniformPropTypes: {
|
|
152
|
+
radiusPixels: {value: 20, min: 0, softMax: 100},
|
|
153
|
+
colorDomain: {value: [0, 1]},
|
|
154
|
+
color1: {value: [0, 0, 0]},
|
|
155
|
+
color2: {value: [0, 0, 0]},
|
|
156
|
+
color3: {value: [0, 0, 0]},
|
|
157
|
+
color4: {value: [0, 0, 0]},
|
|
158
|
+
color5: {value: [0, 0, 0]},
|
|
159
|
+
color6: {value: [0, 0, 0]},
|
|
160
|
+
opacity: {value: 1, min: 0, max: 1}
|
|
161
|
+
},
|
|
162
|
+
uniformTypes: {
|
|
163
|
+
radiusPixels: 'f32',
|
|
164
|
+
colorDomain: 'vec2<f32>',
|
|
165
|
+
color1: 'vec3<f32>',
|
|
166
|
+
color2: 'vec3<f32>',
|
|
167
|
+
color3: 'vec3<f32>',
|
|
168
|
+
color4: 'vec3<f32>',
|
|
169
|
+
color5: 'vec3<f32>',
|
|
170
|
+
color6: 'vec3<f32>',
|
|
171
|
+
opacity: 'f32'
|
|
172
|
+
},
|
|
173
|
+
getUniforms: opts => {
|
|
174
|
+
const {
|
|
175
|
+
colorRange = defaultColorRange,
|
|
176
|
+
radiusPixels = 20,
|
|
177
|
+
colorDomain = [0, 1],
|
|
178
|
+
opacity = 1
|
|
179
|
+
} = opts as HeatmapProps;
|
|
180
|
+
const [color1, color2, color3, color4, color5, color6] = colorRange;
|
|
181
|
+
return {
|
|
182
|
+
color1,
|
|
183
|
+
color2,
|
|
184
|
+
color3,
|
|
185
|
+
color4,
|
|
186
|
+
color5,
|
|
187
|
+
color6,
|
|
188
|
+
radiusPixels,
|
|
189
|
+
colorDomain,
|
|
190
|
+
opacity
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
dependencies: [random],
|
|
194
|
+
fs,
|
|
195
|
+
passes: [{sampler: true}]
|
|
196
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {Framebuffer, TextureProps} from '@luma.gl/core';
|
|
2
|
+
import {
|
|
3
|
+
CompositeLayer,
|
|
4
|
+
Layer,
|
|
5
|
+
LayerContext,
|
|
6
|
+
LayerProps,
|
|
7
|
+
LayersList,
|
|
8
|
+
PostProcessEffect,
|
|
9
|
+
PostRenderOptions
|
|
10
|
+
} from '@deck.gl/core';
|
|
11
|
+
|
|
12
|
+
const TEXTURE_PROPS: TextureProps = {
|
|
13
|
+
format: 'rgba8unorm',
|
|
14
|
+
mipmaps: false,
|
|
15
|
+
sampler: {
|
|
16
|
+
minFilter: 'linear',
|
|
17
|
+
magFilter: 'linear',
|
|
18
|
+
addressModeU: 'clamp-to-edge',
|
|
19
|
+
addressModeV: 'clamp-to-edge'
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
interface IPostProcessLayer {
|
|
24
|
+
applyPostProcess: () => void;
|
|
25
|
+
enableRTT: Layer['draw'];
|
|
26
|
+
disableRTT: () => void;
|
|
27
|
+
props: LayerProps;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type Constructor<T> = (new (...args: any[]) => T) & {layerName: string};
|
|
31
|
+
type DrawableCompositeLayer = CompositeLayer & {
|
|
32
|
+
renderLayers(): Layer<{}> | null | LayersList;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function getPostProcessLayer(layer: any): IPostProcessLayer {
|
|
36
|
+
while (layer.parent && !layer.applyPostProcess) {
|
|
37
|
+
layer = layer.parent;
|
|
38
|
+
}
|
|
39
|
+
return layer as unknown as IPostProcessLayer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Dummy Layer that draws nothing, just calls back to root Layer
|
|
44
|
+
*/
|
|
45
|
+
class DrawCallbackLayer extends Layer {
|
|
46
|
+
static layerName = 'DrawCallbackLayer';
|
|
47
|
+
|
|
48
|
+
initializeState(this: DrawCallbackLayer): void {
|
|
49
|
+
this.id = `draw-callback-${getPostProcessLayer(this).props.id}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_drawLayer(this: DrawCallbackLayer) {
|
|
53
|
+
getPostProcessLayer(this).applyPostProcess();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Modifier that marks a layer for Render-to-Target rendering.
|
|
59
|
+
* Resulting layer must be used as a sublayer of a layer created
|
|
60
|
+
* with `PostProcessModifier`
|
|
61
|
+
*/
|
|
62
|
+
export function RTTModifier(BaseLayer) {
|
|
63
|
+
return class RTTLayer extends BaseLayer {
|
|
64
|
+
static layerName = `RTT-${BaseLayer.layerName}`;
|
|
65
|
+
|
|
66
|
+
draw(this: RTTLayer, opts: any) {
|
|
67
|
+
const {moduleParameters} = opts;
|
|
68
|
+
const {picking} = moduleParameters;
|
|
69
|
+
const postProcessLayer = getPostProcessLayer(this);
|
|
70
|
+
|
|
71
|
+
if (!picking.isActive) {
|
|
72
|
+
postProcessLayer.enableRTT(opts);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Draw actual layer
|
|
76
|
+
super.draw(opts);
|
|
77
|
+
|
|
78
|
+
if (!picking.isActive) {
|
|
79
|
+
postProcessLayer.disableRTT();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Modifier that returns the a modified Layer, which applies a
|
|
87
|
+
* postprocess effect to all subLayers created using `RTTModifier`
|
|
88
|
+
*/
|
|
89
|
+
export function PostProcessModifier<T extends Constructor<DrawableCompositeLayer>>(
|
|
90
|
+
BaseLayer: T,
|
|
91
|
+
effect: any
|
|
92
|
+
): T {
|
|
93
|
+
return class PostProcessLayer extends BaseLayer {
|
|
94
|
+
static layerName = `PostProcess${BaseLayer.layerName}`;
|
|
95
|
+
|
|
96
|
+
internalState: any;
|
|
97
|
+
|
|
98
|
+
initializeState(this: PostProcessLayer, context: LayerContext): void {
|
|
99
|
+
super.initializeState(context);
|
|
100
|
+
|
|
101
|
+
this._createTextures();
|
|
102
|
+
this.internalState.postProcess = new PostProcessEffect(effect, this.props);
|
|
103
|
+
this.internalState.postProcess.setup(context);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
updateState(this: PostProcessLayer, params: any): void {
|
|
107
|
+
super.updateState(params);
|
|
108
|
+
this.internalState.postProcess.setProps(this.props);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
renderLayers(): PostProcessLayer | null | LayersList {
|
|
112
|
+
let subLayers = super.renderLayers();
|
|
113
|
+
if (!subLayers) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
subLayers = Array.isArray(subLayers) ? subLayers : [subLayers];
|
|
117
|
+
return [...subLayers, new DrawCallbackLayer()];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_createTextures(this: PostProcessLayer) {
|
|
121
|
+
const {device} = this.context;
|
|
122
|
+
this.internalState.renderBuffers = [0, 1].map(i => {
|
|
123
|
+
return device.createFramebuffer({
|
|
124
|
+
id: `layer-fbo-${i}`,
|
|
125
|
+
colorAttachments: [device.createTexture(TEXTURE_PROPS)],
|
|
126
|
+
depthStencilAttachment: 'depth16unorm'
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
_resizeBuffers(this: PostProcessLayer, opts: any) {
|
|
132
|
+
// TODO we could likely render to a smaller buffer for better perf
|
|
133
|
+
const {moduleParameters} = opts;
|
|
134
|
+
const {viewport} = this.context;
|
|
135
|
+
const width = moduleParameters.devicePixelRatio * viewport.width;
|
|
136
|
+
const height = moduleParameters.devicePixelRatio * viewport.height;
|
|
137
|
+
this.internalState.renderBuffers.forEach((fbo: Framebuffer) => fbo.resize({width, height}));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
enableRTT(this: PostProcessLayer, opts: any) {
|
|
141
|
+
this._resizeBuffers(opts);
|
|
142
|
+
this.internalState.originalRenderPass = this.context.renderPass;
|
|
143
|
+
|
|
144
|
+
const [framebuffer] = this.internalState.renderBuffers;
|
|
145
|
+
|
|
146
|
+
// Create new render pass for RTT
|
|
147
|
+
this.internalState.internalRenderPass = this.context.device.beginRenderPass({
|
|
148
|
+
framebuffer,
|
|
149
|
+
parameters: {viewport: [0, 0, framebuffer.width, framebuffer.height]},
|
|
150
|
+
// Only clear on first render
|
|
151
|
+
clearColor: this.internalState.renderInProgress ? false : [0, 0, 0, 0]
|
|
152
|
+
});
|
|
153
|
+
this.internalState.renderInProgress = true;
|
|
154
|
+
this.context.renderPass = this.internalState.internalRenderPass;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
disableRTT(this: PostProcessLayer) {
|
|
158
|
+
// End render pass, and reinstate original
|
|
159
|
+
this.internalState.internalRenderPass.end();
|
|
160
|
+
this.context.renderPass = this.internalState.originalRenderPass;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
applyPostProcess(this: PostProcessLayer) {
|
|
164
|
+
if (!this.internalState.renderInProgress) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Apply post process effect
|
|
169
|
+
const [inputBuffer, swapBuffer] = this.internalState.renderBuffers;
|
|
170
|
+
const {framebuffer: target} = this.context.renderPass.props;
|
|
171
|
+
this.internalState.postProcess.postRender({
|
|
172
|
+
inputBuffer,
|
|
173
|
+
swapBuffer,
|
|
174
|
+
target
|
|
175
|
+
} as PostRenderOptions);
|
|
176
|
+
|
|
177
|
+
this.internalState.renderInProgress = false;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {getResolution} from 'quadbin';
|
|
2
|
+
|
|
3
|
+
import {Accessor, CompositeLayer, DefaultProps, Layer} from '@deck.gl/core';
|
|
4
|
+
import {SolidPolygonLayer} from '@deck.gl/layers';
|
|
5
|
+
|
|
6
|
+
import {HeatmapProps, heatmap} from './heatmap';
|
|
7
|
+
import {RTTModifier, PostProcessModifier} from './post-process-utils';
|
|
8
|
+
import QuadbinTileLayer, {QuadbinTileLayerProps} from './quadbin-tile-layer';
|
|
9
|
+
|
|
10
|
+
// Modified polygon layer to draw offscreen and output value expected by heatmap
|
|
11
|
+
class RTTSolidPolygonLayer extends RTTModifier(SolidPolygonLayer) {
|
|
12
|
+
static layerName = 'RTTSolidPolygonLayer';
|
|
13
|
+
|
|
14
|
+
getShaders(type) {
|
|
15
|
+
const shaders = super.getShaders(type);
|
|
16
|
+
shaders.inject = {
|
|
17
|
+
'vs:#decl': 'uniform float cellResolution;',
|
|
18
|
+
'vs:DECKGL_FILTER_COLOR': `
|
|
19
|
+
const vec3 SHIFT = vec3(1.0, 256.0, 256.0 * 256.0);
|
|
20
|
+
float value = 255.0 * dot(SHIFT, color.rgb);
|
|
21
|
+
|
|
22
|
+
// Keep "power" delivered to screen constant when tiles update
|
|
23
|
+
float relativeZoom = log2(project_uScale) - cellResolution + 3.5; // range 0-1
|
|
24
|
+
float relativeArea = pow(2.0, -2.0 * relativeZoom); // range 0.25-1
|
|
25
|
+
value *= relativeArea;
|
|
26
|
+
|
|
27
|
+
// Pack float into 3 channels to pass to heatmap shader
|
|
28
|
+
color = vec4(mod(vec3(value, floor(value / SHIFT.yz)), 256.0), 255.0) / 255.0;
|
|
29
|
+
`
|
|
30
|
+
};
|
|
31
|
+
return shaders;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
draw(this, opts: any) {
|
|
35
|
+
const cell0 = this.props!.data[0];
|
|
36
|
+
const cellResolution = Number(getResolution(cell0.id));
|
|
37
|
+
for (const model of this.state.models) {
|
|
38
|
+
model.setUniforms({cellResolution});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
super.draw(opts);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Modify QuadbinTileLayer to apply heatmap post process effect
|
|
46
|
+
const PostProcessQuadbinTileLayer = PostProcessModifier(QuadbinTileLayer, heatmap);
|
|
47
|
+
|
|
48
|
+
function encodeWeight(w: number) {
|
|
49
|
+
return [w % 256, Math.floor(w / 256), Math.floor(w / (256 * 256))];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const defaultProps: DefaultProps<QuadbinHeatmapTileLayerProps> = {
|
|
53
|
+
...QuadbinTileLayer.defaultProps,
|
|
54
|
+
|
|
55
|
+
getWeight: {type: 'accessor', value: 1}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** All properties supported by QuadbinHeatmapTileLayer. */
|
|
59
|
+
export type QuadbinHeatmapTileLayerProps<DataT = unknown> = _QuadbinHeatmapTileLayerProps<DataT> &
|
|
60
|
+
QuadbinTileLayerProps;
|
|
61
|
+
|
|
62
|
+
/** Properties added by QuadbinHeatmapTileLayer. */
|
|
63
|
+
type _QuadbinHeatmapTileLayerProps<DataT> = QuadbinTileLayerProps<DataT> &
|
|
64
|
+
HeatmapProps & {
|
|
65
|
+
/**
|
|
66
|
+
* The weight of each object.
|
|
67
|
+
*
|
|
68
|
+
* @default 1
|
|
69
|
+
*/
|
|
70
|
+
getWeight?: Accessor<DataT, number>;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
class QuadbinHeatmapTileLayer<DataT = any, ExtraProps extends {} = {}> extends CompositeLayer<
|
|
74
|
+
ExtraProps & Required<_QuadbinHeatmapTileLayerProps<DataT>>
|
|
75
|
+
> {
|
|
76
|
+
static layerName = 'QuadbinHeatmapTileLayer';
|
|
77
|
+
static defaultProps = defaultProps;
|
|
78
|
+
|
|
79
|
+
renderLayers(): Layer {
|
|
80
|
+
const {getWeight, colorDomain, colorRange, radiusPixels, _subLayerProps} = this.props;
|
|
81
|
+
|
|
82
|
+
// Inject modified polygon layer as sublayer into TileLayer
|
|
83
|
+
const subLayerProps = {
|
|
84
|
+
..._subLayerProps,
|
|
85
|
+
cell: {
|
|
86
|
+
..._subLayerProps?.cell,
|
|
87
|
+
_subLayerProps: {
|
|
88
|
+
..._subLayerProps?.cell?._subLayerProps,
|
|
89
|
+
fill: {
|
|
90
|
+
..._subLayerProps?.cell?._subLayerProps?.fill,
|
|
91
|
+
type: RTTSolidPolygonLayer
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const getFillColor =
|
|
98
|
+
typeof getWeight === 'function'
|
|
99
|
+
? (d, info) => encodeWeight(getWeight(d, info))
|
|
100
|
+
: encodeWeight(getWeight);
|
|
101
|
+
|
|
102
|
+
return new PostProcessQuadbinTileLayer(
|
|
103
|
+
this.getSubLayerProps({
|
|
104
|
+
id: 'heatmap',
|
|
105
|
+
data: this.props.data,
|
|
106
|
+
|
|
107
|
+
getFillColor,
|
|
108
|
+
|
|
109
|
+
colorDomain,
|
|
110
|
+
colorRange,
|
|
111
|
+
radiusPixels,
|
|
112
|
+
_subLayerProps: subLayerProps,
|
|
113
|
+
|
|
114
|
+
updateTriggers: {
|
|
115
|
+
getPosition: this.props.updateTriggers.getWeight
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default QuadbinHeatmapTileLayer;
|