@deck.gl/carto 9.0.21 → 9.0.24

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 (39) 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 +1982 -1606
  8. package/dist/index.cjs +747 -517
  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/schema/carto-raster-tile-loader.js +1 -1
  20. package/dist/layers/schema/carto-spatial-tile-loader.js +1 -1
  21. package/dist/layers/schema/carto-vector-tile-loader.js +1 -1
  22. package/dist/style/color-bins-style.d.ts +1 -2
  23. package/dist/style/color-bins-style.d.ts.map +1 -1
  24. package/dist/style/color-categories-style.d.ts +1 -2
  25. package/dist/style/color-categories-style.d.ts.map +1 -1
  26. package/dist/style/color-continuous-style.d.ts +1 -2
  27. package/dist/style/color-continuous-style.d.ts.map +1 -1
  28. package/dist/style/palette.d.ts +1 -1
  29. package/dist/style/palette.d.ts.map +1 -1
  30. package/dist.min.js +10 -10
  31. package/package.json +2 -2
  32. package/src/api/layer-map.ts +30 -29
  33. package/src/index.ts +3 -0
  34. package/src/layers/cluster-tile-layer.ts +218 -0
  35. package/src/layers/cluster-utils.ts +160 -0
  36. package/src/style/color-bins-style.ts +2 -2
  37. package/src/style/color-categories-style.ts +2 -2
  38. package/src/style/color-continuous-style.ts +2 -2
  39. package/src/style/palette.ts +1 -2
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.21",
6
+ "version": "9.0.24",
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": "bb9a6c4887b58e210223d4c6f5472811bd096521"
75
+ "gitHead": "0949b9e539222d84d35b1b315e55710106409553"
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
+ }
@@ -1,7 +1,7 @@
1
1
  import {scaleThreshold} from 'd3-scale';
2
- import {AccessorFunction} from '@deck.gl/core';
2
+ import {AccessorFunction, Color} from '@deck.gl/core';
3
3
  import {Feature} from 'geojson';
4
- import getPalette, {Color, DEFAULT_PALETTE, NULL_COLOR} from './palette';
4
+ import getPalette, {DEFAULT_PALETTE, NULL_COLOR} from './palette';
5
5
  import {assert} from '../utils';
6
6
  import {AttributeSelector, getAttrValue} from './utils';
7
7
 
@@ -1,6 +1,6 @@
1
- import {AccessorFunction} from '@deck.gl/core';
1
+ import {AccessorFunction, Color} from '@deck.gl/core';
2
2
  import {Feature} from 'geojson';
3
- import getPalette, {Color, DEFAULT_PALETTE, NULL_COLOR, OTHERS_COLOR} from './palette';
3
+ import getPalette, {DEFAULT_PALETTE, NULL_COLOR, OTHERS_COLOR} from './palette';
4
4
  import {assert} from '../utils';
5
5
  import {AttributeSelector, getAttrValue} from './utils';
6
6
 
@@ -1,7 +1,7 @@
1
- import {AccessorFunction} from '@deck.gl/core';
1
+ import {AccessorFunction, Color} from '@deck.gl/core';
2
2
  import {scaleLinear} from 'd3-scale';
3
3
  import {Feature} from 'geojson';
4
- import getPalette, {Color, DEFAULT_PALETTE, NULL_COLOR} from './palette';
4
+ import getPalette, {DEFAULT_PALETTE, NULL_COLOR} from './palette';
5
5
  import {assert} from '../utils';
6
6
  import {AttributeSelector, getAttrValue} from './utils';
7
7
 
@@ -1,7 +1,6 @@
1
1
  import * as cartoColors from 'cartocolor';
2
2
  import {assert} from '../utils';
3
-
4
- export type Color = [r: number, b: number, g: number, a?: number];
3
+ import {Color} from '@deck.gl/core';
5
4
 
6
5
  export const DEFAULT_PALETTE = 'PurpOr';
7
6
  export const NULL_COLOR: Color = [204, 204, 204];