@deck.gl/carto 8.8.3 → 8.8.6

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 (96) hide show
  1. package/dist/dist.dev.js +1382 -1153
  2. package/dist/es5/api/index.js.map +1 -1
  3. package/dist/es5/api/layer-map.js +104 -17
  4. package/dist/es5/api/layer-map.js.map +1 -1
  5. package/dist/es5/api/maps-api-common.js.map +1 -1
  6. package/dist/es5/api/maps-v3-client.js +213 -47
  7. package/dist/es5/api/maps-v3-client.js.map +1 -1
  8. package/dist/es5/api/parseMap.js +14 -4
  9. package/dist/es5/api/parseMap.js.map +1 -1
  10. package/dist/es5/config.js +11 -2
  11. package/dist/es5/config.js.map +1 -1
  12. package/dist/es5/index.js.map +1 -1
  13. package/dist/es5/layers/carto-layer.js +10 -23
  14. package/dist/es5/layers/carto-layer.js.map +1 -1
  15. package/dist/es5/style/color-bins-style.js.map +1 -1
  16. package/dist/es5/style/color-categories-style.js.map +1 -1
  17. package/dist/es5/style/color-continuous-style.js.map +1 -1
  18. package/dist/es5/style/utils.js +3 -1
  19. package/dist/es5/style/utils.js.map +1 -1
  20. package/dist/esm/api/index.js.map +1 -1
  21. package/dist/esm/api/layer-map.js +79 -14
  22. package/dist/esm/api/layer-map.js.map +1 -1
  23. package/dist/esm/api/maps-api-common.js.map +1 -1
  24. package/dist/esm/api/maps-v3-client.js +111 -11
  25. package/dist/esm/api/maps-v3-client.js.map +1 -1
  26. package/dist/esm/api/parseMap.js +18 -4
  27. package/dist/esm/api/parseMap.js.map +1 -1
  28. package/dist/esm/config.js +10 -2
  29. package/dist/esm/config.js.map +1 -1
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/layers/carto-layer.js +7 -19
  32. package/dist/esm/layers/carto-layer.js.map +1 -1
  33. package/dist/esm/style/color-bins-style.js.map +1 -1
  34. package/dist/esm/style/color-categories-style.js.map +1 -1
  35. package/dist/esm/style/color-continuous-style.js.map +1 -1
  36. package/dist/esm/style/utils.js +3 -1
  37. package/dist/esm/style/utils.js.map +1 -1
  38. package/dist.min.js +138 -138
  39. package/package.json +9 -9
  40. package/src/api/index.ts +1 -0
  41. package/src/api/layer-map.ts +76 -13
  42. package/src/api/maps-api-common.ts +8 -0
  43. package/src/api/maps-v3-client.ts +116 -12
  44. package/src/api/parseMap.ts +6 -2
  45. package/src/config.ts +12 -2
  46. package/src/index.ts +1 -1
  47. package/src/layers/carto-layer.ts +19 -32
  48. package/src/style/color-bins-style.ts +25 -3
  49. package/src/style/color-categories-style.ts +28 -4
  50. package/src/style/color-continuous-style.ts +25 -3
  51. package/src/style/utils.ts +8 -4
  52. package/typed/api/index.d.ts +1 -0
  53. package/typed/api/index.d.ts.map +1 -1
  54. package/typed/api/layer-map.d.ts +12 -4
  55. package/typed/api/layer-map.d.ts.map +1 -1
  56. package/typed/api/maps-api-common.d.ts +4 -0
  57. package/typed/api/maps-api-common.d.ts.map +1 -1
  58. package/typed/api/maps-v3-client.d.ts +4 -3
  59. package/typed/api/maps-v3-client.d.ts.map +1 -1
  60. package/typed/config.d.ts +2 -0
  61. package/typed/config.d.ts.map +1 -1
  62. package/typed/index.d.ts +1 -0
  63. package/typed/index.d.ts.map +1 -1
  64. package/typed/layers/carto-layer.d.ts +5 -8
  65. package/typed/layers/carto-layer.d.ts.map +1 -1
  66. package/typed/style/color-bins-style.d.ts +22 -3
  67. package/typed/style/color-bins-style.d.ts.map +1 -1
  68. package/typed/style/color-categories-style.d.ts +24 -4
  69. package/typed/style/color-categories-style.d.ts.map +1 -1
  70. package/typed/style/color-continuous-style.d.ts +22 -3
  71. package/typed/style/color-continuous-style.d.ts.map +1 -1
  72. package/typed/style/utils.d.ts +3 -6
  73. package/typed/style/utils.d.ts.map +1 -1
  74. package/typed/api/index.js +0 -3
  75. package/typed/api/layer-map.js +0 -264
  76. package/typed/api/maps-api-common.js +0 -50
  77. package/typed/api/maps-client.js +0 -125
  78. package/typed/api/maps-v3-client.js +0 -304
  79. package/typed/api/parseMap.js +0 -178
  80. package/typed/basemap.js +0 -9
  81. package/typed/config.js +0 -35
  82. package/typed/index.js +0 -10
  83. package/typed/layers/carto-layer.js +0 -173
  84. package/typed/layers/carto-tile-layer.js +0 -105
  85. package/typed/layers/h3-tile-layer.js +0 -47
  86. package/typed/layers/h3-tileset-2d.js +0 -85
  87. package/typed/layers/quadkey-tile-layer.js +0 -42
  88. package/typed/layers/quadkey-tileset-2d.js +0 -36
  89. package/typed/layers/schema/carto-tile.js +0 -161
  90. package/typed/layers/spatial-index-tile-layer.js +0 -40
  91. package/typed/style/color-bins-style.js +0 -12
  92. package/typed/style/color-categories-style.js +0 -16
  93. package/typed/style/color-continuous-style.js +0 -12
  94. package/typed/style/palette.js +0 -62
  95. package/typed/style/utils.js +0 -15
  96. 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.8.3",
5
+ "version": "8.8.6",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -32,12 +32,12 @@
32
32
  "prepublishOnly": "npm run build-bundle && npm run build-bundle -- --env.dev"
33
33
  },
34
34
  "dependencies": {
35
- "@loaders.gl/gis": "^3.2.0",
36
- "@loaders.gl/loader-utils": "^3.2.0",
37
- "@loaders.gl/mvt": "^3.2.0",
38
- "@loaders.gl/tiles": "^3.2.0",
39
- "@luma.gl/constants": "^8.5.14",
40
- "@math.gl/web-mercator": "^3.6.0",
35
+ "@loaders.gl/gis": "^3.2.5",
36
+ "@loaders.gl/loader-utils": "^3.2.5",
37
+ "@loaders.gl/mvt": "^3.2.5",
38
+ "@loaders.gl/tiles": "^3.2.5",
39
+ "@luma.gl/constants": "^8.5.16",
40
+ "@math.gl/web-mercator": "^3.6.2",
41
41
  "cartocolor": "^4.0.2",
42
42
  "d3-array": "^2.8.0",
43
43
  "d3-color": "^2.0.0",
@@ -48,7 +48,7 @@
48
48
  "pbf": "^3.2.1"
49
49
  },
50
50
  "devDependencies": {
51
- "@loaders.gl/schema": "^3.2.0",
51
+ "@loaders.gl/schema": "^3.2.5",
52
52
  "@types/d3-array": "^3.0.2",
53
53
  "@types/d3-color": "^1.4.2",
54
54
  "@types/d3-scale": "^3.0.0"
@@ -61,5 +61,5 @@
61
61
  "@deck.gl/layers": "^8.0.0",
62
62
  "@loaders.gl/core": "^3.0.0"
63
63
  },
64
- "gitHead": "e31e8c1885597eed40dcc404a5b3d05265995713"
64
+ "gitHead": "a0e9fda8ed34ad91a6b25451c8df290ae025028f"
65
65
  }
package/src/api/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export {FORMATS, TILE_FORMATS, MAP_TYPES, API_VERSIONS} from './maps-api-common';
2
+ export type {QueryParameters} from './maps-api-common';
2
3
  export {getDataV2, CONNECTIONS} from './maps-client';
3
4
  export {fetchLayerData, fetchMap, mapInstantiation} from './maps-v3-client';
@@ -19,6 +19,8 @@ import {GeoJsonLayer} from '@deck.gl/layers';
19
19
  import {H3HexagonLayer, MVTLayer} from '@deck.gl/geo-layers';
20
20
 
21
21
  import CartoTileLayer from '../layers/carto-tile-layer';
22
+ import H3TileLayer from '../layers/h3-tile-layer';
23
+ import QuadbinTileLayer from '../layers/quadbin-tile-layer';
22
24
  import {TILE_FORMATS} from './maps-api-common';
23
25
  import {assert} from '../utils';
24
26
 
@@ -111,7 +113,7 @@ export function getLayer(
111
113
  config,
112
114
  dataset
113
115
  ): {Layer: ConstructorOf<Layer>; propMap: any; defaultProps: any} {
114
- if (type === 'mvt' || type === 'tileset') {
116
+ if (type === 'mvt' || type === 'tileset' || type === 'h3' || type === 'quadbin') {
115
117
  return getTileLayer(dataset);
116
118
  }
117
119
 
@@ -163,31 +165,58 @@ export function getLayer(
163
165
  };
164
166
  }
165
167
 
168
+ export function layerFromTileDataset(
169
+ formatTiles: string | null = TILE_FORMATS.MVT,
170
+ scheme: string
171
+ ): typeof CartoTileLayer | typeof H3TileLayer | typeof MVTLayer | typeof QuadbinTileLayer {
172
+ if (scheme === 'h3') {
173
+ return H3TileLayer;
174
+ }
175
+ if (scheme === 'quadbin') {
176
+ return QuadbinTileLayer;
177
+ }
178
+ if (formatTiles === TILE_FORMATS.MVT) {
179
+ return MVTLayer;
180
+ }
181
+
182
+ // formatTiles === BINARY|JSON|GEOJSON
183
+ return CartoTileLayer;
184
+ }
185
+
166
186
  function getTileLayer(dataset) {
167
187
  const {
188
+ aggregationExp,
189
+ aggregationResLevel,
168
190
  data: {
191
+ scheme,
169
192
  tiles: [tileUrl]
170
193
  }
171
194
  } = dataset;
172
195
  /* global URL */
173
- const formatTiles = new URL(tileUrl).searchParams.get('formatTiles') || TILE_FORMATS.MVT;
196
+ const formatTiles = new URL(tileUrl).searchParams.get('formatTiles');
174
197
 
175
198
  return {
176
- Layer: formatTiles === TILE_FORMATS.MVT ? MVTLayer : CartoTileLayer,
199
+ Layer: layerFromTileDataset(formatTiles, scheme),
177
200
  propMap: sharedPropMap,
178
201
  defaultProps: {
179
202
  ...defaultProps,
180
- uniqueIdProperty: 'geoid',
181
- formatTiles
203
+ ...(aggregationExp && {aggregationExp}),
204
+ ...(aggregationResLevel && {aggregationResLevel}),
205
+ formatTiles,
206
+ uniqueIdProperty: 'geoid'
182
207
  }
183
208
  };
184
209
  }
185
210
 
186
- function domainFromAttribute(attribute, scaleType: SCALE_TYPE) {
211
+ function domainFromAttribute(attribute, scaleType: SCALE_TYPE, scaleLength: number) {
187
212
  if (scaleType === 'ordinal' || scaleType === 'point') {
188
213
  return attribute.categories.map(c => c.category).filter(c => c !== undefined && c !== null);
189
214
  }
190
215
 
216
+ if (scaleType === 'quantile' && attribute.quantiles) {
217
+ return attribute.quantiles[scaleLength];
218
+ }
219
+
191
220
  let {min} = attribute;
192
221
  if (scaleType === 'log' && min === 0) {
193
222
  min = 1e-5;
@@ -211,12 +240,12 @@ function domainFromValues(values, scaleType: SCALE_TYPE) {
211
240
  return extent(values);
212
241
  }
213
242
 
214
- function calculateDomain(data, name, scaleType) {
243
+ function calculateDomain(data, name, scaleType, scaleLength?) {
215
244
  if (data.tilestats) {
216
245
  // Tileset data type
217
246
  const {attributes} = data.tilestats.layers[0];
218
247
  const attribute = attributes.find(a => a.attribute === name);
219
- return domainFromAttribute(attribute, scaleType);
248
+ return domainFromAttribute(attribute, scaleType, scaleLength);
220
249
  } else if (data.features) {
221
250
  // GeoJSON data type
222
251
  const values = data.features.map(({properties}) => properties[name]);
@@ -243,6 +272,25 @@ export function opacityToAlpha(opacity) {
243
272
  return opacity !== undefined ? Math.round(255 * Math.pow(opacity, 1 / 2.2)) : 255;
244
273
  }
245
274
 
275
+ function getAccessorKeys(name: string, aggregation: string | undefined): string[] {
276
+ let keys = [name];
277
+ if (aggregation) {
278
+ // Snowflake will capitalized the keys, need to check lower and upper case version
279
+ keys = keys.concat([aggregation, aggregation.toUpperCase()].map(a => `${name}_${a}`));
280
+ }
281
+ return keys;
282
+ }
283
+
284
+ function findAccessorKey(keys: string[], properties): string[] {
285
+ for (const key of keys) {
286
+ if (key in properties) {
287
+ return [key];
288
+ }
289
+ }
290
+
291
+ throw new Error(`Could not find property for any accessor key: ${keys}`);
292
+ }
293
+
246
294
  export function getColorValueAccessor({name}, colorAggregation, data: any) {
247
295
  const aggregator = AGGREGATION_FUNC[colorAggregation];
248
296
  const accessor = values => aggregator(values, p => p[name]);
@@ -252,7 +300,7 @@ export function getColorValueAccessor({name}, colorAggregation, data: any) {
252
300
  export function getColorAccessor(
253
301
  {name},
254
302
  scaleType: SCALE_TYPE,
255
- {colors, colorMap},
303
+ {aggregation, range: {colors, colorMap}},
256
304
  opacity: number | undefined,
257
305
  data: any
258
306
  ) {
@@ -266,7 +314,7 @@ export function getColorAccessor(
266
314
  scaleColor.push(color);
267
315
  });
268
316
  } else {
269
- domain = calculateDomain(data, name, scaleType);
317
+ domain = calculateDomain(data, name, scaleType, colors.length);
270
318
  scaleColor = colors;
271
319
  }
272
320
 
@@ -279,21 +327,36 @@ export function getColorAccessor(
279
327
  scale.unknown(UNKNOWN_COLOR);
280
328
  const alpha = opacityToAlpha(opacity);
281
329
 
330
+ let accessorKeys = getAccessorKeys(name, aggregation);
282
331
  const accessor = properties => {
283
- const propertyValue = properties[name];
332
+ if (!(accessorKeys[0] in properties)) {
333
+ accessorKeys = findAccessorKey(accessorKeys, properties);
334
+ }
335
+ const propertyValue = properties[accessorKeys[0]];
284
336
  const {r, g, b} = rgb(scale(propertyValue));
285
337
  return [r, g, b, propertyValue === null ? 0 : alpha];
286
338
  };
287
339
  return normalizeAccessor(accessor, data);
288
340
  }
289
341
 
290
- export function getSizeAccessor({name}, scaleType: SCALE_TYPE, range: Iterable<Range>, data: any) {
342
+ export function getSizeAccessor(
343
+ {name},
344
+ scaleType: SCALE_TYPE,
345
+ aggregation,
346
+ range: Iterable<Range>,
347
+ data: any
348
+ ) {
291
349
  const scale = SCALE_FUNCS[scaleType as any]();
292
350
  scale.domain(calculateDomain(data, name, scaleType));
293
351
  scale.range(range);
294
352
 
353
+ let accessorKeys = getAccessorKeys(name, aggregation);
295
354
  const accessor = properties => {
296
- return scale(properties[name]);
355
+ if (!(accessorKeys[0] in properties)) {
356
+ accessorKeys = findAccessorKey(accessorKeys, properties);
357
+ }
358
+ const propertyValue = properties[accessorKeys[0]];
359
+ return scale(propertyValue);
297
360
  };
298
361
  return normalizeAccessor(accessor, data);
299
362
  }
@@ -77,3 +77,11 @@ type MapInstantiationFormats = Record<
77
77
  export function encodeParameter(name: string, value: string | boolean | number): string {
78
78
  return `${name}=${encodeURIComponent(value)}`;
79
79
  }
80
+
81
+ export type QueryParameterValue = string | number | boolean | Array<QueryParameterValue> | object;
82
+
83
+ export type NamedQueryParameter = Record<string, QueryParameterValue>;
84
+
85
+ export type PositionalQueryParameter = QueryParameterValue[];
86
+
87
+ export type QueryParameters = NamedQueryParameter | PositionalQueryParameter;
@@ -1,7 +1,12 @@
1
1
  /**
2
2
  * Maps API Client for Carto 3
3
3
  */
4
- import {getDefaultCredentials, buildMapsUrlFromBase, CloudNativeCredentials} from '../config';
4
+ import {
5
+ getDefaultCredentials,
6
+ buildMapsUrlFromBase,
7
+ buildStatsUrlFromBase,
8
+ CloudNativeCredentials
9
+ } from '../config';
5
10
  import {
6
11
  API_VERSIONS,
7
12
  COLUMNS_SUPPORT,
@@ -12,6 +17,7 @@ import {
12
17
  MapInstantiation,
13
18
  MapType,
14
19
  MAP_TYPES,
20
+ QueryParameters,
15
21
  SchemaField,
16
22
  TileFormat,
17
23
  TILE_FORMATS
@@ -117,6 +123,7 @@ type FetchLayerDataParams = {
117
123
  formatTiles?: TileFormat;
118
124
  aggregationExp?: string;
119
125
  aggregationResLevel?: number;
126
+ queryParameters?: QueryParameters;
120
127
  };
121
128
 
122
129
  /**
@@ -129,13 +136,18 @@ function getParameters({
129
136
  columns,
130
137
  clientId,
131
138
  aggregationExp,
132
- aggregationResLevel
139
+ aggregationResLevel,
140
+ queryParameters
133
141
  }: Omit<FetchLayerDataParams, 'connection' | 'credentials'>) {
134
142
  const parameters = [encodeParameter('client', clientId || DEFAULT_CLIENT)];
135
143
 
136
144
  const sourceName = type === MAP_TYPES.QUERY ? 'q' : 'name';
137
145
  parameters.push(encodeParameter(sourceName, source));
138
146
 
147
+ if (queryParameters) {
148
+ parameters.push(encodeParameter('queryParameters', JSON.stringify(queryParameters)));
149
+ }
150
+
139
151
  if (geoColumn) {
140
152
  parameters.push(encodeParameter('geo_column', geoColumn));
141
153
  }
@@ -144,6 +156,9 @@ function getParameters({
144
156
  }
145
157
  if (aggregationExp) {
146
158
  parameters.push(encodeParameter('aggregationExp', aggregationExp));
159
+ } else if (isSpatialIndexGeoColumn(geoColumn)) {
160
+ // Default aggregationExp required for spatial index layers
161
+ parameters.push(encodeParameter('aggregationExp', '1 AS value'));
147
162
  }
148
163
  if (aggregationResLevel) {
149
164
  parameters.push(encodeParameter('aggregationResLevel', aggregationResLevel));
@@ -152,6 +167,11 @@ function getParameters({
152
167
  return parameters.join('&');
153
168
  }
154
169
 
170
+ function isSpatialIndexGeoColumn(geoColumn: string | undefined) {
171
+ const spatialIndex = geoColumn?.split(':')[0];
172
+ return spatialIndex === 'h3' || spatialIndex === 'quadbin';
173
+ }
174
+
155
175
  export async function mapInstantiation({
156
176
  type,
157
177
  source,
@@ -161,7 +181,8 @@ export async function mapInstantiation({
161
181
  columns,
162
182
  clientId,
163
183
  aggregationExp,
164
- aggregationResLevel
184
+ aggregationResLevel,
185
+ queryParameters
165
186
  }: FetchLayerDataParams): Promise<MapInstantiation> {
166
187
  const baseUrl = `${credentials.mapsUrl}/${connection}/${type}`;
167
188
  const url = `${baseUrl}?${getParameters({
@@ -170,8 +191,9 @@ export async function mapInstantiation({
170
191
  geoColumn,
171
192
  columns,
172
193
  clientId,
194
+ aggregationResLevel,
173
195
  aggregationExp,
174
- aggregationResLevel
196
+ queryParameters
175
197
  })}`;
176
198
  const {accessToken} = credentials;
177
199
 
@@ -179,7 +201,8 @@ export async function mapInstantiation({
179
201
  // need to be a POST request
180
202
  const body = JSON.stringify({
181
203
  q: source,
182
- client: clientId || DEFAULT_CLIENT
204
+ client: clientId || DEFAULT_CLIENT,
205
+ queryParameters
183
206
  });
184
207
  return await requestJson({method: 'POST', url: baseUrl, accessToken, body});
185
208
  }
@@ -255,7 +278,8 @@ export async function fetchLayerData({
255
278
  formatTiles,
256
279
  clientId,
257
280
  aggregationExp,
258
- aggregationResLevel
281
+ aggregationResLevel,
282
+ queryParameters
259
283
  }: FetchLayerDataParams): Promise<FetchLayerDataResult> {
260
284
  // Internally we split data fetching into two parts to allow us to
261
285
  // conditionally fetch the actual data, depending on the metadata state
@@ -270,7 +294,8 @@ export async function fetchLayerData({
270
294
  formatTiles,
271
295
  clientId,
272
296
  aggregationExp,
273
- aggregationResLevel
297
+ aggregationResLevel,
298
+ queryParameters
274
299
  });
275
300
 
276
301
  const data = await requestData({url, format: mapFormat, accessToken});
@@ -289,7 +314,8 @@ async function _fetchDataUrl({
289
314
  formatTiles,
290
315
  clientId,
291
316
  aggregationExp,
292
- aggregationResLevel
317
+ aggregationResLevel,
318
+ queryParameters
293
319
  }: FetchLayerDataParams) {
294
320
  const defaultCredentials = getDefaultCredentials();
295
321
  // Only pick up default credentials if they have been defined for
@@ -322,7 +348,8 @@ async function _fetchDataUrl({
322
348
  columns,
323
349
  clientId,
324
350
  aggregationExp,
325
- aggregationResLevel
351
+ aggregationResLevel,
352
+ queryParameters
326
353
  });
327
354
  let url: string | null = null;
328
355
  let mapFormat: Format | undefined;
@@ -361,11 +388,23 @@ async function _fetchMapDataset(
361
388
  dataset,
362
389
  accessToken: string,
363
390
  credentials: CloudNativeCredentials,
364
- clientId?: string
391
+ clientId?: string,
392
+ queryParameters?: QueryParameters
365
393
  ) {
366
- const {connectionName: connection, columns, format, geoColumn, source, type} = dataset;
394
+ const {
395
+ aggregationExp,
396
+ aggregationResLevel,
397
+ connectionName: connection,
398
+ columns,
399
+ format,
400
+ geoColumn,
401
+ source,
402
+ type
403
+ } = dataset;
367
404
  // First fetch metadata
368
405
  const {url, mapFormat} = await _fetchDataUrl({
406
+ aggregationExp,
407
+ aggregationResLevel,
369
408
  clientId,
370
409
  credentials: {...credentials, accessToken},
371
410
  connection,
@@ -373,7 +412,8 @@ async function _fetchMapDataset(
373
412
  format,
374
413
  geoColumn,
375
414
  source,
376
- type
415
+ type,
416
+ queryParameters
377
417
  });
378
418
 
379
419
  // Extract the last time the data changed
@@ -389,6 +429,31 @@ async function _fetchMapDataset(
389
429
  return true;
390
430
  }
391
431
 
432
+ async function _fetchTilestats(
433
+ attribute,
434
+ dataset,
435
+ accessToken: string,
436
+ credentials: CloudNativeCredentials
437
+ ) {
438
+ const {connectionName: connection, source, type} = dataset;
439
+
440
+ const statsUrl = buildStatsUrlFromBase(credentials.apiBaseUrl);
441
+ let url = `${statsUrl}/${connection}/`;
442
+ if (type === MAP_TYPES.QUERY) {
443
+ url += `${attribute}?q=${source}`;
444
+ } else {
445
+ // MAP_TYPE.TABLE
446
+ url += `${source}/${attribute}`;
447
+ }
448
+ const stats = await requestData({url, format: FORMATS.JSON, accessToken});
449
+
450
+ // Replace tilestats for attribute with value from API
451
+ const {attributes} = dataset.data.tilestats.layers[0];
452
+ const index = attributes.findIndex(d => d.attribute === attribute);
453
+ attributes[index] = stats;
454
+ return true;
455
+ }
456
+
392
457
  async function fillInMapDatasets(
393
458
  {datasets, token},
394
459
  clientId: string,
@@ -398,6 +463,42 @@ async function fillInMapDatasets(
398
463
  return await Promise.all(promises);
399
464
  }
400
465
 
466
+ async function fillInTileStats(
467
+ {datasets, keplerMapConfig, token},
468
+ credentials: CloudNativeCredentials
469
+ ) {
470
+ const attributes: {attribute?: string; dataset?: any}[] = [];
471
+ const {layers} = keplerMapConfig.config.visState;
472
+ for (const layer of layers) {
473
+ for (const channel of Object.keys(layer.visualChannels)) {
474
+ const attribute = layer.visualChannels[channel]?.name;
475
+ if (attribute) {
476
+ const dataset = datasets.find(d => d.id === layer.config.dataId);
477
+ if (dataset.data.tilestats && dataset.type !== MAP_TYPES.TILESET) {
478
+ // Only fetch stats for QUERY & TABLE map types
479
+ attributes.push({attribute, dataset});
480
+ }
481
+ }
482
+ }
483
+ }
484
+ // Remove duplicates to avoid repeated requests
485
+ const filteredAttributes: {attribute?: string; dataset?: any}[] = [];
486
+ for (const a of attributes) {
487
+ if (
488
+ !filteredAttributes.find(
489
+ ({attribute, dataset}) => attribute === a.attribute && dataset === a.dataset
490
+ )
491
+ ) {
492
+ filteredAttributes.push(a);
493
+ }
494
+ }
495
+
496
+ const promises = filteredAttributes.map(({attribute, dataset}) =>
497
+ _fetchTilestats(attribute, dataset, token, credentials)
498
+ );
499
+ return await Promise.all(promises);
500
+ }
501
+
401
502
  export async function fetchMap({
402
503
  cartoMapId,
403
504
  clientId,
@@ -466,6 +567,9 @@ export async function fetchMap({
466
567
 
467
568
  // Mutates map.datasets so that dataset.data contains data
468
569
  await fillInMapDatasets(map, clientId, localCreds);
570
+
571
+ // Mutates attributes in visualChannels to contain tile stats
572
+ await fillInTileStats(map, localCreds);
469
573
  return {
470
574
  ...parseMap(map),
471
575
  ...{stopAutoRefresh}
@@ -176,10 +176,11 @@ function createChannelProps(visualChannels, type, config, data) {
176
176
  }
177
177
  }
178
178
  } else if (colorField) {
179
+ const {colorAggregation: aggregation, colorRange: range} = visConfig;
179
180
  result.getFillColor = getColorAccessor(
180
181
  colorField,
181
182
  colorScale,
182
- visConfig.colorRange,
183
+ {aggregation, range},
183
184
  visConfig.opacity,
184
185
  data
185
186
  );
@@ -189,10 +190,11 @@ function createChannelProps(visualChannels, type, config, data) {
189
190
  const fallbackOpacity = type === 'point' ? visConfig.opacity : 1;
190
191
  const opacity =
191
192
  visConfig.strokeOpacity !== undefined ? visConfig.strokeOpacity : fallbackOpacity;
193
+ const {strokeColorAggregation: aggregation, strokeColorRange: range} = visConfig;
192
194
  result.getLineColor = getColorAccessor(
193
195
  strokeColorField,
194
196
  strokeColorScale,
195
- visConfig.strokeColorRange,
197
+ {aggregation, range},
196
198
  opacity,
197
199
  data
198
200
  );
@@ -201,6 +203,7 @@ function createChannelProps(visualChannels, type, config, data) {
201
203
  result.getElevation = getSizeAccessor(
202
204
  heightField,
203
205
  heightScale,
206
+ visConfig.heightAggregation,
204
207
  visConfig.heightRange || visConfig.sizeRange,
205
208
  data
206
209
  );
@@ -209,6 +212,7 @@ function createChannelProps(visualChannels, type, config, data) {
209
212
  result.getPointRadius = getSizeAccessor(
210
213
  sizeField,
211
214
  sizeScale,
215
+ visConfig.sizeAggregation,
212
216
  visConfig.radiusRange || visConfig.sizeRange,
213
217
  data
214
218
  );
package/src/config.ts CHANGED
@@ -50,11 +50,21 @@ export function getDefaultCredentials(): Credentials {
50
50
  return credentials;
51
51
  }
52
52
 
53
- export function buildMapsUrlFromBase(apiBaseUrl: string): string {
54
- let suffix = '/v3/maps';
53
+ export type V3Endpoint = 'maps' | 'stats';
54
+
55
+ function buildUrlFromBase(apiBaseUrl: string, endpoint: V3Endpoint) {
56
+ let suffix = `/v3/${endpoint}`;
55
57
  if (apiBaseUrl.endsWith('/')) {
56
58
  suffix = suffix.substring(1);
57
59
  }
58
60
 
59
61
  return `${apiBaseUrl}${suffix}`;
60
62
  }
63
+
64
+ export function buildMapsUrlFromBase(apiBaseUrl: string): string {
65
+ return buildUrlFromBase(apiBaseUrl, 'maps');
66
+ }
67
+
68
+ export function buildStatsUrlFromBase(apiBaseUrl: string): string {
69
+ return buildUrlFromBase(apiBaseUrl, 'stats');
70
+ }
package/src/index.ts CHANGED
@@ -22,5 +22,5 @@ export {
22
22
  getDataV2 as _getDataV2,
23
23
  mapInstantiation as _mapInstantiation
24
24
  } from './api';
25
-
25
+ export type {QueryParameters} from './api';
26
26
  export type {CartoLayerProps} from './layers/carto-layer';
@@ -1,16 +1,15 @@
1
1
  import {
2
2
  CompositeLayer,
3
- Layer,
4
- log,
5
3
  CompositeLayerProps,
4
+ Layer,
6
5
  LayerProps,
7
- ChangeFlags
6
+ log,
7
+ UpdateParameters
8
8
  } from '@deck.gl/core';
9
- import CartoTileLayer from './carto-tile-layer';
10
- import H3TileLayer from './h3-tile-layer';
11
- import QuadbinTileLayer from './quadbin-tile-layer';
9
+
12
10
  import {MVTLayer} from '@deck.gl/geo-layers';
13
11
  import {fetchLayerData, getDataV2, API_VERSIONS} from '../api';
12
+ import {layerFromTileDataset} from '../api/layer-map';
14
13
  import {
15
14
  COLUMNS_SUPPORT,
16
15
  FORMATS,
@@ -18,7 +17,7 @@ import {
18
17
  MapType,
19
18
  MAP_TYPES,
20
19
  TileFormat,
21
- TILE_FORMATS
20
+ QueryParameters
22
21
  } from '../api/maps-api-common';
23
22
  import {
24
23
  ClassicCredentials,
@@ -66,7 +65,10 @@ const defaultProps = {
66
65
  aggregationExp: null,
67
66
 
68
67
  // (Number, optional): aggregation resolution level. Only used for spatial index datasets, defaults to 6 for quadbins, 4 for h3
69
- aggregationResLevel: null
68
+ aggregationResLevel: null,
69
+
70
+ // (QueryParameters, optional): query parameters to be sent to the server.
71
+ queryParameters: null
70
72
  };
71
73
 
72
74
  /** All properties supported by CartoLayer. */
@@ -151,6 +153,9 @@ type _CartoLayerProps = {
151
153
 
152
154
  /** Aggregation resolution level. Only used for spatial index datasets, defaults to 6 for quadbins, 4 for h3. **/
153
155
  aggregationResLevel?: number;
156
+
157
+ /** Query parameters to be sent to the server. **/
158
+ queryParameters?: QueryParameters;
154
159
  };
155
160
 
156
161
  export default class CartoLayer<ExtraProps = {}> extends CompositeLayer<
@@ -209,16 +214,7 @@ export default class CartoLayer<ExtraProps = {}> extends CompositeLayer<
209
214
  }
210
215
  }
211
216
 
212
- updateState({
213
- props,
214
- oldProps,
215
- changeFlags
216
- }: {
217
- props: CartoLayerProps;
218
- oldProps: CartoLayerProps;
219
- context: any;
220
- changeFlags: ChangeFlags;
221
- }): void {
217
+ updateState({props, oldProps, changeFlags}: UpdateParameters<this>) {
222
218
  this._checkProps(props);
223
219
  const shouldUpdateData =
224
220
  changeFlags.dataChanged ||
@@ -229,7 +225,8 @@ export default class CartoLayer<ExtraProps = {}> extends CompositeLayer<
229
225
  props.formatTiles !== oldProps.formatTiles ||
230
226
  props.type !== oldProps.type ||
231
227
  JSON.stringify(props.columns) !== JSON.stringify(oldProps.columns) ||
232
- JSON.stringify(props.credentials) !== JSON.stringify(oldProps.credentials);
228
+ JSON.stringify(props.credentials) !== JSON.stringify(oldProps.credentials) ||
229
+ JSON.stringify(props.queryParameters) !== JSON.stringify(oldProps.queryParameters);
233
230
 
234
231
  if (shouldUpdateData) {
235
232
  this.setState({data: null, apiVersion: null});
@@ -240,7 +237,7 @@ export default class CartoLayer<ExtraProps = {}> extends CompositeLayer<
240
237
 
241
238
  async _updateData(): Promise<void> {
242
239
  try {
243
- const {type, data: source, clientId, credentials, connection, ...rest} = this.props;
240
+ const {type, data: source, credentials, connection, ...rest} = this.props;
244
241
  const localConfig = {...getDefaultCredentials(), ...credentials};
245
242
  const {apiVersion} = localConfig;
246
243
 
@@ -253,7 +250,6 @@ export default class CartoLayer<ExtraProps = {}> extends CompositeLayer<
253
250
  result = await fetchLayerData({
254
251
  type,
255
252
  source,
256
- clientId,
257
253
  credentials: credentials as CloudNativeCredentials,
258
254
  connection,
259
255
  ...rest,
@@ -294,19 +290,10 @@ export default class CartoLayer<ExtraProps = {}> extends CompositeLayer<
294
290
 
295
291
  /* global URL */
296
292
  const tileUrl = new URL(data.tiles[0]);
297
-
298
293
  props.formatTiles =
299
- props.formatTiles ||
300
- (tileUrl.searchParams.get('formatTiles') as TileFormat) ||
301
- TILE_FORMATS.MVT;
294
+ props.formatTiles || (tileUrl.searchParams.get('formatTiles') as TileFormat);
302
295
 
303
- if (data.scheme === 'h3') {
304
- return [H3TileLayer, props];
305
- }
306
- if (data.scheme === 'quadbin') {
307
- return [QuadbinTileLayer, props];
308
- }
309
- return props.formatTiles === TILE_FORMATS.MVT ? [MVTLayer, props] : [CartoTileLayer, props];
296
+ return [layerFromTileDataset(props.formatTiles, data.scheme), props];
310
297
  }
311
298
 
312
299
  renderLayers(): Layer | null {