@deck.gl/carto 8.8.4 → 8.8.7

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 (125) hide show
  1. package/dist/dist.dev.js +31412 -22945
  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 +230 -57
  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 +15 -23
  14. package/dist/es5/layers/carto-layer.js.map +1 -1
  15. package/dist/es5/layers/carto-tile-layer.js +1 -1
  16. package/dist/es5/layers/carto-tile-layer.js.map +1 -1
  17. package/dist/es5/layers/quadbin-utils.js +2 -0
  18. package/dist/es5/layers/quadbin-utils.js.map +1 -1
  19. package/dist/es5/layers/schema/carto-spatial-tile-loader.js +99 -0
  20. package/dist/es5/layers/schema/carto-spatial-tile-loader.js.map +1 -0
  21. package/dist/es5/layers/schema/carto-spatial-tile.js +117 -0
  22. package/dist/es5/layers/schema/carto-spatial-tile.js.map +1 -0
  23. package/dist/es5/layers/schema/carto-tile.js +5 -1
  24. package/dist/es5/layers/schema/carto-tile.js.map +1 -1
  25. package/dist/es5/layers/schema/spatialjson-utils.js +41 -0
  26. package/dist/es5/layers/schema/spatialjson-utils.js.map +1 -0
  27. package/dist/es5/layers/spatial-index-tile-layer.js +6 -0
  28. package/dist/es5/layers/spatial-index-tile-layer.js.map +1 -1
  29. package/dist/esm/api/index.js.map +1 -1
  30. package/dist/esm/api/layer-map.js +79 -14
  31. package/dist/esm/api/layer-map.js.map +1 -1
  32. package/dist/esm/api/maps-api-common.js.map +1 -1
  33. package/dist/esm/api/maps-v3-client.js +129 -16
  34. package/dist/esm/api/maps-v3-client.js.map +1 -1
  35. package/dist/esm/api/parseMap.js +18 -4
  36. package/dist/esm/api/parseMap.js.map +1 -1
  37. package/dist/esm/config.js +10 -2
  38. package/dist/esm/config.js.map +1 -1
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/layers/carto-layer.js +12 -19
  41. package/dist/esm/layers/carto-layer.js.map +1 -1
  42. package/dist/esm/layers/carto-tile-layer.js +1 -1
  43. package/dist/esm/layers/carto-tile-layer.js.map +1 -1
  44. package/dist/esm/layers/quadbin-utils.js +2 -5
  45. package/dist/esm/layers/quadbin-utils.js.map +1 -1
  46. package/dist/esm/layers/schema/carto-spatial-tile-loader.js +56 -0
  47. package/dist/esm/layers/schema/carto-spatial-tile-loader.js.map +1 -0
  48. package/dist/esm/layers/schema/carto-spatial-tile.js +72 -0
  49. package/dist/esm/layers/schema/carto-spatial-tile.js.map +1 -0
  50. package/dist/esm/layers/schema/carto-tile.js +2 -2
  51. package/dist/esm/layers/schema/carto-tile.js.map +1 -1
  52. package/dist/esm/layers/schema/spatialjson-utils.js +26 -0
  53. package/dist/esm/layers/schema/spatialjson-utils.js.map +1 -0
  54. package/dist/esm/layers/spatial-index-tile-layer.js +3 -0
  55. package/dist/esm/layers/spatial-index-tile-layer.js.map +1 -1
  56. package/dist.min.js +138 -138
  57. package/package.json +2 -2
  58. package/src/api/index.ts +1 -0
  59. package/src/api/layer-map.ts +76 -13
  60. package/src/api/maps-api-common.ts +8 -0
  61. package/src/api/maps-v3-client.ts +148 -21
  62. package/src/api/parseMap.ts +6 -2
  63. package/src/config.ts +12 -2
  64. package/src/index.ts +1 -1
  65. package/src/layers/carto-layer.ts +26 -33
  66. package/src/layers/carto-tile-layer.ts +6 -3
  67. package/src/layers/quadbin-utils.ts +2 -2
  68. package/src/layers/schema/carto-spatial-tile-loader.ts +59 -0
  69. package/src/layers/schema/carto-spatial-tile.ts +83 -0
  70. package/src/layers/schema/carto-tile.ts +6 -6
  71. package/src/layers/schema/spatialjson-utils.ts +35 -0
  72. package/src/layers/spatial-index-tile-layer.ts +4 -0
  73. package/typed/api/index.d.ts +1 -0
  74. package/typed/api/index.d.ts.map +1 -1
  75. package/typed/api/layer-map.d.ts +12 -4
  76. package/typed/api/layer-map.d.ts.map +1 -1
  77. package/typed/api/maps-api-common.d.ts +4 -0
  78. package/typed/api/maps-api-common.d.ts.map +1 -1
  79. package/typed/api/maps-v3-client.d.ts +8 -4
  80. package/typed/api/maps-v3-client.d.ts.map +1 -1
  81. package/typed/config.d.ts +2 -0
  82. package/typed/config.d.ts.map +1 -1
  83. package/typed/index.d.ts +1 -0
  84. package/typed/index.d.ts.map +1 -1
  85. package/typed/layers/carto-layer.d.ts +8 -8
  86. package/typed/layers/carto-layer.d.ts.map +1 -1
  87. package/typed/layers/carto-tile-layer.d.ts.map +1 -1
  88. package/typed/layers/quadbin-utils.d.ts +2 -0
  89. package/typed/layers/quadbin-utils.d.ts.map +1 -1
  90. package/typed/layers/schema/carto-spatial-tile-loader.d.ts +4 -0
  91. package/typed/layers/schema/carto-spatial-tile-loader.d.ts.map +1 -0
  92. package/typed/layers/schema/carto-spatial-tile.d.ts +21 -0
  93. package/typed/layers/schema/carto-spatial-tile.d.ts.map +1 -0
  94. package/typed/layers/schema/carto-tile.d.ts +15 -3
  95. package/typed/layers/schema/carto-tile.d.ts.map +1 -1
  96. package/typed/layers/schema/spatialjson-utils.d.ts +25 -0
  97. package/typed/layers/schema/spatialjson-utils.d.ts.map +1 -0
  98. package/typed/layers/spatial-index-tile-layer.d.ts.map +1 -1
  99. package/typed/api/index.js +0 -3
  100. package/typed/api/layer-map.js +0 -264
  101. package/typed/api/maps-api-common.js +0 -50
  102. package/typed/api/maps-client.js +0 -125
  103. package/typed/api/maps-v3-client.js +0 -304
  104. package/typed/api/parseMap.js +0 -178
  105. package/typed/basemap.js +0 -9
  106. package/typed/config.js +0 -35
  107. package/typed/index.js +0 -10
  108. package/typed/layers/carto-layer.js +0 -173
  109. package/typed/layers/carto-tile-layer.js +0 -105
  110. package/typed/layers/h3-tile-layer.js +0 -47
  111. package/typed/layers/h3-tileset-2d.js +0 -85
  112. package/typed/layers/quadkey-tile-layer.d.ts +0 -21
  113. package/typed/layers/quadkey-tile-layer.d.ts.map +0 -1
  114. package/typed/layers/quadkey-tile-layer.js +0 -42
  115. package/typed/layers/quadkey-tileset-2d.d.ts +0 -15
  116. package/typed/layers/quadkey-tileset-2d.d.ts.map +0 -1
  117. package/typed/layers/quadkey-tileset-2d.js +0 -36
  118. package/typed/layers/schema/carto-tile.js +0 -161
  119. package/typed/layers/spatial-index-tile-layer.js +0 -40
  120. package/typed/style/color-bins-style.js +0 -12
  121. package/typed/style/color-categories-style.js +0 -16
  122. package/typed/style/color-continuous-style.js +0 -12
  123. package/typed/style/palette.js +0 -62
  124. package/typed/style/utils.js +0 -15
  125. 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.4",
5
+ "version": "8.8.7",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -61,5 +61,5 @@
61
61
  "@deck.gl/layers": "^8.0.0",
62
62
  "@loaders.gl/core": "^3.0.0"
63
63
  },
64
- "gitHead": "faab03adb63f2e2470d9c202dd740610be2ebc22"
64
+ "gitHead": "4485a8be47b76ba5f28447af304c683a00bbb4eb"
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
@@ -23,9 +29,11 @@ import {assert} from '../utils';
23
29
  const MAX_GET_LENGTH = 2048;
24
30
  const DEFAULT_CLIENT = 'deck-gl-carto';
25
31
 
32
+ export type Headers = Record<string, string>;
26
33
  interface RequestParams {
27
34
  method?: string;
28
35
  url: string;
36
+ headers?: Headers;
29
37
  accessToken?: string;
30
38
  body?: any;
31
39
  }
@@ -33,8 +41,15 @@ interface RequestParams {
33
41
  /**
34
42
  * Request against Maps API
35
43
  */
36
- async function request({method, url, accessToken, body}: RequestParams): Promise<Response> {
37
- const headers: Record<string, string> = {
44
+ async function request({
45
+ method,
46
+ url,
47
+ headers: customHeaders,
48
+ accessToken,
49
+ body
50
+ }: RequestParams): Promise<Response> {
51
+ const headers: Headers = {
52
+ ...customHeaders,
38
53
  Accept: 'application/json'
39
54
  };
40
55
 
@@ -61,10 +76,11 @@ async function request({method, url, accessToken, body}: RequestParams): Promise
61
76
  async function requestJson<T = unknown>({
62
77
  method,
63
78
  url,
79
+ headers,
64
80
  accessToken,
65
81
  body
66
82
  }: RequestParams): Promise<T> {
67
- const response = await request({method, url, accessToken, body});
83
+ const response = await request({method, url, headers, accessToken, body});
68
84
  const json = await response.json();
69
85
 
70
86
  if (!response.ok) {
@@ -115,8 +131,10 @@ type FetchLayerDataParams = {
115
131
  clientId?: string;
116
132
  format?: Format;
117
133
  formatTiles?: TileFormat;
134
+ headers?: Headers;
118
135
  aggregationExp?: string;
119
136
  aggregationResLevel?: number;
137
+ queryParameters?: QueryParameters;
120
138
  };
121
139
 
122
140
  /**
@@ -129,13 +147,18 @@ function getParameters({
129
147
  columns,
130
148
  clientId,
131
149
  aggregationExp,
132
- aggregationResLevel
150
+ aggregationResLevel,
151
+ queryParameters
133
152
  }: Omit<FetchLayerDataParams, 'connection' | 'credentials'>) {
134
153
  const parameters = [encodeParameter('client', clientId || DEFAULT_CLIENT)];
135
154
 
136
155
  const sourceName = type === MAP_TYPES.QUERY ? 'q' : 'name';
137
156
  parameters.push(encodeParameter(sourceName, source));
138
157
 
158
+ if (queryParameters) {
159
+ parameters.push(encodeParameter('queryParameters', JSON.stringify(queryParameters)));
160
+ }
161
+
139
162
  if (geoColumn) {
140
163
  parameters.push(encodeParameter('geo_column', geoColumn));
141
164
  }
@@ -144,6 +167,9 @@ function getParameters({
144
167
  }
145
168
  if (aggregationExp) {
146
169
  parameters.push(encodeParameter('aggregationExp', aggregationExp));
170
+ } else if (isSpatialIndexGeoColumn(geoColumn)) {
171
+ // Default aggregationExp required for spatial index layers
172
+ parameters.push(encodeParameter('aggregationExp', '1 AS value'));
147
173
  }
148
174
  if (aggregationResLevel) {
149
175
  parameters.push(encodeParameter('aggregationResLevel', aggregationResLevel));
@@ -152,6 +178,11 @@ function getParameters({
152
178
  return parameters.join('&');
153
179
  }
154
180
 
181
+ function isSpatialIndexGeoColumn(geoColumn: string | undefined) {
182
+ const spatialIndex = geoColumn?.split(':')[0];
183
+ return spatialIndex === 'h3' || spatialIndex === 'quadbin';
184
+ }
185
+
155
186
  export async function mapInstantiation({
156
187
  type,
157
188
  source,
@@ -160,8 +191,10 @@ export async function mapInstantiation({
160
191
  geoColumn,
161
192
  columns,
162
193
  clientId,
194
+ headers,
163
195
  aggregationExp,
164
- aggregationResLevel
196
+ aggregationResLevel,
197
+ queryParameters
165
198
  }: FetchLayerDataParams): Promise<MapInstantiation> {
166
199
  const baseUrl = `${credentials.mapsUrl}/${connection}/${type}`;
167
200
  const url = `${baseUrl}?${getParameters({
@@ -170,8 +203,9 @@ export async function mapInstantiation({
170
203
  geoColumn,
171
204
  columns,
172
205
  clientId,
206
+ aggregationResLevel,
173
207
  aggregationExp,
174
- aggregationResLevel
208
+ queryParameters
175
209
  })}`;
176
210
  const {accessToken} = credentials;
177
211
 
@@ -179,12 +213,13 @@ export async function mapInstantiation({
179
213
  // need to be a POST request
180
214
  const body = JSON.stringify({
181
215
  q: source,
182
- client: clientId || DEFAULT_CLIENT
216
+ client: clientId || DEFAULT_CLIENT,
217
+ queryParameters
183
218
  });
184
- return await requestJson({method: 'POST', url: baseUrl, accessToken, body});
219
+ return await requestJson({method: 'POST', url: baseUrl, headers, accessToken, body});
185
220
  }
186
221
 
187
- return await requestJson({url, accessToken});
222
+ return await requestJson({url, headers, accessToken});
188
223
  }
189
224
 
190
225
  function getUrlFromMetadata(metadata: MapInstantiation, format: Format): string | null {
@@ -254,8 +289,10 @@ export async function fetchLayerData({
254
289
  format,
255
290
  formatTiles,
256
291
  clientId,
292
+ headers,
257
293
  aggregationExp,
258
- aggregationResLevel
294
+ aggregationResLevel,
295
+ queryParameters
259
296
  }: FetchLayerDataParams): Promise<FetchLayerDataResult> {
260
297
  // Internally we split data fetching into two parts to allow us to
261
298
  // conditionally fetch the actual data, depending on the metadata state
@@ -269,8 +306,10 @@ export async function fetchLayerData({
269
306
  format,
270
307
  formatTiles,
271
308
  clientId,
309
+ headers,
272
310
  aggregationExp,
273
- aggregationResLevel
311
+ aggregationResLevel,
312
+ queryParameters
274
313
  });
275
314
 
276
315
  const data = await requestData({url, format: mapFormat, accessToken});
@@ -288,8 +327,10 @@ async function _fetchDataUrl({
288
327
  format,
289
328
  formatTiles,
290
329
  clientId,
330
+ headers,
291
331
  aggregationExp,
292
- aggregationResLevel
332
+ aggregationResLevel,
333
+ queryParameters
293
334
  }: FetchLayerDataParams) {
294
335
  const defaultCredentials = getDefaultCredentials();
295
336
  // Only pick up default credentials if they have been defined for
@@ -321,8 +362,10 @@ async function _fetchDataUrl({
321
362
  geoColumn,
322
363
  columns,
323
364
  clientId,
365
+ headers,
324
366
  aggregationExp,
325
- aggregationResLevel
367
+ aggregationResLevel,
368
+ queryParameters
326
369
  });
327
370
  let url: string | null = null;
328
371
  let mapFormat: Format | undefined;
@@ -361,19 +404,34 @@ async function _fetchMapDataset(
361
404
  dataset,
362
405
  accessToken: string,
363
406
  credentials: CloudNativeCredentials,
364
- clientId?: string
407
+ clientId?: string,
408
+ headers?: Headers,
409
+ queryParameters?: QueryParameters
365
410
  ) {
366
- const {connectionName: connection, columns, format, geoColumn, source, type} = dataset;
411
+ const {
412
+ aggregationExp,
413
+ aggregationResLevel,
414
+ connectionName: connection,
415
+ columns,
416
+ format,
417
+ geoColumn,
418
+ source,
419
+ type
420
+ } = dataset;
367
421
  // First fetch metadata
368
422
  const {url, mapFormat} = await _fetchDataUrl({
423
+ aggregationExp,
424
+ aggregationResLevel,
369
425
  clientId,
370
426
  credentials: {...credentials, accessToken},
371
427
  connection,
372
428
  columns,
373
429
  format,
374
430
  geoColumn,
431
+ headers,
375
432
  source,
376
- type
433
+ type,
434
+ queryParameters
377
435
  });
378
436
 
379
437
  // Extract the last time the data changed
@@ -389,12 +447,76 @@ async function _fetchMapDataset(
389
447
  return true;
390
448
  }
391
449
 
450
+ async function _fetchTilestats(
451
+ attribute,
452
+ dataset,
453
+ accessToken: string,
454
+ credentials: CloudNativeCredentials
455
+ ) {
456
+ const {connectionName: connection, source, type} = dataset;
457
+
458
+ const statsUrl = buildStatsUrlFromBase(credentials.apiBaseUrl);
459
+ let url = `${statsUrl}/${connection}/`;
460
+ if (type === MAP_TYPES.QUERY) {
461
+ url += `${attribute}?q=${source}`;
462
+ } else {
463
+ // MAP_TYPE.TABLE
464
+ url += `${source}/${attribute}`;
465
+ }
466
+ const stats = await requestData({url, format: FORMATS.JSON, accessToken});
467
+
468
+ // Replace tilestats for attribute with value from API
469
+ const {attributes} = dataset.data.tilestats.layers[0];
470
+ const index = attributes.findIndex(d => d.attribute === attribute);
471
+ attributes[index] = stats;
472
+ return true;
473
+ }
474
+
392
475
  async function fillInMapDatasets(
393
476
  {datasets, token},
394
477
  clientId: string,
478
+ credentials: CloudNativeCredentials,
479
+ headers?: Headers
480
+ ) {
481
+ const promises = datasets.map(dataset =>
482
+ _fetchMapDataset(dataset, token, credentials, clientId, headers)
483
+ );
484
+ return await Promise.all(promises);
485
+ }
486
+
487
+ async function fillInTileStats(
488
+ {datasets, keplerMapConfig, token},
395
489
  credentials: CloudNativeCredentials
396
490
  ) {
397
- const promises = datasets.map(dataset => _fetchMapDataset(dataset, token, credentials, clientId));
491
+ const attributes: {attribute?: string; dataset?: any}[] = [];
492
+ const {layers} = keplerMapConfig.config.visState;
493
+ for (const layer of layers) {
494
+ for (const channel of Object.keys(layer.visualChannels)) {
495
+ const attribute = layer.visualChannels[channel]?.name;
496
+ if (attribute) {
497
+ const dataset = datasets.find(d => d.id === layer.config.dataId);
498
+ if (dataset.data.tilestats && dataset.type !== MAP_TYPES.TILESET) {
499
+ // Only fetch stats for QUERY & TABLE map types
500
+ attributes.push({attribute, dataset});
501
+ }
502
+ }
503
+ }
504
+ }
505
+ // Remove duplicates to avoid repeated requests
506
+ const filteredAttributes: {attribute?: string; dataset?: any}[] = [];
507
+ for (const a of attributes) {
508
+ if (
509
+ !filteredAttributes.find(
510
+ ({attribute, dataset}) => attribute === a.attribute && dataset === a.dataset
511
+ )
512
+ ) {
513
+ filteredAttributes.push(a);
514
+ }
515
+ }
516
+
517
+ const promises = filteredAttributes.map(({attribute, dataset}) =>
518
+ _fetchTilestats(attribute, dataset, token, credentials)
519
+ );
398
520
  return await Promise.all(promises);
399
521
  }
400
522
 
@@ -402,12 +524,14 @@ export async function fetchMap({
402
524
  cartoMapId,
403
525
  clientId,
404
526
  credentials,
527
+ headers,
405
528
  autoRefresh,
406
529
  onNewData
407
530
  }: {
408
531
  cartoMapId: string;
409
532
  clientId: string;
410
533
  credentials?: CloudNativeCredentials;
534
+ headers?: Headers;
411
535
  autoRefresh?: number;
412
536
  onNewData?: (map: any) => void;
413
537
  }) {
@@ -436,7 +560,7 @@ export async function fetchMap({
436
560
  }
437
561
 
438
562
  const url = `${localCreds.mapsUrl}/public/${cartoMapId}`;
439
- const map = await requestJson<any>({url, accessToken});
563
+ const map = await requestJson<any>({url, headers, accessToken});
440
564
 
441
565
  // Periodically check if the data has changed. Note that this
442
566
  // will not update when a map is published.
@@ -444,7 +568,7 @@ export async function fetchMap({
444
568
  if (autoRefresh) {
445
569
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
446
570
  const intervalId = setInterval(async () => {
447
- const changed = await fillInMapDatasets(map, clientId, localCreds);
571
+ const changed = await fillInMapDatasets(map, clientId, localCreds, headers);
448
572
  if (onNewData && changed.some(v => v === true)) {
449
573
  onNewData(parseMap(map));
450
574
  }
@@ -465,7 +589,10 @@ export async function fetchMap({
465
589
  });
466
590
 
467
591
  // Mutates map.datasets so that dataset.data contains data
468
- await fillInMapDatasets(map, clientId, localCreds);
592
+ await fillInMapDatasets(map, clientId, localCreds, headers);
593
+
594
+ // Mutates attributes in visualChannels to contain tile stats
595
+ await fillInTileStats(map, localCreds);
469
596
  return {
470
597
  ...parseMap(map),
471
598
  ...{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';