@deck.gl/carto 8.9.3 → 8.9.5
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/dist.dev.js +359 -174
- package/dist/es5/api/layer-map.js +44 -44
- package/dist/es5/api/layer-map.js.map +1 -1
- package/dist/es5/api/maps-v3-client.js +22 -4
- package/dist/es5/api/maps-v3-client.js.map +1 -1
- package/dist/es5/api/parseMap.js +64 -48
- package/dist/es5/api/parseMap.js.map +1 -1
- package/dist/es5/index.js +8 -0
- package/dist/es5/index.js.map +1 -1
- package/dist/es5/layers/point-label-layer.js +268 -0
- package/dist/es5/layers/point-label-layer.js.map +1 -0
- package/dist/es5/layers/schema/carto-spatial-tile-loader.js +1 -9
- package/dist/es5/layers/schema/carto-spatial-tile-loader.js.map +1 -1
- package/dist/es5/layers/schema/carto-spatial-tile.js +5 -31
- package/dist/es5/layers/schema/carto-spatial-tile.js.map +1 -1
- package/dist/es5/layers/schema/carto-tile.js +12 -6
- package/dist/es5/layers/schema/carto-tile.js.map +1 -1
- package/dist/es5/layers/schema/carto-vector-tile-loader.js +1 -21
- package/dist/es5/layers/schema/carto-vector-tile-loader.js.map +1 -1
- package/dist/es5/layers/schema/fast-pbf.js +22 -0
- package/dist/es5/layers/schema/fast-pbf.js.map +1 -0
- package/dist/es5/layers/schema/tile-loader-utils.js +0 -17
- package/dist/es5/layers/schema/tile-loader-utils.js.map +1 -1
- package/dist/es5/layers/spatial-index-tile-layer.js +19 -1
- package/dist/es5/layers/spatial-index-tile-layer.js.map +1 -1
- package/dist/esm/api/layer-map.js +29 -21
- package/dist/esm/api/layer-map.js.map +1 -1
- package/dist/esm/api/maps-v3-client.js +12 -1
- package/dist/esm/api/maps-v3-client.js.map +1 -1
- package/dist/esm/api/parseMap.js +59 -44
- package/dist/esm/api/parseMap.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/layers/point-label-layer.js +198 -0
- package/dist/esm/layers/point-label-layer.js.map +1 -0
- package/dist/esm/layers/schema/carto-spatial-tile-loader.js +2 -4
- package/dist/esm/layers/schema/carto-spatial-tile-loader.js.map +1 -1
- package/dist/esm/layers/schema/carto-spatial-tile.js +3 -28
- package/dist/esm/layers/schema/carto-spatial-tile.js.map +1 -1
- package/dist/esm/layers/schema/carto-tile.js +12 -6
- package/dist/esm/layers/schema/carto-tile.js.map +1 -1
- package/dist/esm/layers/schema/carto-vector-tile-loader.js +2 -18
- package/dist/esm/layers/schema/carto-vector-tile-loader.js.map +1 -1
- package/dist/esm/layers/schema/fast-pbf.js +13 -0
- package/dist/esm/layers/schema/fast-pbf.js.map +1 -0
- package/dist/esm/layers/schema/tile-loader-utils.js +0 -16
- package/dist/esm/layers/schema/tile-loader-utils.js.map +1 -1
- package/dist/esm/layers/spatial-index-tile-layer.js +21 -1
- package/dist/esm/layers/spatial-index-tile-layer.js.map +1 -1
- package/dist.min.js +24 -23
- package/package.json +2 -2
- package/src/api/layer-map.ts +21 -25
- package/src/api/maps-v3-client.ts +19 -4
- package/src/api/parseMap.ts +67 -44
- package/src/api/types.ts +1 -0
- package/src/index.ts +1 -0
- package/src/layers/point-label-layer.ts +266 -0
- package/src/layers/schema/carto-spatial-tile-loader.ts +2 -5
- package/src/layers/schema/carto-spatial-tile.ts +5 -30
- package/src/layers/schema/carto-tile.ts +11 -12
- package/src/layers/schema/carto-vector-tile-loader.ts +2 -9
- package/src/layers/schema/fast-pbf.ts +13 -0
- package/src/layers/schema/tile-loader-utils.ts +0 -15
- package/src/layers/spatial-index-tile-layer.ts +22 -1
- package/typed/api/layer-map.d.ts +1 -2
- package/typed/api/layer-map.d.ts.map +1 -1
- package/typed/api/maps-v3-client.d.ts +4 -1
- package/typed/api/maps-v3-client.d.ts.map +1 -1
- package/typed/api/parseMap.d.ts +1 -1
- package/typed/api/parseMap.d.ts.map +1 -1
- package/typed/api/types.d.ts +1 -0
- package/typed/api/types.d.ts.map +1 -1
- package/typed/index.d.ts +1 -0
- package/typed/index.d.ts.map +1 -1
- package/typed/layers/point-label-layer.d.ts +56 -0
- package/typed/layers/point-label-layer.d.ts.map +1 -0
- package/typed/layers/schema/carto-spa-tile.d.ts +57 -0
- package/typed/layers/schema/carto-spa-tile.d.ts.map +1 -0
- package/typed/layers/schema/carto-spatial-tile-loader.d.ts.map +1 -1
- package/typed/layers/schema/carto-spatial-tile.d.ts +2 -2
- package/typed/layers/schema/carto-spatial-tile.d.ts.map +1 -1
- package/typed/layers/schema/carto-tile.d.ts +2 -9
- package/typed/layers/schema/carto-tile.d.ts.map +1 -1
- package/typed/layers/schema/carto-vector-tile-loader.d.ts.map +1 -1
- package/typed/layers/schema/fast-pbf.d.ts +3 -0
- package/typed/layers/schema/fast-pbf.d.ts.map +1 -0
- package/typed/layers/schema/tile-loader-utils.d.ts +0 -3
- package/typed/layers/schema/tile-loader-utils.d.ts.map +1 -1
- package/typed/layers/spatial-index-tile-layer.d.ts +1 -0
- package/typed/layers/spatial-index-tile-layer.d.ts.map +1 -1
- package/typed/api/index.js +0 -3
- package/typed/api/layer-map.js +0 -264
- package/typed/api/maps-api-common.js +0 -50
- package/typed/api/maps-client.js +0 -125
- package/typed/api/maps-v3-client.js +0 -304
- package/typed/api/parseMap.js +0 -178
- package/typed/basemap.js +0 -9
- package/typed/config.js +0 -35
- package/typed/index.js +0 -10
- package/typed/layers/carto-layer.js +0 -173
- package/typed/layers/carto-tile-layer.js +0 -105
- package/typed/layers/h3-tile-layer.js +0 -47
- package/typed/layers/h3-tileset-2d.js +0 -85
- package/typed/layers/quadkey-tile-layer.js +0 -42
- package/typed/layers/quadkey-tileset-2d.js +0 -36
- package/typed/layers/schema/carto-tile.js +0 -161
- package/typed/layers/spatial-index-tile-layer.js +0 -40
- package/typed/style/color-bins-style.js +0 -12
- package/typed/style/color-categories-style.js +0 -16
- package/typed/style/color-continuous-style.js +0 -12
- package/typed/style/palette.js +0 -62
- package/typed/style/utils.js +0 -15
- package/typed/utils.js +0 -4
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@deck.gl/carto",
|
|
3
3
|
"description": "CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "8.9.
|
|
5
|
+
"version": "8.9.5",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"@deck.gl/layers": "^8.0.0",
|
|
63
63
|
"@loaders.gl/core": "^3.0.0"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "001fc31bd3dfa02eb4a0caba299584952f2236f6"
|
|
66
66
|
}
|
package/src/api/layer-map.ts
CHANGED
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
CustomMarkersRange,
|
|
28
28
|
MapDataset,
|
|
29
29
|
MapTextSubLayerConfig,
|
|
30
|
-
TextLabel,
|
|
31
30
|
VisConfig,
|
|
32
31
|
VisualChannelField,
|
|
33
32
|
VisualChannels
|
|
@@ -140,7 +139,7 @@ export function getLayer(
|
|
|
140
139
|
): {Layer: ConstructorOf<Layer>; propMap: any; defaultProps: any} {
|
|
141
140
|
let basePropMap: any = sharedPropMap;
|
|
142
141
|
|
|
143
|
-
if (config.visConfig?.customMarkers
|
|
142
|
+
if (config.visConfig?.customMarkers) {
|
|
144
143
|
basePropMap = mergePropMaps(sharedPropMap, customMarkersPropsMap);
|
|
145
144
|
}
|
|
146
145
|
if (type === 'mvt' || type === 'tileset' || type === 'h3' || type === 'quadbin') {
|
|
@@ -296,8 +295,26 @@ function calculateDomain(data, name, scaleType, scaleLength?) {
|
|
|
296
295
|
|
|
297
296
|
function normalizeAccessor(accessor, data) {
|
|
298
297
|
if (data.features || data.tilestats) {
|
|
299
|
-
return (
|
|
300
|
-
|
|
298
|
+
return (object, info) => {
|
|
299
|
+
if (object) {
|
|
300
|
+
return accessor(object.properties || object.__source.object.properties);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const {data, index} = info;
|
|
304
|
+
const {properties, numericProps} = data;
|
|
305
|
+
const proxy = new Proxy(properties[index] || {}, {
|
|
306
|
+
get(target, property, receiver) {
|
|
307
|
+
if (property in numericProps) {
|
|
308
|
+
return numericProps[property as string].value[index];
|
|
309
|
+
}
|
|
310
|
+
return target[property as string];
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
has(target, property) {
|
|
314
|
+
return property in numericProps || property in target;
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
return accessor(proxy);
|
|
301
318
|
};
|
|
302
319
|
}
|
|
303
320
|
return accessor;
|
|
@@ -468,25 +485,4 @@ export function getTextAccessor({name, type}: VisualChannelField, data) {
|
|
|
468
485
|
return normalizeAccessor(accessor, data);
|
|
469
486
|
}
|
|
470
487
|
|
|
471
|
-
export function getTextPixelOffsetAccessor(
|
|
472
|
-
{alignment, anchor, size}: TextLabel,
|
|
473
|
-
radius
|
|
474
|
-
): Accessor<unknown, [number, number]> {
|
|
475
|
-
const padding = 20;
|
|
476
|
-
const signX = anchor === 'middle' ? 0 : anchor === 'start' ? 1 : -1;
|
|
477
|
-
const signY = alignment === 'center' ? 0 : alignment === 'bottom' ? 1 : -1;
|
|
478
|
-
const sizeOffset = alignment === 'center' ? 0 : size;
|
|
479
|
-
|
|
480
|
-
const calculateOffset = (r: number): [number, number] => [
|
|
481
|
-
signX * (r + padding),
|
|
482
|
-
signY * (r + padding + sizeOffset)
|
|
483
|
-
];
|
|
484
|
-
|
|
485
|
-
return typeof radius === 'function'
|
|
486
|
-
? d => {
|
|
487
|
-
return calculateOffset(radius(d));
|
|
488
|
-
}
|
|
489
|
-
: calculateOffset(radius);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
488
|
export {domainFromValues as _domainFromValues};
|
|
@@ -534,6 +534,7 @@ async function fillInTileStats(
|
|
|
534
534
|
return await Promise.all(promises);
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
/* eslint-disable max-statements */
|
|
537
538
|
export async function fetchMap({
|
|
538
539
|
cartoMapId,
|
|
539
540
|
clientId,
|
|
@@ -613,8 +614,22 @@ export async function fetchMap({
|
|
|
613
614
|
|
|
614
615
|
// Mutates attributes in visualChannels to contain tile stats
|
|
615
616
|
await fillInTileStats(map, localCreds);
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
617
|
+
const out = {...parseMap(map), ...{stopAutoRefresh}};
|
|
618
|
+
|
|
619
|
+
const textLayers = out.layers.filter(layer => {
|
|
620
|
+
const pointType = layer.props.pointType || '';
|
|
621
|
+
return pointType.includes('text');
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
/* global FontFace, window, document */
|
|
625
|
+
if (textLayers.length && window.FontFace && !document.fonts.check('12px Inter')) {
|
|
626
|
+
// Fetch font needed for labels
|
|
627
|
+
const font = new FontFace(
|
|
628
|
+
'Inter',
|
|
629
|
+
'url(https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2)'
|
|
630
|
+
);
|
|
631
|
+
await font.load().then(f => document.fonts.add(f));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return out;
|
|
620
635
|
}
|
package/src/api/parseMap.ts
CHANGED
|
@@ -6,16 +6,19 @@ import {
|
|
|
6
6
|
getColorValueAccessor,
|
|
7
7
|
getSizeAccessor,
|
|
8
8
|
getTextAccessor,
|
|
9
|
-
getTextPixelOffsetAccessor,
|
|
10
9
|
OPACITY_MAP,
|
|
11
10
|
opacityToAlpha,
|
|
12
11
|
getIconUrlAccessor,
|
|
13
12
|
negateAccessor,
|
|
14
13
|
getMaxMarkerSize
|
|
15
14
|
} from './layer-map';
|
|
16
|
-
import
|
|
15
|
+
import PointLabelLayer from '../layers/point-label-layer';
|
|
16
|
+
import {log} from '@deck.gl/core';
|
|
17
|
+
import {CollisionFilterExtension} from '@deck.gl/extensions';
|
|
17
18
|
import {assert} from '../utils';
|
|
18
|
-
import {MapDataset,
|
|
19
|
+
import {MapDataset, MapLayerConfig, VisualChannels} from './types';
|
|
20
|
+
|
|
21
|
+
const collisionFilterExtension = new CollisionFilterExtension();
|
|
19
22
|
|
|
20
23
|
export function parseMap(json) {
|
|
21
24
|
const {keplerMapConfig, datasets, token} = json;
|
|
@@ -32,7 +35,7 @@ export function parseMap(json) {
|
|
|
32
35
|
initialViewState: mapState,
|
|
33
36
|
mapStyle,
|
|
34
37
|
token,
|
|
35
|
-
layers:
|
|
38
|
+
layers: layers.reverse().map(({id, type, config, visualChannels}) => {
|
|
36
39
|
try {
|
|
37
40
|
const {dataId} = config;
|
|
38
41
|
const dataset: MapDataset | null = datasets.find(d => d.id === dataId);
|
|
@@ -45,9 +48,9 @@ export function parseMap(json) {
|
|
|
45
48
|
id,
|
|
46
49
|
data,
|
|
47
50
|
...defaultProps,
|
|
48
|
-
...
|
|
51
|
+
...createInteractionProps(interactionConfig),
|
|
49
52
|
...styleProps,
|
|
50
|
-
...createChannelProps(
|
|
53
|
+
...createChannelProps(id, type, config, visualChannels, data), // Must come after style
|
|
51
54
|
...createParametersProp(layerBlending, styleProps.parameters || {}), // Must come after style
|
|
52
55
|
...createLoadOptions(token)
|
|
53
56
|
});
|
|
@@ -59,34 +62,6 @@ export function parseMap(json) {
|
|
|
59
62
|
};
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
function extractTextLayers(layers) {
|
|
63
|
-
return flatten(
|
|
64
|
-
layers.map(({id, config, ...rest}) => {
|
|
65
|
-
const {textLabel, ...configRest} = config;
|
|
66
|
-
return [
|
|
67
|
-
// Original layer without textLabel
|
|
68
|
-
{id, config: configRest, ...rest},
|
|
69
|
-
|
|
70
|
-
// One layer per valid text label, with full opacity
|
|
71
|
-
...textLabel
|
|
72
|
-
.filter(t => t.field)
|
|
73
|
-
.map(t => {
|
|
74
|
-
return {
|
|
75
|
-
id: `${id}-label-${t.field.name}`,
|
|
76
|
-
config: {
|
|
77
|
-
textLabel: t,
|
|
78
|
-
...configRest,
|
|
79
|
-
label: `${config.label}-label-${t.field.name}`,
|
|
80
|
-
visConfig: {...configRest.visConfig, opacity: 1}
|
|
81
|
-
},
|
|
82
|
-
...rest
|
|
83
|
-
};
|
|
84
|
-
})
|
|
85
|
-
];
|
|
86
|
-
})
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
65
|
function createParametersProp(layerBlending, parameters: Record<string, any>) {
|
|
91
66
|
if (layerBlending === 'additive') {
|
|
92
67
|
parameters.blendFunc = [GL.SRC_ALPHA, GL.DST_ALPHA];
|
|
@@ -127,7 +102,7 @@ function mapProps(source, target, mapping) {
|
|
|
127
102
|
}
|
|
128
103
|
}
|
|
129
104
|
|
|
130
|
-
function createStyleProps(config:
|
|
105
|
+
function createStyleProps(config: MapLayerConfig, mapping) {
|
|
131
106
|
const result: Record<string, any> = {};
|
|
132
107
|
mapProps(config, result, mapping);
|
|
133
108
|
|
|
@@ -153,9 +128,10 @@ function createStyleProps(config: MapTextSubLayerConfig, mapping) {
|
|
|
153
128
|
|
|
154
129
|
/* eslint-disable complexity, max-statements */
|
|
155
130
|
function createChannelProps(
|
|
156
|
-
|
|
131
|
+
id: string,
|
|
157
132
|
type: string,
|
|
158
|
-
config:
|
|
133
|
+
config: MapLayerConfig,
|
|
134
|
+
visualChannels: VisualChannels,
|
|
159
135
|
data
|
|
160
136
|
) {
|
|
161
137
|
const {
|
|
@@ -175,7 +151,6 @@ function createChannelProps(
|
|
|
175
151
|
}
|
|
176
152
|
const {textLabel, visConfig} = config;
|
|
177
153
|
const result: Record<string, any> = {};
|
|
178
|
-
const textLabelField = textLabel && textLabel.field;
|
|
179
154
|
|
|
180
155
|
if (type === 'grid' || type === 'hexagon') {
|
|
181
156
|
result.colorScaleType = colorScale;
|
|
@@ -253,12 +228,7 @@ function createChannelProps(
|
|
|
253
228
|
);
|
|
254
229
|
}
|
|
255
230
|
|
|
256
|
-
if (
|
|
257
|
-
result.getText = getTextAccessor(textLabelField, data);
|
|
258
|
-
result.pointType = 'text';
|
|
259
|
-
const radius = result.getPointRadius || visConfig.radius;
|
|
260
|
-
result.getTextPixelOffset = getTextPixelOffsetAccessor(textLabel, radius);
|
|
261
|
-
} else if (visConfig.customMarkers) {
|
|
231
|
+
if (visConfig.customMarkers) {
|
|
262
232
|
const maxIconSize = getMaxMarkerSize(visConfig, visualChannels);
|
|
263
233
|
const {getPointRadius, getFillColor} = result;
|
|
264
234
|
const {customMarkersUrl, customMarkersRange, filled: useMaskedIcons} = visConfig;
|
|
@@ -298,6 +268,59 @@ function createChannelProps(
|
|
|
298
268
|
getSizeAccessor(visualChannels.rotationField, undefined, null, undefined, data)
|
|
299
269
|
);
|
|
300
270
|
}
|
|
271
|
+
} else if (type === 'point' || type === 'tileset') {
|
|
272
|
+
result.pointType = 'circle';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (textLabel && textLabel.length && textLabel[0].field) {
|
|
276
|
+
const [mainLabel, secondaryLabel] = textLabel;
|
|
277
|
+
const collisionGroup = id;
|
|
278
|
+
|
|
279
|
+
({
|
|
280
|
+
alignment: result.getTextAlignmentBaseline,
|
|
281
|
+
anchor: result.getTextAnchor,
|
|
282
|
+
color: result.getTextColor,
|
|
283
|
+
outlineColor: result.textOutlineColor,
|
|
284
|
+
size: result.textSizeScale
|
|
285
|
+
} = mainLabel);
|
|
286
|
+
const {
|
|
287
|
+
color: getSecondaryColor,
|
|
288
|
+
field: secondaryField,
|
|
289
|
+
outlineColor: secondaryOutlineColor,
|
|
290
|
+
size: secondarySizeScale
|
|
291
|
+
} = secondaryLabel || {};
|
|
292
|
+
|
|
293
|
+
result.getText = mainLabel.field && getTextAccessor(mainLabel.field, data);
|
|
294
|
+
const getSecondaryText = secondaryField && getTextAccessor(secondaryField, data);
|
|
295
|
+
|
|
296
|
+
result.pointType = `${result.pointType}+text`;
|
|
297
|
+
result.textCharacterSet = 'auto';
|
|
298
|
+
result.textFontFamily = 'Inter, sans';
|
|
299
|
+
result.textFontSettings = {sdf: true};
|
|
300
|
+
result.textFontWeight = 600;
|
|
301
|
+
result.textOutlineWidth = 3;
|
|
302
|
+
|
|
303
|
+
result._subLayerProps = {
|
|
304
|
+
...result._subLayerProps,
|
|
305
|
+
'points-text': {
|
|
306
|
+
type: PointLabelLayer,
|
|
307
|
+
extensions: [collisionFilterExtension],
|
|
308
|
+
collisionEnabled: true,
|
|
309
|
+
collisionGroup,
|
|
310
|
+
|
|
311
|
+
// getPointRadius already has radiusScale baked in, so only pass one or the other
|
|
312
|
+
...(result.getPointRadius
|
|
313
|
+
? {getRadius: result.getPointRadius}
|
|
314
|
+
: {radiusScale: visConfig.radius}),
|
|
315
|
+
|
|
316
|
+
...(secondaryField && {
|
|
317
|
+
getSecondaryText,
|
|
318
|
+
getSecondaryColor,
|
|
319
|
+
secondarySizeScale,
|
|
320
|
+
secondaryOutlineColor
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
};
|
|
301
324
|
}
|
|
302
325
|
|
|
303
326
|
return result;
|
package/src/api/types.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export {getDefaultCredentials, setDefaultCredentials} from './config';
|
|
|
2
2
|
export {default as CartoLayer} from './layers/carto-layer';
|
|
3
3
|
export {default as _CartoTileLayer} from './layers/carto-tile-layer';
|
|
4
4
|
export {default as _H3TileLayer} from './layers/h3-tile-layer';
|
|
5
|
+
export {default as _PointLabelLayer} from './layers/point-label-layer';
|
|
5
6
|
export {default as _QuadbinTileLayer} from './layers/quadbin-tile-layer';
|
|
6
7
|
export {default as BASEMAP} from './basemap';
|
|
7
8
|
export {default as colorBins} from './style/color-bins-style';
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Accessor,
|
|
3
|
+
Color,
|
|
4
|
+
CompositeLayer,
|
|
5
|
+
CompositeLayerProps,
|
|
6
|
+
DefaultProps,
|
|
7
|
+
Layer,
|
|
8
|
+
LayersList
|
|
9
|
+
} from '@deck.gl/core';
|
|
10
|
+
import {
|
|
11
|
+
TextLayer,
|
|
12
|
+
TextLayerProps,
|
|
13
|
+
_TextBackgroundLayer as TextBackgroundLayer
|
|
14
|
+
} from '@deck.gl/layers';
|
|
15
|
+
|
|
16
|
+
const [LEFT, TOP, RIGHT, BOTTOM] = [0, 1, 2, 3];
|
|
17
|
+
|
|
18
|
+
class EnhancedTextBackgroundLayer extends TextBackgroundLayer {
|
|
19
|
+
static layerName = 'EnhancedTextBackgroundLayer';
|
|
20
|
+
|
|
21
|
+
getShaders() {
|
|
22
|
+
const shaders = super.getShaders();
|
|
23
|
+
let vs = shaders.vs;
|
|
24
|
+
|
|
25
|
+
// Modify shader so that the padding is offset by the pixel offset to ensure the padding
|
|
26
|
+
// always captures the anchor point. As padding is uniform we cannot pass it a per-label value
|
|
27
|
+
vs = vs.replaceAll('padding.', '_padding.');
|
|
28
|
+
vs = vs.replace(
|
|
29
|
+
'void main(void) {',
|
|
30
|
+
'void main(void) {\n vec4 _padding = padding + instancePixelOffsets.xyxy * vec4(1.0, 1.0, -1.0, -1.0);'
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return {...shaders, vs};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// TextLayer which includes modified text-background-layer-vertex shader and only renders the
|
|
38
|
+
// primary background layer in the collision pass
|
|
39
|
+
class EnhancedTextLayer extends TextLayer {
|
|
40
|
+
static layerName = 'EnhancedTextLayer';
|
|
41
|
+
|
|
42
|
+
filterSubLayer({layer, renderPass}) {
|
|
43
|
+
const background = layer.id.includes('primary-background');
|
|
44
|
+
if (renderPass === 'collision') {
|
|
45
|
+
return background; // Only draw primary background layer in collision pass
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return !background; // Do not draw background layer in other passes
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const defaultProps: DefaultProps<PointLabelLayerProps> = {
|
|
53
|
+
...TextLayer.defaultProps,
|
|
54
|
+
getRadius: {type: 'accessor', value: 1},
|
|
55
|
+
radiusScale: {type: 'number', min: 0, value: 1}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** All properties supported by PointLabelLayer. */
|
|
59
|
+
export type PointLabelLayerProps<DataT = any> = _PointLabelLayerProps<DataT> &
|
|
60
|
+
TextLayerProps &
|
|
61
|
+
CompositeLayerProps;
|
|
62
|
+
|
|
63
|
+
/** Properties added by PointLabelLayer. */
|
|
64
|
+
type _PointLabelLayerProps<DataT> = TextLayerProps<DataT> & {
|
|
65
|
+
/**
|
|
66
|
+
* Radius multiplier.
|
|
67
|
+
* @default 1
|
|
68
|
+
*/
|
|
69
|
+
radiusScale?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Radius accessor.
|
|
72
|
+
* @default 1
|
|
73
|
+
*/
|
|
74
|
+
getRadius?: Accessor<DataT, number>;
|
|
75
|
+
/**
|
|
76
|
+
* Secondary label text accessor
|
|
77
|
+
*/
|
|
78
|
+
getSecondaryText?: Accessor<DataT, string>;
|
|
79
|
+
/**
|
|
80
|
+
* Secondary label color accessor
|
|
81
|
+
* @default [0, 0, 0, 255]
|
|
82
|
+
*/
|
|
83
|
+
getSecondaryColor?: Accessor<DataT, Color>;
|
|
84
|
+
/**
|
|
85
|
+
* Secondary label color of outline around the text, in `[r, g, b, [a]]`. Each channel is a number between 0-255 and `a` is 255 if not supplied.
|
|
86
|
+
* @default [0, 0, 0, 255]
|
|
87
|
+
*/
|
|
88
|
+
secondaryOutlineColor?: Color;
|
|
89
|
+
/**
|
|
90
|
+
* Secondary label text size multiplier.
|
|
91
|
+
* @default 1
|
|
92
|
+
*/
|
|
93
|
+
secondarySizeScale?: number;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export default class PointLabelLayer<
|
|
97
|
+
DataT = any,
|
|
98
|
+
ExtraProps extends {} = {}
|
|
99
|
+
> extends CompositeLayer<ExtraProps & Required<_PointLabelLayerProps<DataT>>> {
|
|
100
|
+
static layerName = 'PointLabelLayer';
|
|
101
|
+
static defaultProps = defaultProps;
|
|
102
|
+
|
|
103
|
+
calculatePixelOffset(secondary) {
|
|
104
|
+
const {
|
|
105
|
+
getTextAnchor: anchor,
|
|
106
|
+
getAlignmentBaseline: alignment,
|
|
107
|
+
getRadius,
|
|
108
|
+
getSecondaryText,
|
|
109
|
+
radiusScale,
|
|
110
|
+
secondarySizeScale,
|
|
111
|
+
sizeScale
|
|
112
|
+
} = this.props;
|
|
113
|
+
const xMult = anchor === 'middle' ? 0 : anchor === 'start' ? 1 : -1;
|
|
114
|
+
const yMult = alignment === 'center' ? 0 : alignment === 'bottom' ? 1 : -1;
|
|
115
|
+
|
|
116
|
+
// Padding based on font size (font size / 4)
|
|
117
|
+
const xPadding = sizeScale / 4;
|
|
118
|
+
const yPadding = sizeScale * (1 + 1 / 4);
|
|
119
|
+
|
|
120
|
+
// Place secondary label under main label (secondary label always 'top' baseline aligned)
|
|
121
|
+
const secondaryOffset = 0.6 * (1 - yMult) * sizeScale;
|
|
122
|
+
let yOffset = secondary ? secondaryOffset : 0;
|
|
123
|
+
|
|
124
|
+
// Special case, position relative to secondary label
|
|
125
|
+
if (anchor === 'middle' && alignment === 'top' && getSecondaryText) {
|
|
126
|
+
yOffset -= secondaryOffset;
|
|
127
|
+
yOffset -= secondarySizeScale;
|
|
128
|
+
yOffset += sizeScale;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Padding based on point radius (radius/ 4)
|
|
132
|
+
const radiusPadding = 1 + 1 / 4;
|
|
133
|
+
|
|
134
|
+
return typeof getRadius === 'function'
|
|
135
|
+
? (d, info) => {
|
|
136
|
+
const r = (info ? getRadius(d, info) : 1) * radiusScale * radiusPadding;
|
|
137
|
+
return [xMult * (r + xPadding), yMult * (r + yPadding) + yOffset];
|
|
138
|
+
}
|
|
139
|
+
: [
|
|
140
|
+
xMult * (getRadius * radiusScale * radiusPadding + xPadding),
|
|
141
|
+
yMult * (getRadius * radiusScale * radiusPadding + yPadding) + yOffset
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
calculateBackgroundPadding() {
|
|
146
|
+
const {getTextAnchor: anchor, getAlignmentBaseline: alignment, sizeScale} = this.props;
|
|
147
|
+
|
|
148
|
+
// Heuristics to avoid label overlap
|
|
149
|
+
const paddingX = 12 * sizeScale;
|
|
150
|
+
const paddingY = 3 * sizeScale;
|
|
151
|
+
const backgroundPadding = [0, 0, 0, 0];
|
|
152
|
+
if (alignment === 'top') {
|
|
153
|
+
backgroundPadding[TOP] = paddingY;
|
|
154
|
+
} else if (alignment === 'bottom') {
|
|
155
|
+
backgroundPadding[BOTTOM] = paddingY;
|
|
156
|
+
} else {
|
|
157
|
+
backgroundPadding[TOP] = 0.5 * paddingY;
|
|
158
|
+
backgroundPadding[BOTTOM] = 0.5 * paddingY;
|
|
159
|
+
}
|
|
160
|
+
if (anchor === 'start') {
|
|
161
|
+
backgroundPadding[LEFT] = paddingX;
|
|
162
|
+
} else if (anchor === 'end') {
|
|
163
|
+
backgroundPadding[RIGHT] = paddingX;
|
|
164
|
+
} else {
|
|
165
|
+
backgroundPadding[LEFT] = 0.5 * paddingX;
|
|
166
|
+
backgroundPadding[RIGHT] = 0.5 * paddingX;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return backgroundPadding;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
renderTextLayer(id, {updateTriggers: updateTriggersOverride = {}, ...props}): EnhancedTextLayer {
|
|
173
|
+
const {
|
|
174
|
+
data,
|
|
175
|
+
|
|
176
|
+
characterSet,
|
|
177
|
+
fontFamily,
|
|
178
|
+
fontSettings,
|
|
179
|
+
fontWeight,
|
|
180
|
+
outlineColor,
|
|
181
|
+
outlineWidth,
|
|
182
|
+
sizeScale,
|
|
183
|
+
radiusScale,
|
|
184
|
+
|
|
185
|
+
getAlignmentBaseline,
|
|
186
|
+
getColor,
|
|
187
|
+
getPosition,
|
|
188
|
+
getTextAnchor,
|
|
189
|
+
|
|
190
|
+
updateTriggers
|
|
191
|
+
} = this.props;
|
|
192
|
+
|
|
193
|
+
return new EnhancedTextLayer(
|
|
194
|
+
this.getSubLayerProps({
|
|
195
|
+
id,
|
|
196
|
+
data,
|
|
197
|
+
|
|
198
|
+
characterSet,
|
|
199
|
+
fontFamily,
|
|
200
|
+
fontSettings,
|
|
201
|
+
fontWeight,
|
|
202
|
+
outlineColor,
|
|
203
|
+
outlineWidth,
|
|
204
|
+
sizeScale,
|
|
205
|
+
|
|
206
|
+
getAlignmentBaseline,
|
|
207
|
+
getColor,
|
|
208
|
+
getPosition,
|
|
209
|
+
getTextAnchor,
|
|
210
|
+
|
|
211
|
+
updateTriggers: {
|
|
212
|
+
...updateTriggers,
|
|
213
|
+
...updateTriggersOverride,
|
|
214
|
+
getPixelOffset: [
|
|
215
|
+
updateTriggers.getRadius,
|
|
216
|
+
updateTriggers.getTextAnchor,
|
|
217
|
+
updateTriggers.getAlignmentBaseline,
|
|
218
|
+
radiusScale,
|
|
219
|
+
sizeScale
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
}),
|
|
223
|
+
{
|
|
224
|
+
getSize: 1,
|
|
225
|
+
_subLayerProps: {background: {type: EnhancedTextBackgroundLayer}}
|
|
226
|
+
},
|
|
227
|
+
props
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
renderLayers(): Layer | null | LayersList {
|
|
232
|
+
const {
|
|
233
|
+
getText,
|
|
234
|
+
getSecondaryColor,
|
|
235
|
+
getSecondaryText,
|
|
236
|
+
secondaryOutlineColor,
|
|
237
|
+
secondarySizeScale,
|
|
238
|
+
updateTriggers
|
|
239
|
+
} = this.props;
|
|
240
|
+
const getPixelOffset = this.calculatePixelOffset(false);
|
|
241
|
+
const backgroundPadding = this.calculateBackgroundPadding();
|
|
242
|
+
const out = [
|
|
243
|
+
// Text doesn't update via updateTrigger for some reason
|
|
244
|
+
this.renderTextLayer(`${updateTriggers.getText}-primary`, {
|
|
245
|
+
backgroundPadding,
|
|
246
|
+
getText,
|
|
247
|
+
getPixelOffset,
|
|
248
|
+
background: true // Only use background for primary label for faster collisions
|
|
249
|
+
}),
|
|
250
|
+
Boolean(getSecondaryText) &&
|
|
251
|
+
this.renderTextLayer(`${updateTriggers.getSecondaryText}-secondary`, {
|
|
252
|
+
getText: getSecondaryText,
|
|
253
|
+
getPixelOffset: this.calculatePixelOffset(true),
|
|
254
|
+
getAlignmentBaseline: 'top',
|
|
255
|
+
// updateTriggers: {getText: updateTriggers.getSecondaryText},
|
|
256
|
+
|
|
257
|
+
// Optional overrides
|
|
258
|
+
...(getSecondaryColor && {getColor: getSecondaryColor}),
|
|
259
|
+
...(secondarySizeScale && {sizeScale: secondarySizeScale}),
|
|
260
|
+
...(secondaryOutlineColor && {outlineColor: secondaryOutlineColor})
|
|
261
|
+
})
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils';
|
|
2
2
|
|
|
3
3
|
import {Tile, TileReader} from './carto-spatial-tile';
|
|
4
|
-
import {parsePbf
|
|
4
|
+
import {parsePbf} from './tile-loader-utils';
|
|
5
5
|
import {IndexScheme, binaryToSpatialjson, SpatialJson} from './spatialjson-utils';
|
|
6
6
|
|
|
7
7
|
const CartoSpatialTileLoader: LoaderWithParser = {
|
|
@@ -31,10 +31,7 @@ function parseCartoSpatialTile(
|
|
|
31
31
|
|
|
32
32
|
const {cells} = tile;
|
|
33
33
|
const scheme = options?.cartoSpatialTile?.scheme;
|
|
34
|
-
const data = {
|
|
35
|
-
cells: {...cells, properties: unpackProperties(cells.properties)},
|
|
36
|
-
scheme
|
|
37
|
-
};
|
|
34
|
+
const data = {cells, scheme};
|
|
38
35
|
|
|
39
36
|
return binaryToSpatialjson(data);
|
|
40
37
|
}
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
+
import {readPackedFixed64} from './fast-pbf';
|
|
1
2
|
import {Indices, IndexScheme} from './spatialjson-utils';
|
|
2
|
-
import {
|
|
3
|
-
KeyValueProperties,
|
|
4
|
-
NumericProp,
|
|
5
|
-
NumericPropKeyValueReader,
|
|
6
|
-
PropertiesReader
|
|
7
|
-
} from './carto-tile';
|
|
3
|
+
import {NumericProp, NumericPropKeyValueReader, PropertiesReader} from './carto-tile';
|
|
8
4
|
|
|
9
5
|
// Indices =====================================
|
|
10
6
|
|
|
11
7
|
export class IndicesReader {
|
|
12
8
|
static read(pbf, end?: number): Indices {
|
|
13
|
-
|
|
14
|
-
return {value: new BigUint64Array(value)};
|
|
9
|
+
return pbf.readFields(IndicesReader._readField, {value: []}, end);
|
|
15
10
|
}
|
|
16
11
|
static _readField(this: void, tag: number, obj, pbf) {
|
|
17
|
-
if (tag === 1) readPackedFixed64(pbf, obj
|
|
12
|
+
if (tag === 1) readPackedFixed64(pbf, obj);
|
|
18
13
|
}
|
|
19
14
|
}
|
|
20
15
|
|
|
@@ -22,7 +17,7 @@ export class IndicesReader {
|
|
|
22
17
|
|
|
23
18
|
interface Cells {
|
|
24
19
|
indices: Indices;
|
|
25
|
-
properties:
|
|
20
|
+
properties: Record<string, string>[];
|
|
26
21
|
numericProps: Record<string, NumericProp>;
|
|
27
22
|
}
|
|
28
23
|
|
|
@@ -61,23 +56,3 @@ export class TileReader {
|
|
|
61
56
|
else if (tag === 2) obj.cells = CellsReader.read(pbf, pbf.readVarint() + pbf.pos);
|
|
62
57
|
}
|
|
63
58
|
}
|
|
64
|
-
|
|
65
|
-
// pbf doesn't support BigInt natively, implement support for packed fixed64 type
|
|
66
|
-
const SHIFT_LEFT_32 = BigInt((1 << 16) * (1 << 16));
|
|
67
|
-
|
|
68
|
-
function readPackedEnd(pbf) {
|
|
69
|
-
return pbf.type === 2 ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
|
|
70
|
-
}
|
|
71
|
-
function readFixed64(pbf) {
|
|
72
|
-
const a = BigInt(pbf.readFixed32());
|
|
73
|
-
const b = BigInt(pbf.readFixed32());
|
|
74
|
-
return a + b * SHIFT_LEFT_32;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function readPackedFixed64(pbf, arr) {
|
|
78
|
-
if (pbf.type !== 2) return arr.push(readFixed64(pbf));
|
|
79
|
-
const end = readPackedEnd(pbf);
|
|
80
|
-
arr = arr || [];
|
|
81
|
-
while (pbf.pos < end) arr.push(readFixed64(pbf));
|
|
82
|
-
return arr;
|
|
83
|
-
}
|