@deck.gl/carto 9.0.16 → 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/dist/api/carto-api-error.d.ts +3 -1
- package/dist/api/carto-api-error.d.ts.map +1 -1
- package/dist/api/carto-api-error.js +2 -1
- package/dist/api/common.d.ts +0 -8
- package/dist/api/common.d.ts.map +1 -1
- package/dist/api/common.js +0 -9
- package/dist/api/request-with-parameters.d.ts +1 -1
- package/dist/api/request-with-parameters.d.ts.map +1 -1
- package/dist/api/request-with-parameters.js +48 -15
- package/dist/api/types.d.ts +5 -5
- package/dist/api/types.d.ts.map +1 -1
- 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 +494 -560
- package/dist/index.cjs +445 -358
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/layers/heatmap-tile-layer.d.ts +36 -0
- package/dist/layers/heatmap-tile-layer.d.ts.map +1 -0
- package/dist/layers/heatmap-tile-layer.js +165 -0
- package/dist/layers/heatmap.d.ts +5 -0
- package/dist/layers/heatmap.d.ts.map +1 -1
- package/dist/layers/heatmap.js +17 -15
- 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/layers/vector-tile-layer.d.ts +1 -1
- package/dist/layers/vector-tile-layer.d.ts.map +1 -1
- package/dist/layers/vector-tile-layer.js +5 -10
- package/dist/sources/raster-source.d.ts +2 -2
- package/dist/sources/raster-source.d.ts.map +1 -1
- package/dist/sources/raster-source.js +4 -1
- package/dist.min.js +37 -31
- package/package.json +2 -2
- package/src/api/carto-api-error.ts +10 -1
- package/src/api/common.ts +0 -12
- package/src/api/request-with-parameters.ts +55 -19
- package/src/api/types.ts +5 -5
- package/src/index.ts +3 -3
- package/src/layers/heatmap-tile-layer.ts +236 -0
- package/src/layers/heatmap.ts +31 -18
- package/src/layers/vector-tile-layer.ts +4 -14
- package/src/sources/raster-source.ts +10 -5
- package/dist/layers/quadbin-heatmap-tile-layer.d.ts +0 -24
- package/dist/layers/quadbin-heatmap-tile-layer.d.ts.map +0 -1
- package/dist/layers/quadbin-heatmap-tile-layer.js +0 -84
- package/src/layers/quadbin-heatmap-tile-layer.ts +0 -125
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.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": "
|
|
76
|
+
"gitHead": "32f7e51eb96319739a18a3ca22092e60948fcbdb"
|
|
77
77
|
}
|
|
@@ -16,7 +16,15 @@ export class CartoAPIError extends Error {
|
|
|
16
16
|
/** Response from server */
|
|
17
17
|
response?: Response;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
/** JSON Response from server */
|
|
20
|
+
responseJson?: any;
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
error: Error,
|
|
24
|
+
errorContext: APIErrorContext,
|
|
25
|
+
response?: Response,
|
|
26
|
+
responseJson?: any
|
|
27
|
+
) {
|
|
20
28
|
let responseString = 'Failed to connect';
|
|
21
29
|
if (response) {
|
|
22
30
|
responseString = 'Server returned: ';
|
|
@@ -46,6 +54,7 @@ export class CartoAPIError extends Error {
|
|
|
46
54
|
|
|
47
55
|
this.name = 'CartoAPIError';
|
|
48
56
|
this.response = response;
|
|
57
|
+
this.responseJson = responseJson;
|
|
49
58
|
this.error = error;
|
|
50
59
|
this.errorContext = errorContext;
|
|
51
60
|
}
|
package/src/api/common.ts
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
import {VERSION} from '@deck.gl/core';
|
|
2
|
-
|
|
3
1
|
export const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
|
|
4
2
|
export const DEFAULT_CLIENT = 'deck-gl-carto';
|
|
5
3
|
export const V3_MINOR_VERSION = '3.4';
|
|
6
4
|
export const MAX_GET_LENGTH = 8192;
|
|
7
|
-
|
|
8
|
-
export const DEFAULT_PARAMETERS = {
|
|
9
|
-
v: V3_MINOR_VERSION,
|
|
10
|
-
deckglVersion: VERSION
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const DEFAULT_HEADERS = {
|
|
14
|
-
Accept: 'application/json',
|
|
15
|
-
'Content-Type': 'application/json'
|
|
16
|
-
};
|
|
@@ -1,37 +1,45 @@
|
|
|
1
|
+
import {VERSION} from '@deck.gl/core';
|
|
1
2
|
import {isPureObject} from '../utils';
|
|
2
3
|
import {CartoAPIError} from './carto-api-error';
|
|
3
|
-
import {
|
|
4
|
+
import {MAX_GET_LENGTH, V3_MINOR_VERSION} from './common';
|
|
4
5
|
import type {APIErrorContext} from './types';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Parameters added to all requests issued with `requestWithParameters()`.
|
|
9
|
+
* These parameters override parameters already in the base URL, but not
|
|
10
|
+
* user-provided parameters.
|
|
8
11
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const DEFAULT_PARAMETERS = {
|
|
13
|
+
v: V3_MINOR_VERSION,
|
|
14
|
+
deckglVersion: VERSION
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DEFAULT_HEADERS = {
|
|
18
|
+
Accept: 'application/json',
|
|
19
|
+
'Content-Type': 'application/json'
|
|
20
|
+
};
|
|
15
21
|
|
|
16
22
|
const REQUEST_CACHE = new Map<string, Promise<unknown>>();
|
|
23
|
+
|
|
17
24
|
export async function requestWithParameters<T = any>({
|
|
18
25
|
baseUrl,
|
|
19
|
-
parameters,
|
|
20
|
-
headers: customHeaders,
|
|
26
|
+
parameters = {},
|
|
27
|
+
headers: customHeaders = {},
|
|
21
28
|
errorContext
|
|
22
29
|
}: {
|
|
23
30
|
baseUrl: string;
|
|
24
31
|
parameters?: Record<string, unknown>;
|
|
25
|
-
headers
|
|
32
|
+
headers?: Record<string, string>;
|
|
26
33
|
errorContext: APIErrorContext;
|
|
27
34
|
}): Promise<T> {
|
|
28
35
|
parameters = {...DEFAULT_PARAMETERS, ...parameters};
|
|
29
|
-
|
|
36
|
+
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
37
|
+
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
30
38
|
if (REQUEST_CACHE.has(key)) {
|
|
31
39
|
return REQUEST_CACHE.get(key) as Promise<T>;
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
const url =
|
|
42
|
+
const url = createURLWithParameters(baseUrl, parameters);
|
|
35
43
|
const headers = {...DEFAULT_HEADERS, ...customHeaders};
|
|
36
44
|
|
|
37
45
|
/* global fetch */
|
|
@@ -41,12 +49,14 @@ export async function requestWithParameters<T = any>({
|
|
|
41
49
|
: fetch(url, {headers});
|
|
42
50
|
|
|
43
51
|
let response: Response | undefined;
|
|
52
|
+
let responseJson;
|
|
44
53
|
const jsonPromise: Promise<T> = fetchPromise
|
|
45
54
|
.then((_response: Response) => {
|
|
46
55
|
response = _response;
|
|
47
56
|
return response.json();
|
|
48
57
|
})
|
|
49
58
|
.then((json: any) => {
|
|
59
|
+
responseJson = json;
|
|
50
60
|
if (!response || !response.ok) {
|
|
51
61
|
throw new Error(json.error);
|
|
52
62
|
}
|
|
@@ -54,7 +64,7 @@ export async function requestWithParameters<T = any>({
|
|
|
54
64
|
})
|
|
55
65
|
.catch((error: Error) => {
|
|
56
66
|
REQUEST_CACHE.delete(key);
|
|
57
|
-
throw new CartoAPIError(error, errorContext, response);
|
|
67
|
+
throw new CartoAPIError(error, errorContext, response, responseJson);
|
|
58
68
|
});
|
|
59
69
|
|
|
60
70
|
REQUEST_CACHE.set(key, jsonPromise);
|
|
@@ -71,9 +81,35 @@ function createCacheKey(
|
|
|
71
81
|
return JSON.stringify({baseUrl, parameters: parameterEntries, headers: headerEntries});
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Appends query string parameters to a URL. Existing URL parameters are kept,
|
|
86
|
+
* unless there is a conflict, in which case the new parameters override
|
|
87
|
+
* those already in the URL.
|
|
88
|
+
*/
|
|
89
|
+
function createURLWithParameters(
|
|
90
|
+
baseUrlString: string,
|
|
91
|
+
parameters: Record<string, unknown>
|
|
92
|
+
): string {
|
|
93
|
+
const baseUrl = new URL(baseUrlString);
|
|
94
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
95
|
+
if (isPureObject(value) || Array.isArray(value)) {
|
|
96
|
+
baseUrl.searchParams.set(key, JSON.stringify(value));
|
|
97
|
+
} else {
|
|
98
|
+
baseUrl.searchParams.set(key, (value as string | boolean | number).toString());
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return baseUrl.toString();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Deletes query string parameters from a URL.
|
|
106
|
+
*/
|
|
107
|
+
function excludeURLParameters(baseUrlString: string, parameters: string[]) {
|
|
108
|
+
const baseUrl = new URL(baseUrlString);
|
|
109
|
+
for (const param of parameters) {
|
|
110
|
+
if (baseUrl.searchParams.has(param)) {
|
|
111
|
+
baseUrl.searchParams.delete(param);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return baseUrl.toString();
|
|
79
115
|
}
|
package/src/api/types.ts
CHANGED
|
@@ -66,11 +66,11 @@ export interface Filters {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
interface Filter {
|
|
69
|
-
[FilterTypes.In]
|
|
70
|
-
[FilterTypes.Between]
|
|
71
|
-
[FilterTypes.ClosedOpen]
|
|
72
|
-
[FilterTypes.Time]
|
|
73
|
-
[FilterTypes.StringSearch]
|
|
69
|
+
[FilterTypes.In]?: number[];
|
|
70
|
+
[FilterTypes.Between]?: number[][];
|
|
71
|
+
[FilterTypes.ClosedOpen]?: number[][];
|
|
72
|
+
[FilterTypes.Time]?: number[][];
|
|
73
|
+
[FilterTypes.StringSearch]?: string[];
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export enum FilterTypes {
|
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;
|
package/src/layers/heatmap.ts
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
73
|
-
float
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
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 = (
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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,12 +1,17 @@
|
|
|
1
1
|
import {baseSource} from './base-source';
|
|
2
|
-
import type {SourceOptions, TilejsonResult, TilesetSourceOptions} from './types';
|
|
2
|
+
import type {FilterOptions, SourceOptions, TilejsonResult, TilesetSourceOptions} from './types';
|
|
3
3
|
|
|
4
|
-
export type RasterSourceOptions = SourceOptions & TilesetSourceOptions;
|
|
5
|
-
type UrlParameters = {
|
|
4
|
+
export type RasterSourceOptions = SourceOptions & TilesetSourceOptions & FilterOptions;
|
|
5
|
+
type UrlParameters = {
|
|
6
|
+
name: string;
|
|
7
|
+
filters?: Record<string, unknown>;
|
|
8
|
+
};
|
|
6
9
|
|
|
7
10
|
export const rasterSource = async function (options: RasterSourceOptions): Promise<TilejsonResult> {
|
|
8
|
-
const {tableName} = options;
|
|
11
|
+
const {tableName, filters} = options;
|
|
9
12
|
const urlParameters: UrlParameters = {name: tableName};
|
|
10
|
-
|
|
13
|
+
if (filters) {
|
|
14
|
+
urlParameters.filters = filters;
|
|
15
|
+
}
|
|
11
16
|
return baseSource<UrlParameters>('raster', options, urlParameters) as Promise<TilejsonResult>;
|
|
12
17
|
};
|
|
@@ -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
|