@deck.gl/carto 9.0.17 → 9.0.18

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/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.17",
6
+ "version": "9.0.18",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -73,5 +73,5 @@
73
73
  "@deck.gl/layers": "^9.0.0",
74
74
  "@loaders.gl/core": "^4.2.0"
75
75
  },
76
- "gitHead": "203d25c47f421a29d607a2a6fd9e1ef50f67b2bf"
76
+ "gitHead": "32f7e51eb96319739a18a3ca22092e60948fcbdb"
77
77
  }
package/src/index.ts CHANGED
@@ -1,23 +1,23 @@
1
1
  import {default as H3TileLayer} from './layers/h3-tile-layer';
2
+ import {default as HeatmapTileLayer} from './layers/heatmap-tile-layer';
2
3
  import {default as _PointLabelLayer} from './layers/point-label-layer';
3
4
  import {default as QuadbinTileLayer} from './layers/quadbin-tile-layer';
4
- import {default as QuadbinHeatmapTileLayer} from './layers/quadbin-heatmap-tile-layer';
5
5
  import {default as RasterTileLayer} from './layers/raster-tile-layer';
6
6
  import {default as VectorTileLayer} from './layers/vector-tile-layer';
7
7
  const CARTO_LAYERS = {
8
8
  H3TileLayer,
9
+ HeatmapTileLayer,
9
10
  _PointLabelLayer,
10
11
  QuadbinTileLayer,
11
- QuadbinHeatmapTileLayer,
12
12
  RasterTileLayer,
13
13
  VectorTileLayer
14
14
  };
15
15
  export {
16
16
  CARTO_LAYERS,
17
17
  H3TileLayer,
18
+ HeatmapTileLayer,
18
19
  _PointLabelLayer,
19
20
  QuadbinTileLayer,
20
- QuadbinHeatmapTileLayer,
21
21
  RasterTileLayer,
22
22
  VectorTileLayer
23
23
  };
@@ -0,0 +1,236 @@
1
+ import {getResolution} from 'quadbin';
2
+
3
+ import {Accessor, CompositeLayer, CompositeLayerProps, 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
+ import {TilejsonPropType} from './utils';
10
+ import {TilejsonResult} from '../sources';
11
+ import {_Tile2DHeader as Tile2DHeader} from '@deck.gl/geo-layers';
12
+
13
+ /**
14
+ * Computes the unit density (inverse of cell area)
15
+ */
16
+ function unitDensityForCell(cell: bigint) {
17
+ const cellResolution = Number(getResolution(cell));
18
+ return Math.pow(4.0, cellResolution);
19
+ }
20
+
21
+ // Modified polygon layer to draw offscreen and output value expected by heatmap
22
+ class RTTSolidPolygonLayer extends RTTModifier(SolidPolygonLayer) {
23
+ static layerName = 'RTTSolidPolygonLayer';
24
+
25
+ getShaders(type) {
26
+ const shaders = super.getShaders(type);
27
+ shaders.inject = {
28
+ 'vs:#decl': `
29
+ uniform float densityFactor;
30
+ `,
31
+ 'vs:#main-end': `
32
+ // Value from getWeight accessor
33
+ float weight = elevations;
34
+
35
+ // Keep "power" delivered to screen constant when tiles update
36
+ // by outputting normalized density
37
+ weight *= densityFactor;
38
+
39
+ // Pack float into 3 channels to pass to heatmap shader
40
+ // SCALE value important, as we don't want to saturate
41
+ // but also want enough definition to avoid banding
42
+ const vec3 SHIFT = vec3(1.0, 256.0, 256.0 * 256.0);
43
+ const float MAX_VAL = SHIFT.z * 255.0;
44
+ const float SCALE = MAX_VAL / 8.0;
45
+ weight *= SCALE;
46
+ weight = clamp(weight, 0.0, MAX_VAL);
47
+ vColor = vec4(mod(vec3(weight, floor(weight / SHIFT.yz)), 256.0), 255.0) / 255.0;
48
+ `
49
+ };
50
+ return shaders;
51
+ }
52
+
53
+ draw(this, opts: any) {
54
+ const cell = this.props!.data[0];
55
+ const maxDensity = this.props.elevationScale;
56
+ const densityFactor = unitDensityForCell(cell.id) / maxDensity;
57
+ for (const model of this.state.models) {
58
+ model.setUniforms({densityFactor});
59
+ }
60
+
61
+ super.draw(opts);
62
+ }
63
+ }
64
+
65
+ // Modify QuadbinTileLayer to apply heatmap post process effect
66
+ const PostProcessQuadbinTileLayer = PostProcessModifier(QuadbinTileLayer, heatmap);
67
+
68
+ const defaultProps: DefaultProps<HeatmapTileLayerProps> = {
69
+ data: TilejsonPropType,
70
+ getWeight: {type: 'accessor', value: 1},
71
+ onMaxDensityChange: {type: 'function', optional: true, value: null},
72
+ colorDomain: {type: 'array', value: [0, 1]},
73
+ intensity: {type: 'number', value: 1},
74
+ radiusPixels: {type: 'number', min: 0, max: 100, value: 20}
75
+ };
76
+
77
+ /** All properties supported by HeatmapTileLayer. */
78
+ export type HeatmapTileLayerProps<DataT = unknown> = _HeatmapTileLayerProps<DataT> &
79
+ CompositeLayerProps & {
80
+ data: null | TilejsonResult | Promise<TilejsonResult>;
81
+ };
82
+
83
+ /** Properties added by HeatmapTileLayer. */
84
+ type _HeatmapTileLayerProps<DataT> = QuadbinTileLayerProps<DataT> &
85
+ HeatmapProps & {
86
+ /**
87
+ * The weight of each object.
88
+ *
89
+ * @default 1
90
+ */
91
+ getWeight?: Accessor<DataT, number>;
92
+
93
+ /** Called when maximum density in displayed tiles changes. */
94
+ onMaxDensityChange?: ((maxDensity: number) => void) | null;
95
+ };
96
+
97
+ class HeatmapTileLayer<DataT = any, ExtraProps extends {} = {}> extends CompositeLayer<
98
+ ExtraProps & Required<_HeatmapTileLayerProps<DataT>>
99
+ > {
100
+ static layerName = 'HeatmapTileLayer';
101
+ static defaultProps = defaultProps;
102
+
103
+ state!: {
104
+ isLoaded: boolean;
105
+ tiles: Set<Tile2DHeader>;
106
+ viewportChanged?: boolean;
107
+ };
108
+ initializeState() {
109
+ this.state = {
110
+ isLoaded: false,
111
+ tiles: new Set(),
112
+ viewportChanged: false
113
+ };
114
+ }
115
+
116
+ shouldUpdateState({changeFlags}): boolean {
117
+ const {viewportChanged} = changeFlags;
118
+ this.setState({viewportChanged});
119
+ return changeFlags.somethingChanged;
120
+ }
121
+
122
+ renderLayers(): Layer {
123
+ const {
124
+ data,
125
+ getWeight,
126
+ colorDomain,
127
+ colorRange,
128
+ intensity,
129
+ radiusPixels,
130
+ _subLayerProps,
131
+ updateTriggers,
132
+ onMaxDensityChange,
133
+ onViewportLoad,
134
+ onTileLoad,
135
+ onTileUnload,
136
+ ...tileLayerProps
137
+ } = this.props;
138
+
139
+ // Inject modified polygon layer as sublayer into TileLayer
140
+ const subLayerProps = {
141
+ ..._subLayerProps,
142
+ cell: {
143
+ ..._subLayerProps?.cell,
144
+ _subLayerProps: {
145
+ ..._subLayerProps?.cell?._subLayerProps,
146
+ fill: {
147
+ ..._subLayerProps?.cell?._subLayerProps?.fill,
148
+ type: RTTSolidPolygonLayer
149
+ }
150
+ }
151
+ }
152
+ };
153
+
154
+ let tileZ = 0;
155
+ let maxDensity = 0;
156
+ const tiles = [...this.state.tiles].filter(t => t.isVisible && t.data);
157
+ for (const tile of tiles) {
158
+ const cell = tile.data[0];
159
+ const unitDensity = unitDensityForCell(cell.id);
160
+ maxDensity = Math.max(tile.userData!.maxWeight * unitDensity, maxDensity);
161
+ tileZ = Math.max(tile.zoom, tileZ);
162
+ }
163
+
164
+ // Between zoom levels the max density will change, but it isn't possible to know by what factor. Uniform data distributions lead to a factor of 4, while very localized data gives 1. As a heurstic estimate with a value inbetween (2) to make the transitions less obvious.
165
+ const overzoom = this.context.viewport.zoom - tileZ;
166
+ const estimatedMaxDensity = maxDensity * Math.pow(2, overzoom);
167
+
168
+ maxDensity = estimatedMaxDensity;
169
+ if (typeof onMaxDensityChange === 'function') {
170
+ onMaxDensityChange(maxDensity);
171
+ }
172
+ return new PostProcessQuadbinTileLayer(
173
+ tileLayerProps as Omit<QuadbinTileLayerProps, 'data'>,
174
+ this.getSubLayerProps({
175
+ id: 'heatmap',
176
+ data,
177
+
178
+ // Re-use existing props to pass down values to sublayer
179
+ // TODO replace with custom layer
180
+ getFillColor: 0,
181
+ getElevation: getWeight,
182
+ elevationScale: maxDensity,
183
+
184
+ colorDomain,
185
+
186
+ colorRange,
187
+ radiusPixels,
188
+ intensity,
189
+ _subLayerProps: subLayerProps,
190
+ refinementStrategy: 'no-overlap',
191
+
192
+ // Disable line rendering
193
+ extruded: false,
194
+ stroked: false,
195
+
196
+ updateTriggers: {
197
+ getElevation: updateTriggers.getWeight
198
+ },
199
+
200
+ // Tile stats
201
+ onViewportLoad: tiles => {
202
+ this.setState({isLoaded: true});
203
+ if (typeof onViewportLoad === 'function') {
204
+ onViewportLoad(tiles);
205
+ }
206
+ },
207
+ onTileLoad: (tile: Tile2DHeader) => {
208
+ let maxWeight = -Infinity;
209
+ if (typeof getWeight !== 'function') {
210
+ maxWeight = getWeight;
211
+ } else if (tile.content) {
212
+ for (const d of tile.content) {
213
+ maxWeight = Math.max(getWeight(d, {} as any), maxWeight);
214
+ }
215
+ }
216
+ tile.userData = {maxWeight};
217
+
218
+ this.state.tiles.add(tile);
219
+
220
+ if (typeof onTileLoad === 'function') {
221
+ onTileLoad(tile);
222
+ }
223
+ },
224
+ onTileUnload: (tile: Tile2DHeader) => {
225
+ this.state.tiles.delete(tile);
226
+ if (typeof onTileUnload === 'function') {
227
+ onTileUnload(tile);
228
+ }
229
+ },
230
+ transitions: {elevationScale: {type: 'spring', stiffness: 0.3, damping: 0.5}}
231
+ })
232
+ );
233
+ }
234
+ }
235
+
236
+ export default HeatmapTileLayer;
@@ -1,5 +1,4 @@
1
1
  import type {ShaderPass} from '@luma.gl/shadertools';
2
- import {random} from '@luma.gl/shadertools';
3
2
  import {Color} from '@deck.gl/core';
4
3
  const glsl = (s: TemplateStringsArray) => `${s}`;
5
4
 
@@ -21,11 +20,10 @@ uniform heatmapUniforms {
21
20
  vec3 color4;
22
21
  vec3 color5;
23
22
  vec3 color6;
23
+ float intensity;
24
24
  float opacity;
25
25
  } heatmap;
26
26
 
27
- // Controls quality of heatmap, larger values increase quality at expense of performance
28
- const float SUPPORT = 8.0;
29
27
  const vec4 STOPS = vec4(0.2, 0.4, 0.6, 0.8);
30
28
 
31
29
  vec3 colorGradient(float value) {
@@ -44,20 +42,18 @@ vec3 colorGradient(float value) {
44
42
  } else if (value < STOPS.w) {
45
43
  range = STOPS.zw;
46
44
  c1 = heatmap.color4; c2 = heatmap.color5;
47
- } else if (value < 1.0 ) {
45
+ } else {
48
46
  range = vec2(STOPS.w, 1.0);
49
47
  c1 = heatmap.color5; c2 = heatmap.color6;
50
- } else {
51
- // Fade out to white
52
- range = vec2(1.0, 10.0);
53
- c1 = heatmap.color6; c2 = vec3(255.0);
54
48
  }
55
49
 
56
- float f = (value - range.x) / (range.y - range.x);
50
+ float f = (clamp(value, 0.0, 1.0) - range.x) / (range.y - range.x);
57
51
  return mix(c1, c2, f) / 255.0;
58
52
  }
59
53
 
60
54
  const vec3 SHIFT = vec3(1.0, 256.0, 256.0 * 256.0);
55
+ const float MAX_VAL = SHIFT.z * 255.0;
56
+ const float SCALE = MAX_VAL / 8.0;
61
57
  vec4 pack(float value) {
62
58
  return vec4(mod(vec3(value, floor(value / SHIFT.yz)), 256.0), 255.0) / 255.0;
63
59
  }
@@ -69,16 +65,15 @@ vec4 heatmap_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) {
69
65
  bool firstPass = (heatmap.delta.y < 0.5);
70
66
  float accumulator = 0.0;
71
67
 
72
- // Randomize the lookup values to hide the fixed number of samples
73
- float offset = 0.5 * random(vec3(12.9898, 78.233, 151.7182), 0.0);
68
+ // Controls quality of heatmap, larger values increase quality at expense of performance
69
+ float SUPPORT = clamp(heatmap.radiusPixels / 2.0, 8.0, 32.0);
74
70
 
75
71
  // Gaussian normalization parameters
76
- const float sigma = SUPPORT / 3.0;
77
- const float a = -0.5 / (sigma * sigma);
78
- const float w0 = 0.3989422804014327 / sigma; // 1D normalization
79
-
72
+ float sigma = SUPPORT / 3.0;
73
+ float a = -0.5 / (sigma * sigma);
74
+ float w0 = 0.3989422804014327 / sigma; // 1D normalization
80
75
  for (float t = -SUPPORT; t <= SUPPORT; t++) {
81
- vec2 percent = (t * heatmap.delta + offset - 0.5) / SUPPORT;
76
+ vec2 percent = (t * heatmap.delta - 0.5) / SUPPORT;
82
77
  vec2 delta = percent * heatmap.radiusPixels / texSize;
83
78
  vec4 offsetColor = texture(source, texCoord + delta);
84
79
 
@@ -95,8 +90,18 @@ vec4 heatmap_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) {
95
90
  return pack(accumulator);
96
91
  }
97
92
 
93
+ // Undo scaling to obtain normalized density
94
+ float density = 10.0 * heatmap.intensity * accumulator / SCALE;
95
+
96
+ // Domain also in normalized density units
97
+ vec2 domain = heatmap.colorDomain;
98
+
98
99
  // Apply domain
99
- float f = (accumulator - heatmap.colorDomain[0]) / (heatmap.colorDomain[1] - heatmap.colorDomain[0]);
100
+ float f = (density - domain[0]) / (domain[1] - domain[0]);
101
+
102
+ // sqrt/log scaling??
103
+ // float f = (log(density) - log(domain[0] + 1.0)) / (log(domain[1] + 1.0) - log(domain[0] + 1.0));
104
+ // f = sqrt(f);
100
105
 
101
106
  // Color map
102
107
  vec4 color = vec4(0.0);
@@ -138,6 +143,10 @@ export type HeatmapProps = {
138
143
  * @default `6-class YlOrRd` - [colorbrewer](http://colorbrewer2.org/#type=sequential&scheme=YlOrRd&n=6)
139
144
  */
140
145
  colorRange: Color[];
146
+ /**
147
+ * Value that is multiplied with the total weight at a pixel to obtain the final weight. A value larger than 1 biases the output color towards the higher end of the spectrum, and a value less than 1 biases the output color towards the lower end of the spectrum.
148
+ */
149
+ intensity?: number;
141
150
  opacity: number;
142
151
  };
143
152
 
@@ -151,6 +160,7 @@ export type HeatmapUniforms = {
151
160
  color4?: [number, number, number];
152
161
  color5?: [number, number, number];
153
162
  color6?: [number, number, number];
163
+ intensity: number;
154
164
  opacity?: number;
155
165
  };
156
166
 
@@ -166,6 +176,7 @@ export const heatmap: ShaderPass<HeatmapProps, HeatmapUniforms> = {
166
176
  color4: {value: [0, 0, 0]},
167
177
  color5: {value: [0, 0, 0]},
168
178
  color6: {value: [0, 0, 0]},
179
+ intensity: {value: 1, min: 0.1, max: 10},
169
180
  opacity: {value: 1, min: 0, max: 1}
170
181
  },
171
182
  uniformTypes: {
@@ -178,6 +189,7 @@ export const heatmap: ShaderPass<HeatmapProps, HeatmapUniforms> = {
178
189
  color4: 'vec3<f32>',
179
190
  color5: 'vec3<f32>',
180
191
  color6: 'vec3<f32>',
192
+ intensity: 'f32',
181
193
  opacity: 'f32'
182
194
  },
183
195
  getUniforms: opts => {
@@ -186,6 +198,7 @@ export const heatmap: ShaderPass<HeatmapProps, HeatmapUniforms> = {
186
198
  colorRange = defaultColorRange,
187
199
  radiusPixels = 20,
188
200
  colorDomain = [0, 1],
201
+ intensity = 1,
189
202
  opacity = 1
190
203
  } = opts as HeatmapProps & {delta: [number, number]};
191
204
  const [color1, color2, color3, color4, color5, color6] = colorRange;
@@ -199,10 +212,10 @@ export const heatmap: ShaderPass<HeatmapProps, HeatmapUniforms> = {
199
212
  color6,
200
213
  radiusPixels,
201
214
  colorDomain,
215
+ intensity,
202
216
  opacity
203
217
  };
204
218
  },
205
- dependencies: [random],
206
219
  fs,
207
220
  passes: [
208
221
  {sampler: true, uniforms: {delta: [1, 0]}},
@@ -14,9 +14,6 @@ import {
14
14
  _TileLoadProps as TileLoadProps
15
15
  } from '@deck.gl/geo-layers';
16
16
  import {GeoJsonLayer} from '@deck.gl/layers';
17
- import {binaryToGeojson} from '@loaders.gl/gis';
18
- import type {BinaryFeatureCollection} from '@loaders.gl/schema';
19
- import type {Feature, Geometry} from 'geojson';
20
17
 
21
18
  import type {TilejsonResult} from '../sources/types';
22
19
  import {TilejsonPropType, injectAccessToken, mergeBoundaryData} from './utils';
@@ -148,16 +145,9 @@ export default class VectorTileLayer<
148
145
  return subLayer;
149
146
  }
150
147
 
151
- getPickingInfo(params) {
152
- const info = super.getPickingInfo(params);
153
-
154
- if (this.state.binary && info.index !== -1) {
155
- const {data} = params.sourceLayer!.props;
156
- info.object = binaryToGeojson(data as BinaryFeatureCollection, {
157
- globalFeatureId: info.index
158
- }) as Feature<Geometry, FeaturePropertiesT>;
159
- }
160
-
161
- return info;
148
+ protected override _isWGS84(): boolean {
149
+ // CARTO binary tile coordinates are [lng, lat], not tile-relative like MVT.
150
+ if (this.state.mvt) return super._isWGS84();
151
+ return true;
162
152
  }
163
153
  }
@@ -1,24 +0,0 @@
1
- import { Accessor, CompositeLayer, CompositeLayerProps, DefaultProps, Layer } from '@deck.gl/core';
2
- import { HeatmapProps } from "./heatmap.js";
3
- import { QuadbinTileLayerProps } from "./quadbin-tile-layer.js";
4
- import { TilejsonResult } from "../sources.js";
5
- /** All properties supported by QuadbinHeatmapTileLayer. */
6
- export type QuadbinHeatmapTileLayerProps<DataT = unknown> = _QuadbinHeatmapTileLayerProps<DataT> & CompositeLayerProps & {
7
- data: null | TilejsonResult | Promise<TilejsonResult>;
8
- };
9
- /** Properties added by QuadbinHeatmapTileLayer. */
10
- type _QuadbinHeatmapTileLayerProps<DataT> = QuadbinTileLayerProps<DataT> & HeatmapProps & {
11
- /**
12
- * The weight of each object.
13
- *
14
- * @default 1
15
- */
16
- getWeight?: Accessor<DataT, number>;
17
- };
18
- declare class QuadbinHeatmapTileLayer<DataT = any, ExtraProps extends {} = {}> extends CompositeLayer<ExtraProps & Required<_QuadbinHeatmapTileLayerProps<DataT>>> {
19
- static layerName: string;
20
- static defaultProps: DefaultProps<QuadbinHeatmapTileLayerProps<unknown>>;
21
- renderLayers(): Layer;
22
- }
23
- export default QuadbinHeatmapTileLayer;
24
- //# sourceMappingURL=quadbin-heatmap-tile-layer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"quadbin-heatmap-tile-layer.d.ts","sourceRoot":"","sources":["../../src/layers/quadbin-heatmap-tile-layer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,KAAK,EAAC,MAAM,eAAe,CAAC;AAGjG,OAAO,EAAC,YAAY,EAAU,qBAAkB;AAEhD,OAAyB,EAAC,qBAAqB,EAAC,gCAA6B;AAE7E,OAAO,EAAC,cAAc,EAAC,sBAAmB;AAiD1C,2DAA2D;AAC3D,MAAM,MAAM,4BAA4B,CAAC,KAAK,GAAG,OAAO,IAAI,6BAA6B,CAAC,KAAK,CAAC,GAC9F,mBAAmB,GAAG;IACpB,IAAI,EAAE,IAAI,GAAG,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACvD,CAAC;AAEJ,mDAAmD;AACnD,KAAK,6BAA6B,CAAC,KAAK,IAAI,qBAAqB,CAAC,KAAK,CAAC,GACtE,YAAY,GAAG;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CACrC,CAAC;AAEJ,cAAM,uBAAuB,CAAC,KAAK,GAAG,GAAG,EAAE,UAAU,SAAS,EAAE,GAAG,EAAE,CAAE,SAAQ,cAAc,CAC3F,UAAU,GAAG,QAAQ,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAC5D;IACC,MAAM,CAAC,SAAS,SAA6B;IAC7C,MAAM,CAAC,YAAY,sDAAgB;IAEnC,YAAY,IAAI,KAAK;CAmDtB;AAED,eAAe,uBAAuB,CAAC"}
@@ -1,84 +0,0 @@
1
- import { getResolution } from 'quadbin';
2
- import { CompositeLayer } from '@deck.gl/core';
3
- import { SolidPolygonLayer } from '@deck.gl/layers';
4
- import { heatmap } from "./heatmap.js";
5
- import { RTTModifier, PostProcessModifier } from "./post-process-utils.js";
6
- import QuadbinTileLayer from "./quadbin-tile-layer.js";
7
- import { TilejsonPropType } from "./utils.js";
8
- // Modified polygon layer to draw offscreen and output value expected by heatmap
9
- class RTTSolidPolygonLayer extends RTTModifier(SolidPolygonLayer) {
10
- static { this.layerName = 'RTTSolidPolygonLayer'; }
11
- getShaders(type) {
12
- const shaders = super.getShaders(type);
13
- shaders.inject = {
14
- 'vs:#decl': 'uniform float cellResolution;',
15
- 'vs:DECKGL_FILTER_COLOR': `
16
- const vec3 SHIFT = vec3(1.0, 256.0, 256.0 * 256.0);
17
- float value = 255.0 * dot(SHIFT, color.rgb);
18
-
19
- // Keep "power" delivered to screen constant when tiles update
20
- float relativeZoom = log2(project_uScale) - cellResolution + 3.5; // range 0-1
21
- float relativeArea = pow(2.0, -2.0 * relativeZoom); // range 0.25-1
22
- value *= relativeArea;
23
-
24
- // Pack float into 3 channels to pass to heatmap shader
25
- color = vec4(mod(vec3(value, floor(value / SHIFT.yz)), 256.0), 255.0) / 255.0;
26
- `
27
- };
28
- return shaders;
29
- }
30
- draw(opts) {
31
- const cell0 = this.props.data[0];
32
- const cellResolution = Number(getResolution(cell0.id));
33
- for (const model of this.state.models) {
34
- model.setUniforms({ cellResolution });
35
- }
36
- super.draw(opts);
37
- }
38
- }
39
- // Modify QuadbinTileLayer to apply heatmap post process effect
40
- const PostProcessQuadbinTileLayer = PostProcessModifier(QuadbinTileLayer, heatmap);
41
- function encodeWeight(w) {
42
- return [w % 256, Math.floor(w / 256), Math.floor(w / (256 * 256))];
43
- }
44
- const defaultProps = {
45
- data: TilejsonPropType,
46
- getWeight: { type: 'accessor', value: 1 }
47
- };
48
- class QuadbinHeatmapTileLayer extends CompositeLayer {
49
- static { this.layerName = 'QuadbinHeatmapTileLayer'; }
50
- static { this.defaultProps = defaultProps; }
51
- renderLayers() {
52
- const { data, getWeight, colorDomain, colorRange, radiusPixels, _subLayerProps, updateTriggers, ...tileLayerProps } = this.props;
53
- // Inject modified polygon layer as sublayer into TileLayer
54
- const subLayerProps = {
55
- ..._subLayerProps,
56
- cell: {
57
- ..._subLayerProps?.cell,
58
- _subLayerProps: {
59
- ..._subLayerProps?.cell?._subLayerProps,
60
- fill: {
61
- ..._subLayerProps?.cell?._subLayerProps?.fill,
62
- type: RTTSolidPolygonLayer
63
- }
64
- }
65
- }
66
- };
67
- const getFillColor = typeof getWeight === 'function'
68
- ? (d, info) => encodeWeight(getWeight(d, info))
69
- : encodeWeight(getWeight);
70
- return new PostProcessQuadbinTileLayer(tileLayerProps, this.getSubLayerProps({
71
- id: 'heatmap',
72
- data,
73
- getFillColor,
74
- colorDomain,
75
- colorRange,
76
- radiusPixels,
77
- _subLayerProps: subLayerProps,
78
- updateTriggers: {
79
- getPosition: updateTriggers.getWeight
80
- }
81
- }));
82
- }
83
- }
84
- export default QuadbinHeatmapTileLayer;