@deck.gl/carto 9.0.23 → 9.0.25

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.
Files changed (72) hide show
  1. package/dist/api/layer-map.d.ts +4 -6
  2. package/dist/api/layer-map.d.ts.map +1 -1
  3. package/dist/api/layer-map.js +16 -17
  4. package/dist/cartoRasterTile-worker.js +1 -1
  5. package/dist/cartoSpatialTile-worker.js +1 -1
  6. package/dist/cartoVectorTile-worker.js +1 -1
  7. package/dist/dist.dev.js +1631 -1195
  8. package/dist/index.cjs +798 -512
  9. package/dist/index.cjs.map +4 -4
  10. package/dist/index.d.ts +3 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -1
  13. package/dist/layers/cluster-tile-layer.d.ts +41 -0
  14. package/dist/layers/cluster-tile-layer.d.ts.map +1 -0
  15. package/dist/layers/cluster-tile-layer.js +116 -0
  16. package/dist/layers/cluster-utils.d.ts +30 -0
  17. package/dist/layers/cluster-utils.d.ts.map +1 -0
  18. package/dist/layers/cluster-utils.js +122 -0
  19. package/dist/layers/raster-layer.d.ts +21 -1
  20. package/dist/layers/raster-layer.d.ts.map +1 -1
  21. package/dist/layers/raster-layer.js +31 -1
  22. package/dist/layers/raster-tile-layer.d.ts +2 -2
  23. package/dist/layers/raster-tile-layer.d.ts.map +1 -1
  24. package/dist/layers/raster-tile-layer.js +2 -2
  25. package/dist/layers/schema/carto-raster-tile-loader.js +1 -1
  26. package/dist/layers/schema/carto-spatial-tile-loader.js +1 -1
  27. package/dist/layers/schema/carto-vector-tile-loader.js +1 -1
  28. package/dist/layers/spatial-index-tile-layer.d.ts +2 -5
  29. package/dist/layers/spatial-index-tile-layer.d.ts.map +1 -1
  30. package/dist/layers/vector-tile-layer.d.ts.map +1 -1
  31. package/dist/layers/vector-tile-layer.js +10 -2
  32. package/dist/sources/h3-query-source.d.ts +2 -2
  33. package/dist/sources/h3-query-source.d.ts.map +1 -1
  34. package/dist/sources/h3-query-source.js +4 -1
  35. package/dist/sources/h3-table-source.d.ts +2 -2
  36. package/dist/sources/h3-table-source.d.ts.map +1 -1
  37. package/dist/sources/h3-table-source.js +4 -1
  38. package/dist/sources/quadbin-query-source.d.ts +2 -2
  39. package/dist/sources/quadbin-query-source.d.ts.map +1 -1
  40. package/dist/sources/quadbin-query-source.js +4 -1
  41. package/dist/sources/quadbin-table-source.d.ts +2 -2
  42. package/dist/sources/quadbin-table-source.d.ts.map +1 -1
  43. package/dist/sources/quadbin-table-source.js +4 -1
  44. package/dist/style/color-bins-style.d.ts +1 -2
  45. package/dist/style/color-bins-style.d.ts.map +1 -1
  46. package/dist/style/color-categories-style.d.ts +1 -2
  47. package/dist/style/color-categories-style.d.ts.map +1 -1
  48. package/dist/style/color-continuous-style.d.ts +1 -2
  49. package/dist/style/color-continuous-style.d.ts.map +1 -1
  50. package/dist/style/palette.d.ts +1 -1
  51. package/dist/style/palette.d.ts.map +1 -1
  52. package/dist/utils.d.ts.map +1 -1
  53. package/dist/utils.js +6 -0
  54. package/dist.min.js +10 -10
  55. package/package.json +2 -2
  56. package/src/api/layer-map.ts +30 -29
  57. package/src/index.ts +3 -0
  58. package/src/layers/cluster-tile-layer.ts +218 -0
  59. package/src/layers/cluster-utils.ts +160 -0
  60. package/src/layers/raster-layer.ts +45 -2
  61. package/src/layers/raster-tile-layer.ts +3 -3
  62. package/src/layers/spatial-index-tile-layer.ts +2 -2
  63. package/src/layers/vector-tile-layer.ts +11 -2
  64. package/src/sources/h3-query-source.ts +11 -2
  65. package/src/sources/h3-table-source.ts +11 -2
  66. package/src/sources/quadbin-query-source.ts +11 -2
  67. package/src/sources/quadbin-table-source.ts +11 -2
  68. package/src/style/color-bins-style.ts +2 -2
  69. package/src/style/color-categories-style.ts +2 -2
  70. package/src/style/color-continuous-style.ts +2 -2
  71. package/src/style/palette.ts +1 -2
  72. package/src/utils.ts +8 -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.23",
6
+ "version": "9.0.25",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -72,5 +72,5 @@
72
72
  "@deck.gl/layers": "^9.0.0",
73
73
  "@loaders.gl/core": "^4.2.0"
74
74
  },
75
- "gitHead": "3574ce735fa6e90e6207bef8edbccac27949d3c1"
75
+ "gitHead": "7f2f016c464ba82472e732a213076a7248cb2c05"
76
76
  }
@@ -18,11 +18,11 @@ import {CPUGridLayer, HeatmapLayer, HexagonLayer} from '@deck.gl/aggregation-lay
18
18
  import {GeoJsonLayer} from '@deck.gl/layers';
19
19
  import {H3HexagonLayer} from '@deck.gl/geo-layers';
20
20
 
21
+ import ClusterTileLayer from '../layers/cluster-tile-layer';
21
22
  import H3TileLayer from '../layers/h3-tile-layer';
22
23
  import QuadbinTileLayer from '../layers/quadbin-tile-layer';
23
24
  import RasterTileLayer from '../layers/raster-tile-layer';
24
25
  import VectorTileLayer from '../layers/vector-tile-layer';
25
- import {MapType} from './types';
26
26
  import {assert, createBinaryProxy, scaleIdentity} from '../utils';
27
27
  import {
28
28
  CustomMarkersRange,
@@ -32,6 +32,7 @@ import {
32
32
  VisualChannelField,
33
33
  VisualChannels
34
34
  } from './types';
35
+ import HeatmapTileLayer from '../layers/heatmap-tile-layer';
35
36
 
36
37
  const SCALE_FUNCS = {
37
38
  linear: scaleLinear,
@@ -46,6 +47,17 @@ const SCALE_FUNCS = {
46
47
  };
47
48
  export type SCALE_TYPE = keyof typeof SCALE_FUNCS;
48
49
 
50
+ type TileLayerType =
51
+ | 'clusterTile'
52
+ | 'h3'
53
+ | 'heatmapTile'
54
+ | 'mvt'
55
+ | 'quadbin'
56
+ | 'raster'
57
+ | 'tileset';
58
+ type DocumentLayerType = 'geojson' | 'grid' | 'heatmap' | 'hexagon' | 'hexagonId' | 'point';
59
+ type LayerType = TileLayerType | DocumentLayerType;
60
+
49
61
  function identity<T>(v: T): T {
50
62
  return v;
51
63
  }
@@ -74,6 +86,16 @@ const AGGREGATION_FUNC = {
74
86
  variance
75
87
  };
76
88
 
89
+ const TILE_LAYER_TYPE_TO_LAYER: Record<TileLayerType, ConstructorOf<Layer>> = {
90
+ clusterTile: ClusterTileLayer,
91
+ h3: H3TileLayer,
92
+ heatmapTile: HeatmapTileLayer,
93
+ mvt: VectorTileLayer,
94
+ quadbin: QuadbinTileLayer,
95
+ raster: RasterTileLayer,
96
+ tileset: VectorTileLayer
97
+ };
98
+
77
99
  const hexToRGBA = c => {
78
100
  const {r, g, b, opacity} = rgb(c);
79
101
  return [r, g, b, 255 * opacity];
@@ -135,7 +157,7 @@ function mergePropMaps(a: Record<string, any> = {}, b: Record<string, any> = {})
135
157
  }
136
158
 
137
159
  export function getLayer(
138
- type: string,
160
+ type: LayerType,
139
161
  config: MapTextSubLayerConfig,
140
162
  dataset: MapDataset
141
163
  ): {Layer: ConstructorOf<Layer>; propMap: any; defaultProps: any} {
@@ -144,8 +166,8 @@ export function getLayer(
144
166
  if (config.visConfig?.customMarkers) {
145
167
  basePropMap = mergePropMaps(sharedPropMap, customMarkersPropsMap);
146
168
  }
147
- if (type === 'mvt' || type === 'tileset' || type === 'h3' || type === 'quadbin') {
148
- return getTileLayer(dataset, basePropMap);
169
+ if (TILE_LAYER_TYPE_TO_LAYER[type]) {
170
+ return getTileLayer(dataset, basePropMap, type);
149
171
  }
150
172
 
151
173
  const geoColumn = dataset?.geoColumn;
@@ -154,7 +176,7 @@ export function getLayer(
154
176
  const hexagonId = config.columns?.hex_id;
155
177
 
156
178
  const layerTypeDefs: Record<
157
- string,
179
+ DocumentLayerType,
158
180
  {Layer: ConstructorOf<Layer>; propMap?: any; defaultProps?: any}
159
181
  > = {
160
182
  point: {
@@ -201,32 +223,11 @@ export function getLayer(
201
223
  };
202
224
  }
203
225
 
204
- export function layerFromTileDataset(
205
- scheme: string,
206
- type?: MapType
207
- ): typeof VectorTileLayer | typeof H3TileLayer | typeof QuadbinTileLayer {
208
- if (type === 'raster') {
209
- return RasterTileLayer;
210
- }
211
- if (scheme === 'h3') {
212
- return H3TileLayer;
213
- }
214
- if (scheme === 'quadbin') {
215
- return QuadbinTileLayer;
216
- }
217
-
218
- return VectorTileLayer;
219
- }
220
-
221
- function getTileLayer(dataset: MapDataset, basePropMap) {
222
- const {
223
- aggregationExp,
224
- aggregationResLevel,
225
- data: {scheme}
226
- } = dataset;
226
+ function getTileLayer(dataset: MapDataset, basePropMap, type: LayerType) {
227
+ const {aggregationExp, aggregationResLevel} = dataset;
227
228
 
228
229
  return {
229
- Layer: layerFromTileDataset(scheme),
230
+ Layer: TILE_LAYER_TYPE_TO_LAYER[type] || VectorTileLayer,
230
231
  propMap: basePropMap,
231
232
  defaultProps: {
232
233
  ...defaultProps,
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import {default as ClusterTileLayer} from './layers/cluster-tile-layer';
1
2
  import {default as H3TileLayer} from './layers/h3-tile-layer';
2
3
  import {default as HeatmapTileLayer} from './layers/heatmap-tile-layer';
3
4
  import {default as _PointLabelLayer} from './layers/point-label-layer';
@@ -5,6 +6,7 @@ import {default as QuadbinTileLayer} from './layers/quadbin-tile-layer';
5
6
  import {default as RasterTileLayer} from './layers/raster-tile-layer';
6
7
  import {default as VectorTileLayer} from './layers/vector-tile-layer';
7
8
  const CARTO_LAYERS = {
9
+ ClusterTileLayer,
8
10
  H3TileLayer,
9
11
  HeatmapTileLayer,
10
12
  _PointLabelLayer,
@@ -14,6 +16,7 @@ const CARTO_LAYERS = {
14
16
  };
15
17
  export {
16
18
  CARTO_LAYERS,
19
+ ClusterTileLayer,
17
20
  H3TileLayer,
18
21
  HeatmapTileLayer,
19
22
  _PointLabelLayer,
@@ -0,0 +1,218 @@
1
+ import {GeoJsonLayer, GeoJsonLayerProps} from '@deck.gl/layers';
2
+ import {
3
+ TileLayer,
4
+ _Tile2DHeader as Tile2DHeader,
5
+ TileLayerProps,
6
+ TileLayerPickingInfo
7
+ } from '@deck.gl/geo-layers';
8
+ import {registerLoaders} from '@loaders.gl/core';
9
+ import {binaryToGeojson} from '@loaders.gl/gis';
10
+ import {BinaryFeatureCollection} from '@loaders.gl/schema';
11
+ import type {Feature, Geometry} from 'geojson';
12
+
13
+ import {
14
+ Accessor,
15
+ DefaultProps,
16
+ CompositeLayer,
17
+ _deepEqual as deepEqual,
18
+ GetPickingInfoParams,
19
+ Layer,
20
+ LayersList,
21
+ PickingInfo
22
+ } from '@deck.gl/core';
23
+
24
+ import {
25
+ aggregateTile,
26
+ ClusteredFeaturePropertiesT,
27
+ clustersToBinary,
28
+ computeAggregationStats,
29
+ extractAggregationProperties,
30
+ ParsedQuadbinCell,
31
+ ParsedQuadbinTile
32
+ } from './cluster-utils';
33
+ import {DEFAULT_TILE_SIZE} from '../constants';
34
+ import QuadbinTileset2D from './quadbin-tileset-2d';
35
+ import {getQuadbinPolygon} from './quadbin-utils';
36
+ import CartoSpatialTileLoader from './schema/carto-spatial-tile-loader';
37
+ import {injectAccessToken, TilejsonPropType} from './utils';
38
+ import type {TilejsonResult} from '../sources/types';
39
+
40
+ registerLoaders([CartoSpatialTileLoader]);
41
+
42
+ const defaultProps: DefaultProps<ClusterTileLayerProps> = {
43
+ data: TilejsonPropType,
44
+ clusterLevel: {type: 'number', value: 5, min: 1},
45
+ getPosition: {
46
+ type: 'accessor',
47
+ value: ({id}) => getQuadbinPolygon(id, 0.5).slice(2, 4) as [number, number]
48
+ },
49
+ getWeight: {type: 'accessor', value: 1},
50
+ refinementStrategy: 'no-overlap',
51
+ tileSize: DEFAULT_TILE_SIZE
52
+ };
53
+
54
+ export type ClusterTileLayerPickingInfo<FeaturePropertiesT = {}> = TileLayerPickingInfo<
55
+ ParsedQuadbinTile<FeaturePropertiesT>,
56
+ PickingInfo<Feature<Geometry, FeaturePropertiesT>>
57
+ >;
58
+
59
+ /** All properties supported by ClusterTileLayer. */
60
+ export type ClusterTileLayerProps<FeaturePropertiesT = unknown> =
61
+ _ClusterTileLayerProps<FeaturePropertiesT> &
62
+ Omit<TileLayerProps<ParsedQuadbinTile<FeaturePropertiesT>>, 'data'>;
63
+
64
+ /** Properties added by ClusterTileLayer. */
65
+ type _ClusterTileLayerProps<FeaturePropertiesT> = Omit<
66
+ GeoJsonLayerProps<ClusteredFeaturePropertiesT<FeaturePropertiesT>>,
67
+ 'data'
68
+ > & {
69
+ data: null | TilejsonResult | Promise<TilejsonResult>;
70
+
71
+ /**
72
+ * The number of aggregation levels to cluster cells by. Larger values increase
73
+ * the clustering radius, with an increment of `clusterLevel` doubling the radius.
74
+ *
75
+ * @default 5
76
+ */
77
+ clusterLevel?: number;
78
+
79
+ /**
80
+ * The (average) position of points in a cell used for clustering.
81
+ * If not supplied the center of the quadbin cell is used.
82
+ *
83
+ * @default cell center
84
+ */
85
+ getPosition?: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, [number, number]>;
86
+
87
+ /**
88
+ * The weight of each cell used for clustering.
89
+ *
90
+ * @default 1
91
+ */
92
+ getWeight?: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, number>;
93
+ };
94
+
95
+ class ClusterGeoJsonLayer<
96
+ FeaturePropertiesT extends {} = {},
97
+ ExtraProps extends {} = {}
98
+ > extends TileLayer<
99
+ ParsedQuadbinTile<FeaturePropertiesT>,
100
+ ExtraProps & Required<_ClusterTileLayerProps<FeaturePropertiesT>>
101
+ > {
102
+ static layerName = 'ClusterGeoJsonLayer';
103
+ static defaultProps = defaultProps;
104
+ state!: TileLayer<FeaturePropertiesT>['state'] & {
105
+ data: BinaryFeatureCollection;
106
+ clusterIds: bigint[];
107
+ hoveredFeatureId: bigint | number | null;
108
+ highlightColor: number[];
109
+ };
110
+
111
+ renderLayers(): Layer | null | LayersList {
112
+ const visibleTiles = this.state.tileset?.tiles.filter((tile: Tile2DHeader) => {
113
+ return tile.isLoaded && tile.content && this.state.tileset!.isTileVisible(tile);
114
+ }) as Tile2DHeader<ParsedQuadbinTile<FeaturePropertiesT>>[];
115
+ if (!visibleTiles?.length) {
116
+ return null;
117
+ }
118
+ visibleTiles.sort((a, b) => b.zoom - a.zoom);
119
+
120
+ const {zoom} = this.context.viewport;
121
+ const {clusterLevel, getPosition, getWeight} = this.props;
122
+
123
+ const properties = extractAggregationProperties(visibleTiles[0]);
124
+ const data = [] as ClusteredFeaturePropertiesT<FeaturePropertiesT>[];
125
+ for (const tile of visibleTiles) {
126
+ // Calculate aggregation based on viewport zoom
127
+ const overZoom = Math.round(zoom - tile.zoom);
128
+ const aggregationLevels = Math.round(clusterLevel) - overZoom;
129
+ aggregateTile(tile, aggregationLevels, properties, getPosition, getWeight);
130
+ data.push(...tile.userData![aggregationLevels]);
131
+ }
132
+
133
+ data.sort((a, b) => Number(b.count - a.count));
134
+
135
+ const clusterIds = data?.map((tile: any) => tile.id);
136
+ const needsUpdate = !deepEqual(clusterIds, this.state.clusterIds, 1);
137
+ this.setState({clusterIds});
138
+
139
+ if (needsUpdate) {
140
+ const stats = computeAggregationStats(data, properties);
141
+ for (const d of data) {
142
+ d.stats = stats;
143
+ }
144
+ this.setState({data: clustersToBinary(data)});
145
+ }
146
+
147
+ const props = {
148
+ ...this.props,
149
+ id: 'clusters',
150
+ data: this.state.data,
151
+ dataComparator: (data?: BinaryFeatureCollection, oldData?: BinaryFeatureCollection) => {
152
+ const newIds = data?.points?.properties?.map((tile: any) => tile.id);
153
+ const oldIds = oldData?.points?.properties?.map((tile: any) => tile.id);
154
+ return deepEqual(newIds, oldIds, 1);
155
+ }
156
+ } as GeoJsonLayerProps<ClusteredFeaturePropertiesT<FeaturePropertiesT>>;
157
+
158
+ return new GeoJsonLayer(this.getSubLayerProps(props));
159
+ }
160
+
161
+ getPickingInfo(params: GetPickingInfoParams): ClusterTileLayerPickingInfo<FeaturePropertiesT> {
162
+ const info = params.info as TileLayerPickingInfo<ParsedQuadbinTile<FeaturePropertiesT>>;
163
+
164
+ if (info.index !== -1) {
165
+ const {data} = params.sourceLayer!.props;
166
+ info.object = binaryToGeojson(data as BinaryFeatureCollection, {
167
+ globalFeatureId: info.index
168
+ }) as Feature;
169
+ }
170
+
171
+ return info;
172
+ }
173
+
174
+ protected _updateAutoHighlight(info: PickingInfo): void {
175
+ for (const layer of this.getSubLayers()) {
176
+ layer.updateAutoHighlight(info);
177
+ }
178
+ }
179
+
180
+ filterSubLayer() {
181
+ return true;
182
+ }
183
+ }
184
+
185
+ // Adapter layer around ClusterLayer that converts tileJSON into TileLayer API
186
+ export default class ClusterTileLayer<
187
+ FeaturePropertiesT = any,
188
+ ExtraProps extends {} = {}
189
+ > extends CompositeLayer<ExtraProps & Required<_ClusterTileLayerProps<FeaturePropertiesT>>> {
190
+ static layerName = 'ClusterTileLayer';
191
+ static defaultProps = defaultProps;
192
+
193
+ getLoadOptions(): any {
194
+ const loadOptions = super.getLoadOptions() || {};
195
+ const tileJSON = this.props.data as TilejsonResult;
196
+ injectAccessToken(loadOptions, tileJSON.accessToken);
197
+ loadOptions.cartoSpatialTile = {...loadOptions.cartoSpatialTile, scheme: 'quadbin'};
198
+ return loadOptions;
199
+ }
200
+
201
+ renderLayers(): Layer | null | LayersList {
202
+ const tileJSON = this.props.data as TilejsonResult;
203
+ if (!tileJSON) return null;
204
+
205
+ const {tiles: data, maxresolution: maxZoom} = tileJSON;
206
+ return [
207
+ // @ts-ignore
208
+ new ClusterGeoJsonLayer(this.props, {
209
+ id: `cluster-geojson-layer-${this.props.id}`,
210
+ data,
211
+ // TODO: Tileset2D should be generic over TileIndex type
212
+ TilesetClass: QuadbinTileset2D as any,
213
+ maxZoom,
214
+ loadOptions: this.getLoadOptions()
215
+ })
216
+ ];
217
+ }
218
+ }
@@ -0,0 +1,160 @@
1
+ import {cellToParent} from 'quadbin';
2
+ import {_Tile2DHeader as Tile2DHeader} from '@deck.gl/geo-layers';
3
+ import {Accessor} from '@deck.gl/core';
4
+ import {BinaryFeatureCollection} from '@loaders.gl/schema';
5
+
6
+ export type Aggregation = 'any' | 'average' | 'count' | 'min' | 'max' | 'sum';
7
+ export type AggregationProperties<FeaturePropertiesT> = {
8
+ aggregation: Aggregation;
9
+ name: keyof FeaturePropertiesT;
10
+ }[];
11
+ export type ClusteredFeaturePropertiesT<FeaturePropertiesT> = FeaturePropertiesT & {
12
+ id: bigint;
13
+ count: number;
14
+ position: [number, number];
15
+ stats: Record<keyof FeaturePropertiesT, {min: number; max: number}>;
16
+ };
17
+ export type ParsedQuadbinCell<FeaturePropertiesT> = {id: bigint; properties: FeaturePropertiesT};
18
+ export type ParsedQuadbinTile<FeaturePropertiesT> = ParsedQuadbinCell<FeaturePropertiesT>[];
19
+
20
+ export function aggregateTile<FeaturePropertiesT>(
21
+ tile: Tile2DHeader<ParsedQuadbinTile<FeaturePropertiesT>>,
22
+ aggregationLevels: number,
23
+ properties: AggregationProperties<FeaturePropertiesT> = [],
24
+ getPosition: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, [number, number]>,
25
+ getWeight: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, number>
26
+ ): void {
27
+ if (!tile.content) return;
28
+
29
+ // Aggregate on demand and cache result
30
+ if (!tile.userData) tile.userData = {};
31
+ if (tile.userData[aggregationLevels]) return;
32
+
33
+ const out: Record<number, any> = {};
34
+ for (const cell of tile.content) {
35
+ let id = cell.id;
36
+ const position = typeof getPosition === 'function' ? getPosition(cell, {} as any) : getPosition;
37
+
38
+ // Aggregate by parent id
39
+ for (let i = 0; i < aggregationLevels - 1; i++) {
40
+ id = cellToParent(id);
41
+ }
42
+
43
+ // Unfortunately TS doesn't support Record<bigint, any>
44
+ // https://github.com/microsoft/TypeScript/issues/46395
45
+ const parentId = Number(id);
46
+ if (!(parentId in out)) {
47
+ out[parentId] = {id, count: 0, position: [0, 0]};
48
+ for (const {name, aggregation} of properties) {
49
+ if (aggregation === 'any') {
50
+ // Just pick first value for ANY
51
+ out[parentId][name] = cell.properties[name];
52
+ } else {
53
+ out[parentId][name] = 0;
54
+ }
55
+ }
56
+ }
57
+ // Layout props
58
+ const prevTotalW = out[parentId].count;
59
+ out[parentId].count += typeof getWeight === 'function' ? getWeight(cell, {} as any) : getWeight;
60
+
61
+ const totalW = out[parentId].count;
62
+ const W = totalW - prevTotalW;
63
+ out[parentId].position[0] = (prevTotalW * out[parentId].position[0] + W * position[0]) / totalW;
64
+ out[parentId].position[1] = (prevTotalW * out[parentId].position[1] + W * position[1]) / totalW;
65
+
66
+ // Re-aggregate other properties so clusters can be styled
67
+ for (const {name, aggregation} of properties) {
68
+ const prevValue = out[parentId][name];
69
+ const value = cell.properties[name] as number;
70
+ if (aggregation === 'average') {
71
+ out[parentId][name] = (prevTotalW * prevValue + W * value) / totalW;
72
+ } else if (aggregation === 'count' || aggregation === 'sum') {
73
+ out[parentId][name] = prevValue + value;
74
+ } else if (aggregation === 'max') {
75
+ out[parentId][name] = Math.max(prevValue, value);
76
+ } else if (aggregation === 'min') {
77
+ out[parentId][name] = Math.min(prevValue, value);
78
+ }
79
+ }
80
+ }
81
+
82
+ tile.userData[aggregationLevels] = Object.values(out);
83
+ }
84
+
85
+ export function extractAggregationProperties<FeaturePropertiesT extends {}>(
86
+ tile: Tile2DHeader<ParsedQuadbinTile<FeaturePropertiesT>>
87
+ ): AggregationProperties<FeaturePropertiesT> {
88
+ const properties: AggregationProperties<FeaturePropertiesT> = [];
89
+ const validAggregations: Aggregation[] = ['any', 'average', 'count', 'min', 'max', 'sum'];
90
+ for (const name of Object.keys(tile.content![0].properties)) {
91
+ let aggregation = name.split('_').pop()!.toLowerCase() as Aggregation;
92
+ if (!validAggregations.includes(aggregation)) {
93
+ aggregation = 'any';
94
+ }
95
+ properties.push({name: name as keyof FeaturePropertiesT, aggregation});
96
+ }
97
+
98
+ return properties;
99
+ }
100
+
101
+ export function computeAggregationStats<FeaturePropertiesT>(
102
+ data: ClusteredFeaturePropertiesT<FeaturePropertiesT>[],
103
+ properties: AggregationProperties<FeaturePropertiesT>
104
+ ) {
105
+ const stats = {} as Record<keyof FeaturePropertiesT, {min: number; max: number}>;
106
+ for (const {name, aggregation} of properties) {
107
+ stats[name] = {min: Infinity, max: -Infinity};
108
+ if (aggregation !== 'any') {
109
+ for (const d of data) {
110
+ stats[name].min = Math.min(stats[name].min, d[name] as number);
111
+ stats[name].max = Math.max(stats[name].max, d[name] as number);
112
+ }
113
+ }
114
+ }
115
+
116
+ return stats;
117
+ }
118
+
119
+ const EMPTY_UINT16ARRAY = new Uint16Array();
120
+ const EMPTY_BINARY_PROPS = {
121
+ positions: {value: new Float32Array(), size: 2},
122
+ properties: [],
123
+ numericProps: {},
124
+ featureIds: {value: EMPTY_UINT16ARRAY, size: 1},
125
+ globalFeatureIds: {value: EMPTY_UINT16ARRAY, size: 1}
126
+ };
127
+
128
+ export function clustersToBinary<FeaturePropertiesT>(
129
+ data: ClusteredFeaturePropertiesT<FeaturePropertiesT>[]
130
+ ): BinaryFeatureCollection {
131
+ const positions = new Float32Array(data.length * 2);
132
+ const featureIds = new Uint16Array(data.length);
133
+ for (let i = 0; i < data.length; i++) {
134
+ positions.set(data[i].position, 2 * i);
135
+ featureIds[i] = i;
136
+ }
137
+
138
+ return {
139
+ shape: 'binary-feature-collection',
140
+ points: {
141
+ type: 'Point',
142
+ positions: {value: positions, size: 2},
143
+ properties: data,
144
+ numericProps: {},
145
+ featureIds: {value: featureIds, size: 1},
146
+ globalFeatureIds: {value: featureIds, size: 1}
147
+ },
148
+ lines: {
149
+ type: 'LineString',
150
+ pathIndices: {value: EMPTY_UINT16ARRAY, size: 1},
151
+ ...EMPTY_BINARY_PROPS
152
+ },
153
+ polygons: {
154
+ type: 'Polygon',
155
+ polygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},
156
+ primitivePolygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},
157
+ ...EMPTY_BINARY_PROPS
158
+ }
159
+ };
160
+ }
@@ -4,7 +4,8 @@ import {
4
4
  CompositeLayerProps,
5
5
  Layer,
6
6
  LayersList,
7
- DefaultProps
7
+ DefaultProps,
8
+ PickingInfo
8
9
  } from '@deck.gl/core';
9
10
  import {ColumnLayer, ColumnLayerProps} from '@deck.gl/layers';
10
11
  import {quadbinToOffset} from './quadbin-utils';
@@ -83,6 +84,11 @@ export default class RasterLayer<DataT = any, ExtraProps = {}> extends Composite
83
84
  static layerName = 'RasterLayer';
84
85
  static defaultProps = defaultProps;
85
86
 
87
+ state!: {
88
+ highlightedObjectIndex: number;
89
+ highlightColor: number[];
90
+ };
91
+
86
92
  renderLayers(): Layer | null | LayersList {
87
93
  // Rendering props underlying layer
88
94
  const {
@@ -102,6 +108,7 @@ export default class RasterLayer<DataT = any, ExtraProps = {}> extends Composite
102
108
 
103
109
  // Filled Column Layer
104
110
  const CellLayer = this.getSubLayerClass('column', RasterColumnLayer);
111
+ const {highlightedObjectIndex, highlightColor} = this.state;
105
112
  return new CellLayer(
106
113
  this.props,
107
114
  this.getSubLayerProps({
@@ -118,7 +125,9 @@ export default class RasterLayer<DataT = any, ExtraProps = {}> extends Composite
118
125
  data, // Pass through data for getSubLayerAccessor()
119
126
  length: blockSize * blockSize
120
127
  },
121
- offset
128
+ offset,
129
+ highlightedObjectIndex,
130
+ highlightColor
122
131
  }
123
132
  );
124
133
  }
@@ -137,4 +146,38 @@ export default class RasterLayer<DataT = any, ExtraProps = {}> extends Composite
137
146
  return accessor({properties: proxy}, info);
138
147
  };
139
148
  }
149
+
150
+ getPickingInfo(params: any) {
151
+ const info = super.getPickingInfo(params);
152
+
153
+ if (info.index !== -1) {
154
+ info.object = this.getSubLayerAccessor((x: any) => x)(undefined, {
155
+ data: this.props,
156
+ index: info.index
157
+ });
158
+ }
159
+
160
+ return info;
161
+ }
162
+
163
+ _updateAutoHighlight(info: PickingInfo) {
164
+ const {highlightedObjectIndex} = this.state;
165
+ let newHighlightedObjectIndex: number = -1;
166
+
167
+ if (info.index !== -1) {
168
+ newHighlightedObjectIndex = info.index;
169
+ }
170
+
171
+ if (highlightedObjectIndex !== newHighlightedObjectIndex) {
172
+ let {highlightColor} = this.props;
173
+ if (typeof highlightColor === 'function') {
174
+ highlightColor = highlightColor(info);
175
+ }
176
+
177
+ this.setState({
178
+ highlightColor,
179
+ highlightedObjectIndex: newHighlightedObjectIndex
180
+ });
181
+ }
182
+ }
140
183
  }
@@ -1,10 +1,10 @@
1
1
  import {CompositeLayer, CompositeLayerProps, DefaultProps, Layer, LayersList} from '@deck.gl/core';
2
2
  import RasterLayer, {RasterLayerProps} from './raster-layer';
3
3
  import QuadbinTileset2D from './quadbin-tileset-2d';
4
- import SpatialIndexTileLayer, {SpatialIndexTileLayerProps} from './spatial-index-tile-layer';
5
4
  import type {TilejsonResult} from '../sources/types';
6
5
  import {injectAccessToken, TilejsonPropType} from './utils';
7
6
  import {DEFAULT_TILE_SIZE} from '../constants';
7
+ import {TileLayer, TileLayerProps} from '@deck.gl/geo-layers';
8
8
 
9
9
  export const renderSubLayers = props => {
10
10
  const tileIndex = props.tile?.index?.q;
@@ -23,7 +23,7 @@ export type RasterTileLayerProps<DataT = unknown> = _RasterTileLayerProps<DataT>
23
23
 
24
24
  /** Properties added by RasterTileLayer. */
25
25
  type _RasterTileLayerProps<DataT> = Omit<RasterLayerProps<DataT>, 'data'> &
26
- Omit<SpatialIndexTileLayerProps<DataT>, 'data'> & {
26
+ Omit<TileLayerProps<DataT>, 'data'> & {
27
27
  data: null | TilejsonResult | Promise<TilejsonResult>;
28
28
  };
29
29
 
@@ -48,7 +48,7 @@ export default class RasterTileLayer<
48
48
  const {tiles: data, minzoom: minZoom, maxzoom: maxZoom} = tileJSON;
49
49
  return [
50
50
  // @ts-ignore
51
- new SpatialIndexTileLayer(this.props, {
51
+ new TileLayer(this.props, {
52
52
  id: `raster-tile-layer-${this.props.id}`,
53
53
  data,
54
54
  // TODO: Tileset2D should be generic over TileIndex type
@@ -1,5 +1,5 @@
1
1
  import {registerLoaders} from '@loaders.gl/core';
2
- import {DefaultProps} from '@deck.gl/core';
2
+ import {DefaultProps, LayerProps} from '@deck.gl/core';
3
3
  import CartoRasterTileLoader from './schema/carto-raster-tile-loader';
4
4
  import CartoSpatialTileLoader from './schema/carto-spatial-tile-loader';
5
5
  registerLoaders([CartoRasterTileLoader, CartoSpatialTileLoader]);
@@ -57,7 +57,7 @@ export default class SpatialIndexTileLayer<
57
57
  }
58
58
  }
59
59
 
60
- getSubLayerPropsByTile(tile: Tile2DHeader) {
60
+ getSubLayerPropsByTile(tile: Tile2DHeader): Partial<LayerProps> | null {
61
61
  return {
62
62
  highlightedObjectIndex: this.getHighlightedObjectIndex(tile),
63
63
  highlightColor: this.state.highlightColor
@@ -134,11 +134,20 @@ export default class VectorTileLayer<
134
134
  const tileBbox = props.tile.bbox as any;
135
135
  const {west, south, east, north} = tileBbox;
136
136
 
137
+ const clipProps = {
138
+ extensions: [new ClipExtension(), ...(props.extensions || [])],
139
+ clipBounds: [west, south, east, north]
140
+ };
141
+
137
142
  const subLayerProps = {
138
143
  ...props,
139
144
  autoHighlight: false,
140
- extensions: [new ClipExtension(), ...(props.extensions || [])],
141
- clipBounds: [west, south, east, north]
145
+ // Do not perform clipping on points (#9059)
146
+ _subLayerProps: {
147
+ 'polygons-fill': clipProps,
148
+ 'polygons-stroke': clipProps,
149
+ linestrings: clipProps
150
+ }
142
151
  };
143
152
 
144
153
  const subLayer = new GeoJsonLayer(subLayerProps);