@carto/api-client 0.5.5-alpha.0 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "homepage": "https://github.com/CartoDB/carto-api-client#readme",
9
9
  "author": "Don McCurdy <donmccurdy@carto.com>",
10
10
  "packageManager": "yarn@4.3.1",
11
- "version": "0.5.5-alpha.0",
11
+ "version": "0.5.5",
12
12
  "license": "MIT",
13
13
  "publishConfig": {
14
14
  "access": "public"
@@ -1,4 +1,4 @@
1
- import {deviation, extent, groupSort, median, variance} from 'd3-array';
1
+ import {extent, groupSort} from 'd3-array';
2
2
  import {rgb} from 'd3-color';
3
3
  import {
4
4
  scaleLinear,
@@ -38,7 +38,24 @@ import type {
38
38
  import type {ProviderType, SchemaField} from '../types.js';
39
39
  import {DEFAULT_AGGREGATION_EXP_ALIAS} from '../constants-internal.js';
40
40
 
41
- const SCALE_FUNCS: Record<string, () => any> = {
41
+ export type D3Scale = {
42
+ domain: (d?: any) => any[];
43
+ range: (d?: any) => any[];
44
+ unknown?: (d?: string) => any;
45
+ } & ((d: any) => any);
46
+ type D3ScaleFactory = () => D3Scale;
47
+
48
+ export type ScaleType =
49
+ | 'linear'
50
+ | 'ordinal'
51
+ | 'log'
52
+ | 'point'
53
+ | 'quantile'
54
+ | 'quantize'
55
+ | 'sqrt'
56
+ | 'custom'
57
+ | 'identity';
58
+ const SCALE_FUNCS: Record<ScaleType, D3ScaleFactory> = {
42
59
  linear: scaleLinear,
43
60
  ordinal: scaleOrdinal,
44
61
  log: scaleLog,
@@ -49,7 +66,6 @@ const SCALE_FUNCS: Record<string, () => any> = {
49
66
  custom: scaleThreshold,
50
67
  identity: scaleIdentity,
51
68
  };
52
- export type SCALE_TYPE = keyof typeof SCALE_FUNCS;
53
69
 
54
70
  function identity<T>(v: T): T {
55
71
  return v;
@@ -57,30 +73,12 @@ function identity<T>(v: T): T {
57
73
 
58
74
  const UNKNOWN_COLOR = '#868d91';
59
75
 
60
- export const AGGREGATION: Record<string, string> = {
61
- average: 'MEAN',
62
- maximum: 'MAX',
63
- minimum: 'MIN',
64
- sum: 'SUM',
65
- };
66
-
67
76
  export const OPACITY_MAP: Record<string, string> = {
68
77
  getFillColor: 'opacity',
69
78
  getLineColor: 'strokeOpacity',
70
79
  getTextColor: 'opacity',
71
80
  };
72
81
 
73
- const AGGREGATION_FUNC: Record<string, (values: any, accessor: any) => any> = {
74
- 'count unique': (values: any, accessor: any) =>
75
- groupSort(values, (v) => v.length, accessor).length,
76
- median,
77
- // Unfortunately mode() is only available in d3-array@3+ which is ESM only
78
- mode: (values: any, accessor: any) =>
79
- groupSort(values, (v) => v.length, accessor).pop(),
80
- stddev: deviation,
81
- variance,
82
- };
83
-
84
82
  const hexToRGBA = (c: any) => {
85
83
  const {r, g, b, opacity} = rgb(c);
86
84
  return [r, g, b, 255 * opacity];
@@ -197,7 +195,7 @@ export function getLayerProps(
197
195
 
198
196
  function domainFromAttribute(
199
197
  attribute: any,
200
- scaleType: SCALE_TYPE,
198
+ scaleType: ScaleType,
201
199
  scaleLength: number
202
200
  ) {
203
201
  if (scaleType === 'ordinal' || scaleType === 'point') {
@@ -219,7 +217,7 @@ function domainFromAttribute(
219
217
  return [min, attribute.max];
220
218
  }
221
219
 
222
- function domainFromValues(values: any, scaleType: SCALE_TYPE) {
220
+ function domainFromValues(values: any, scaleType: ScaleType) {
223
221
  if (scaleType === 'ordinal' || scaleType === 'point') {
224
222
  return groupSort(
225
223
  values,
@@ -238,7 +236,7 @@ function domainFromValues(values: any, scaleType: SCALE_TYPE) {
238
236
  function calculateDomain(
239
237
  data: any,
240
238
  name: any,
241
- scaleType: SCALE_TYPE,
239
+ scaleType: ScaleType,
242
240
  scaleLength?: number
243
241
  ) {
244
242
  if (data.tilestats) {
@@ -295,23 +293,13 @@ function findAccessorKey(keys: string[], properties: any): string[] {
295
293
  return keys;
296
294
  }
297
295
 
298
- export function getColorValueAccessor(
299
- {name}: VisualChannelField,
300
- colorAggregation: string,
301
- data: any
302
- ) {
303
- const aggregator = AGGREGATION_FUNC[colorAggregation];
304
- const accessor = (values: any) => aggregator(values, (p: any) => p[name]);
305
- return normalizeAccessor(accessor, data);
306
- }
307
-
308
296
  export function getColorAccessor(
309
297
  {name, colorColumn}: VisualChannelField,
310
- scaleType: SCALE_TYPE,
298
+ scaleType: ScaleType,
311
299
  {aggregation, range}: {aggregation: string; range: any},
312
300
  opacity: number | undefined,
313
301
  data: any
314
- ) {
302
+ ): {accessor: any; scale: any} {
315
303
  const scale = calculateLayerScale(
316
304
  colorColumn || name,
317
305
  scaleType,
@@ -329,12 +317,12 @@ export function getColorAccessor(
329
317
  const {r, g, b} = rgb(scale(propertyValue));
330
318
  return [r, g, b, propertyValue === null ? 0 : alpha];
331
319
  };
332
- return normalizeAccessor(accessor, data);
320
+ return {accessor: normalizeAccessor(accessor, data), scale};
333
321
  }
334
322
 
335
323
  function calculateLayerScale(
336
324
  name: any,
337
- scaleType: SCALE_TYPE,
325
+ scaleType: ScaleType,
338
326
  range: any,
339
327
  data: any
340
328
  ) {
@@ -362,7 +350,7 @@ function calculateLayerScale(
362
350
 
363
351
  scale.domain(domain);
364
352
  scale.range(scaleColor);
365
- scale.unknown(UNKNOWN_COLOR);
353
+ scale.unknown!(UNKNOWN_COLOR);
366
354
 
367
355
  return scale;
368
356
  }
@@ -436,17 +424,17 @@ export function negateAccessor(accessor: Accessor): Accessor {
436
424
 
437
425
  export function getSizeAccessor(
438
426
  {name}: VisualChannelField,
439
- scaleType: SCALE_TYPE | undefined,
427
+ scaleType: ScaleType | undefined,
440
428
  aggregation: string | null | undefined,
441
429
  range: Iterable<Range> | null | undefined,
442
430
  data: any
443
- ) {
444
- const scale = scaleType ? SCALE_FUNCS[scaleType as any]() : identity;
431
+ ): {accessor: any; scale: any} {
432
+ const scale = scaleType ? SCALE_FUNCS[scaleType]() : identity;
445
433
  if (scaleType) {
446
434
  if (aggregation !== 'count') {
447
- scale.domain(calculateDomain(data, name, scaleType));
435
+ (scale as D3Scale).domain(calculateDomain(data, name, scaleType));
448
436
  }
449
- scale.range(range);
437
+ (scale as D3Scale).range(range);
450
438
  }
451
439
 
452
440
  let accessorKeys = getAccessorKeys(name, aggregation);
@@ -457,7 +445,7 @@ export function getSizeAccessor(
457
445
  const propertyValue = properties[accessorKeys[0]];
458
446
  return scale(propertyValue);
459
447
  };
460
- return normalizeAccessor(accessor, data);
448
+ return {accessor: normalizeAccessor(accessor, data), scale};
461
449
  }
462
450
 
463
451
  const FORMATS: Record<string, (value: any) => string> = {
@@ -5,7 +5,6 @@ import {
5
5
  getDefaultAggregationExpColumnAliasForLayerType,
6
6
  getLayerProps,
7
7
  getColorAccessor,
8
- getColorValueAccessor,
9
8
  getSizeAccessor,
10
9
  getTextAccessor,
11
10
  opacityToAlpha,
@@ -13,11 +12,12 @@ import {
13
12
  negateAccessor,
14
13
  getMaxMarkerSize,
15
14
  type LayerType,
16
- AGGREGATION,
17
15
  OPACITY_MAP,
18
16
  TEXT_NUMBER_FORMATTER,
19
17
  TEXT_LABEL_INDEX,
20
18
  TEXT_OUTLINE_OPACITY,
19
+ type D3Scale,
20
+ type ScaleType,
21
21
  } from './layer-map.js';
22
22
 
23
23
  import {assert, isEmptyObject} from '../utils.js';
@@ -29,13 +29,22 @@ import type {
29
29
  VisConfig,
30
30
  MapConfigLayer,
31
31
  Dataset,
32
+ VisualChannelField,
32
33
  } from './types.js';
33
34
  import {isRemoteCalculationSupported} from './utils.js';
34
35
 
36
+ export type Scale = {
37
+ field: VisualChannelField;
38
+ domain: string[] | number[];
39
+ range: string[] | number[];
40
+ type: ScaleType;
41
+ };
42
+
35
43
  export type LayerDescriptor = {
36
44
  type: LayerType;
37
45
  props: Record<string, any>;
38
46
  filters?: Filters;
47
+ scales: Record<ScaleKey, Scale>;
39
48
  };
40
49
 
41
50
  export type ParseMapResult = {
@@ -94,6 +103,14 @@ export function parseMap(json: any) {
94
103
 
95
104
  const styleProps = createStyleProps(config, propMap);
96
105
 
106
+ const {channelProps, scales} = createChannelProps(
107
+ id,
108
+ type,
109
+ config,
110
+ visualChannels,
111
+ data,
112
+ dataset
113
+ );
97
114
  const layer: LayerDescriptor = {
98
115
  type,
99
116
  filters:
@@ -106,20 +123,14 @@ export function parseMap(json: any) {
106
123
  ...defaultProps,
107
124
  ...createInteractionProps(interactionConfig),
108
125
  ...styleProps,
109
- ...createChannelProps(
110
- id,
111
- type,
112
- config,
113
- visualChannels,
114
- data,
115
- dataset
116
- ), // Must come after style
126
+ ...channelProps,
117
127
  ...createParametersProp(
118
128
  layerBlending,
119
129
  styleProps.parameters || {}
120
130
  ), // Must come after style
121
131
  ...createLoadOptions(token),
122
132
  },
133
+ scales,
123
134
  };
124
135
  return layer;
125
136
  } catch (e: any) {
@@ -205,79 +216,67 @@ function createStyleProps(config: MapLayerConfig, mapping: any) {
205
216
  return result;
206
217
  }
207
218
 
219
+ function domainAndRangeFromScale(
220
+ scale: D3Scale
221
+ ): Pick<Scale, 'domain' | 'range'> {
222
+ return {
223
+ domain: scale.domain(),
224
+ range: scale.range(),
225
+ };
226
+ }
227
+
228
+ export type ScaleKey =
229
+ | 'fillColor'
230
+ | 'pointRadius'
231
+ | 'lineColor'
232
+ | 'elevation'
233
+ | 'weight';
234
+
208
235
  function createChannelProps(
209
236
  id: string,
210
- type: string,
237
+ layerType: LayerType,
211
238
  config: MapLayerConfig,
212
239
  visualChannels: VisualChannels,
213
240
  data: any,
214
241
  dataset: Dataset
215
- ) {
242
+ ): {channelProps: Record<string, any>; scales: Record<ScaleKey, Scale>} {
216
243
  const {
217
244
  colorField,
218
245
  colorScale,
219
246
  radiusField,
220
247
  radiusScale,
221
- sizeField,
222
- sizeScale,
223
248
  strokeColorField,
224
249
  strokeColorScale,
225
250
  weightField,
226
251
  } = visualChannels;
227
- let {heightField, heightScale} = visualChannels;
228
- if (type === 'hexagonId') {
229
- heightField = sizeField;
230
- heightScale = sizeScale;
231
- }
252
+ const {heightField, heightScale} = visualChannels;
232
253
  const {textLabel, visConfig} = config;
233
254
  const result: Record<string, any> = {};
234
255
 
235
- if (type === 'grid' || type === 'hexagon') {
236
- result.colorScaleType = colorScale;
237
- if (colorField) {
238
- const {colorAggregation} = config.visConfig;
239
- if (!AGGREGATION[colorAggregation]) {
240
- result.getColorValue = getColorValueAccessor(
241
- colorField,
242
- colorAggregation,
243
- data
244
- );
245
- } else {
246
- result.getColorWeight = (d: any) => d[colorField.name];
247
- }
248
- }
249
- } else if (colorField) {
256
+ const scales: Record<string, Scale> = {};
257
+
258
+ if (colorField) {
250
259
  const {colorAggregation: aggregation, colorRange: range} = visConfig;
251
- result.getFillColor = getColorAccessor(
260
+ const {accessor, scale} = getColorAccessor(
252
261
  colorField,
253
- // @ts-ignore
254
- colorScale,
262
+ colorScale!,
255
263
  {aggregation, range},
256
264
  visConfig.opacity,
257
265
  data
258
266
  );
267
+ result.getFillColor = accessor;
268
+ scales.fillColor = {
269
+ field: colorField,
270
+ type: colorScale!,
271
+ ...domainAndRangeFromScale(scale),
272
+ };
273
+ } else if (visConfig.filled) {
274
+ scales.fillColor = {} as any;
259
275
  }
260
276
 
261
- if (type === 'point') {
262
- const altitude = config.columns?.altitude;
263
- if (altitude) {
264
- result.dataTransform = (data: any) => {
265
- data.features.forEach(
266
- ({geometry, properties}: {geometry: any; properties: any}) => {
267
- const {type, coordinates} = geometry;
268
- if (type === 'Point') {
269
- coordinates[2] = properties[altitude];
270
- }
271
- }
272
- );
273
- return data;
274
- };
275
- }
276
- }
277
-
278
- if (type === 'clusterTile') {
277
+ if (layerType === 'clusterTile') {
279
278
  const aggregationExpAlias = getDefaultAggregationExpColumnAliasForLayerType(
280
- type,
279
+ layerType,
281
280
  dataset.providerId,
282
281
  data.schema
283
282
  );
@@ -325,55 +324,71 @@ function createChannelProps(
325
324
  };
326
325
  }
327
326
 
328
- if (radiusField || sizeField) {
329
- result.getPointRadius = getSizeAccessor(
330
- // @ts-ignore
331
- radiusField || sizeField,
332
- // @ts-ignore
333
- radiusScale || sizeScale,
327
+ if (radiusField) {
328
+ const {accessor, scale} = getSizeAccessor(
329
+ radiusField,
330
+ radiusScale,
334
331
  visConfig.sizeAggregation,
335
332
  visConfig.radiusRange || visConfig.sizeRange,
336
333
  data
337
334
  );
335
+ result.getPointRadius = accessor;
336
+ scales.pointRadius = {
337
+ field: radiusField,
338
+ type: radiusScale || 'identity',
339
+ ...domainAndRangeFromScale(scale),
340
+ };
338
341
  }
339
342
 
340
343
  if (strokeColorField) {
341
- const fallbackOpacity = type === 'point' ? visConfig.opacity : 1;
342
344
  const opacity =
343
- visConfig.strokeOpacity !== undefined
344
- ? visConfig.strokeOpacity
345
- : fallbackOpacity;
345
+ visConfig.strokeOpacity !== undefined ? visConfig.strokeOpacity : 1;
346
346
  const {strokeColorAggregation: aggregation, strokeColorRange: range} =
347
347
  visConfig;
348
- result.getLineColor = getColorAccessor(
348
+ const {accessor, scale} = getColorAccessor(
349
349
  strokeColorField,
350
- // @ts-ignore
351
- strokeColorScale,
352
- // @ts-ignore
350
+ strokeColorScale!,
353
351
  {aggregation, range},
354
352
  opacity,
355
353
  data
356
354
  );
355
+ result.getLineColor = accessor;
356
+ scales.lineColor = {
357
+ field: strokeColorField,
358
+ type: strokeColorScale!,
359
+ ...domainAndRangeFromScale(scale),
360
+ };
357
361
  }
358
362
  if (heightField && visConfig.enable3d) {
359
- result.getElevation = getSizeAccessor(
363
+ const {accessor, scale} = getSizeAccessor(
360
364
  heightField,
361
- // @ts-ignore
362
365
  heightScale,
363
366
  visConfig.heightAggregation,
364
367
  visConfig.heightRange || visConfig.sizeRange,
365
368
  data
366
369
  );
370
+ result.getElevation = accessor;
371
+ scales.elevation = {
372
+ field: heightField,
373
+ type: heightScale || 'identity',
374
+ ...domainAndRangeFromScale(scale),
375
+ };
367
376
  }
368
377
 
369
378
  if (weightField) {
370
- result.getWeight = getSizeAccessor(
379
+ const {accessor, scale} = getSizeAccessor(
371
380
  weightField,
372
381
  undefined,
373
382
  visConfig.weightAggregation,
374
383
  undefined,
375
384
  data
376
385
  );
386
+ result.getWeight = accessor;
387
+ scales.weight = {
388
+ field: weightField,
389
+ type: 'identity',
390
+ ...domainAndRangeFromScale(scale),
391
+ };
377
392
  }
378
393
 
379
394
  if (visConfig.customMarkers) {
@@ -416,17 +431,16 @@ function createChannelProps(
416
431
  }
417
432
 
418
433
  if (visualChannels.rotationField) {
419
- result.getIconAngle = negateAccessor(
420
- getSizeAccessor(
421
- visualChannels.rotationField,
422
- undefined,
423
- null,
424
- undefined,
425
- data
426
- )
434
+ const {accessor} = getSizeAccessor(
435
+ visualChannels.rotationField,
436
+ undefined,
437
+ null,
438
+ undefined,
439
+ data
427
440
  );
441
+ result.getIconAngle = negateAccessor(accessor);
428
442
  }
429
- } else if (type === 'point' || type === 'tileset') {
443
+ } else if (layerType === 'tileset') {
430
444
  result.pointType = 'circle';
431
445
  }
432
446
 
@@ -480,7 +494,7 @@ function createChannelProps(
480
494
  };
481
495
  }
482
496
 
483
- return result;
497
+ return {channelProps: result, scales};
484
498
  }
485
499
 
486
500
  function createLoadOptions(accessToken: string) {
@@ -1,4 +1,4 @@
1
- import type {LayerType, SCALE_TYPE} from './layer-map.js';
1
+ import type {LayerType, ScaleType} from './layer-map.js';
2
2
  import type {Format, MapType, ProviderType, QueryParameters} from '../types.js';
3
3
  import type {TilejsonResult} from '../sources/types.js';
4
4
 
@@ -10,25 +10,25 @@ export type VisualChannelField = {
10
10
 
11
11
  export type VisualChannels = {
12
12
  colorField?: VisualChannelField;
13
- colorScale?: SCALE_TYPE;
13
+ colorScale?: ScaleType;
14
14
 
15
15
  customMarkersField?: VisualChannelField;
16
- customMarkersScale?: SCALE_TYPE;
16
+ customMarkersScale?: ScaleType;
17
17
 
18
18
  radiusField?: VisualChannelField;
19
- radiusScale?: SCALE_TYPE;
19
+ radiusScale?: ScaleType;
20
20
 
21
- rotationScale?: SCALE_TYPE;
21
+ rotationScale?: ScaleType;
22
22
  rotationField?: VisualChannelField;
23
23
 
24
24
  sizeField?: VisualChannelField;
25
- sizeScale?: SCALE_TYPE;
25
+ sizeScale?: ScaleType;
26
26
 
27
27
  strokeColorField?: VisualChannelField;
28
- strokeColorScale?: SCALE_TYPE;
28
+ strokeColorScale?: ScaleType;
29
29
 
30
30
  heightField?: VisualChannelField;
31
- heightScale?: SCALE_TYPE;
31
+ heightScale?: ScaleType;
32
32
 
33
33
  weightField?: VisualChannelField;
34
34
  };