@carto/api-client 0.5.15-alpha.raster-4 → 0.5.15

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.
@@ -16,6 +16,7 @@ import {
16
16
  TEXT_NUMBER_FORMATTER,
17
17
  TEXT_LABEL_INDEX,
18
18
  TEXT_OUTLINE_OPACITY,
19
+ type D3Scale,
19
20
  type ScaleType,
20
21
  } from './layer-map.js';
21
22
 
@@ -31,38 +32,19 @@ import type {
31
32
  VisualChannelField,
32
33
  } from './types.js';
33
34
  import {isRemoteCalculationSupported} from './utils.js';
34
- import {
35
- getRasterTileLayerStylePropsRgb,
36
- getRasterTileLayerStylePropsScaledBand,
37
- } from './raster-layer.js';
38
- import type {TilejsonResult} from '../sources/types.js';
39
35
 
40
36
  export type Scale = {
41
- type: ScaleType;
42
37
  field: VisualChannelField;
43
-
44
- /** Natural domain of the scale, as defined by the data */
45
38
  domain: string[] | number[];
46
-
47
- /** Domain of the user to construct d3 scale */
48
- scaleDomain?: string[] | number[];
49
- range?: string[] | number[];
39
+ range: string[] | number[];
40
+ type: ScaleType;
50
41
  };
51
42
 
52
- export type ScaleKey =
53
- | 'fillColor'
54
- | 'pointRadius'
55
- | 'lineColor'
56
- | 'elevation'
57
- | 'weight';
58
-
59
- export type Scales = Partial<Record<ScaleKey, Scale>>;
60
-
61
43
  export type LayerDescriptor = {
62
44
  type: LayerType;
63
45
  props: Record<string, any>;
64
46
  filters?: Filters;
65
- scales: Scales;
47
+ scales: Record<ScaleKey, Scale>;
66
48
  };
67
49
 
68
50
  export type ParseMapResult = {
@@ -86,62 +68,13 @@ export type ParseMapResult = {
86
68
  layers: LayerDescriptor[];
87
69
  };
88
70
 
89
- export function getLayerDescriptor({
90
- mapConfig,
91
- layer,
92
- dataset,
93
- }: {
94
- mapConfig: KeplerMapConfig;
95
- layer: MapConfigLayer;
96
- dataset: Dataset;
97
- }) {
98
- const {filters, visState} = mapConfig;
99
- const {layerBlending, interactionConfig} = visState;
100
- const {id, type, config, visualChannels} = layer;
101
- const {data, id: datasetId} = dataset;
102
-
103
- const {propMap, defaultProps} = getLayerProps(type, config, dataset);
104
-
105
- const styleProps = createStyleProps(config, propMap);
106
-
107
- const {channelProps, scales} = createChannelProps(
108
- id,
109
- type,
110
- config,
111
- visualChannels,
112
- data,
113
- dataset
114
- );
115
- const layerDescriptor: LayerDescriptor = {
116
- type,
117
- filters:
118
- isEmptyObject(filters) || isRemoteCalculationSupported(dataset)
119
- ? undefined
120
- : filters[datasetId],
121
- props: {
122
- id,
123
- data,
124
- ...defaultProps,
125
- ...createInteractionProps(interactionConfig),
126
- ...styleProps,
127
- ...channelProps,
128
- ...createParametersProp(layerBlending, styleProps.parameters || {}), // Must come after style
129
- ...createLoadOptions(data.accessToken),
130
- },
131
- scales,
132
- };
133
- return layerDescriptor;
134
- }
135
-
136
71
  export function parseMap(json: any) {
137
72
  const {keplerMapConfig, datasets, token} = json;
138
73
  assert(keplerMapConfig.version === 'v1', 'Only support Kepler v1');
139
- const mapConfig = keplerMapConfig.config as KeplerMapConfig;
140
- const {mapState, mapStyle, popupSettings, legendSettings, visState} =
141
- mapConfig;
142
- const {layers} = visState;
74
+ const config = keplerMapConfig.config as KeplerMapConfig;
75
+ const {filters, mapState, mapStyle, popupSettings, legendSettings} = config;
76
+ const {layers, layerBlending, interactionConfig} = config.visState;
143
77
 
144
- const layersReverse = [...layers].reverse();
145
78
  return {
146
79
  id: json.id,
147
80
  title: json.title,
@@ -154,24 +87,57 @@ export function parseMap(json: any) {
154
87
  popupSettings,
155
88
  legendSettings,
156
89
  token,
157
- layers: layersReverse.map((layer: MapConfigLayer) => {
158
- try {
159
- const {dataId} = layer.config;
160
- const dataset: Dataset | null = datasets.find(
161
- (d: any) => d.id === dataId
162
- );
163
- assert(dataset, `No dataset matching dataId: ${dataId}`);
164
- const layerDescriptor = getLayerDescriptor({
165
- mapConfig,
166
- layer,
167
- dataset,
168
- });
169
- return layerDescriptor;
170
- } catch (e: any) {
171
- console.error(e.message);
172
- return undefined;
173
- }
174
- }),
90
+ layers: layers
91
+ .reverse()
92
+ .map(({id, type, config, visualChannels}: MapConfigLayer) => {
93
+ try {
94
+ const {dataId} = config;
95
+ const dataset: Dataset | null = datasets.find(
96
+ (d: any) => d.id === dataId
97
+ );
98
+ assert(dataset, `No dataset matching dataId: ${dataId}`);
99
+ const {data} = dataset;
100
+ assert(data, `No data loaded for dataId: ${dataId}`);
101
+
102
+ const {propMap, defaultProps} = getLayerProps(type, config, dataset);
103
+
104
+ const styleProps = createStyleProps(config, propMap);
105
+
106
+ const {channelProps, scales} = createChannelProps(
107
+ id,
108
+ type,
109
+ config,
110
+ visualChannels,
111
+ data,
112
+ dataset
113
+ );
114
+ const layer: LayerDescriptor = {
115
+ type,
116
+ filters:
117
+ isEmptyObject(filters) || isRemoteCalculationSupported(dataset)
118
+ ? undefined
119
+ : filters[dataId],
120
+ props: {
121
+ id,
122
+ data,
123
+ ...defaultProps,
124
+ ...createInteractionProps(interactionConfig),
125
+ ...styleProps,
126
+ ...channelProps,
127
+ ...createParametersProp(
128
+ layerBlending,
129
+ styleProps.parameters || {}
130
+ ), // Must come after style
131
+ ...createLoadOptions(token),
132
+ },
133
+ scales,
134
+ };
135
+ return layer;
136
+ } catch (e: any) {
137
+ console.error(e.message);
138
+ return undefined;
139
+ }
140
+ }),
175
141
  };
176
142
  }
177
143
 
@@ -238,7 +204,7 @@ function createStyleProps(config: MapLayerConfig, mapping: any) {
238
204
  if (Array.isArray(result[colorAccessor])) {
239
205
  const color = [...result[colorAccessor]];
240
206
  const opacityKey = OPACITY_MAP[colorAccessor];
241
- const opacity = config.visConfig[opacityKey as keyof VisConfig] as number;
207
+ const opacity = config.visConfig[opacityKey as keyof VisConfig];
242
208
  color[3] = opacityToAlpha(opacity);
243
209
  result[colorAccessor] = color;
244
210
  }
@@ -250,75 +216,62 @@ function createStyleProps(config: MapLayerConfig, mapping: any) {
250
216
  return result;
251
217
  }
252
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
+
253
235
  function createChannelProps(
254
236
  id: string,
255
237
  layerType: LayerType,
256
238
  config: MapLayerConfig,
257
239
  visualChannels: VisualChannels,
258
- data: TilejsonResult,
240
+ data: any,
259
241
  dataset: Dataset
260
- ): {
261
- channelProps: Record<string, any>;
262
- scales: Partial<Record<ScaleKey, Scale>>;
263
- } {
264
- if (layerType === 'raster') {
265
- const rasterMetadata = data.raster_metadata;
266
- if (!rasterMetadata) {
267
- return {
268
- channelProps: {},
269
- scales: {},
270
- };
271
- }
272
- const rasterStyleType = config.visConfig.rasterStyleType;
273
- if (rasterStyleType === 'Rgb') {
274
- return {
275
- channelProps: getRasterTileLayerStylePropsRgb({
276
- layerConfig: config,
277
- rasterMetadata,
278
- visualChannels,
279
- }),
280
- scales: {}, // TODO
281
- };
282
- } else {
283
- return {
284
- channelProps: getRasterTileLayerStylePropsScaledBand({
285
- layerConfig: config,
286
- visualChannels,
287
- rasterMetadata,
288
- }),
289
- scales: {
290
- // TODO
291
- },
292
- };
293
- }
294
- }
242
+ ): {channelProps: Record<string, any>; scales: Record<ScaleKey, Scale>} {
243
+ const {
244
+ colorField,
245
+ colorScale,
246
+ radiusField,
247
+ radiusScale,
248
+ strokeColorField,
249
+ strokeColorScale,
250
+ weightField,
251
+ } = visualChannels;
252
+ const {heightField, heightScale} = visualChannels;
295
253
  const {textLabel, visConfig} = config;
296
254
  const result: Record<string, any> = {};
297
- const updateTriggers: Record<string, any> = {};
298
255
 
299
256
  const scales: Record<string, Scale> = {};
300
257
 
301
- // fill color
302
- {
303
- const {colorField, colorScale} = visualChannels;
304
- const {colorRange, colorAggregation} = visConfig;
305
- if (colorField && colorScale && colorRange) {
306
- const {accessor, ...scaleProps} = getColorAccessor(
307
- colorField,
308
- colorScale,
309
- {aggregation: colorAggregation, range: colorRange},
310
- visConfig.opacity,
311
- data
312
- );
313
- result.getFillColor = accessor;
314
- scales.fillColor = updateTriggers.getFillColor = {
315
- field: colorField,
316
- type: colorScale,
317
- ...scaleProps,
318
- };
319
- } else {
320
- scales.fillColor = {} as any;
321
- }
258
+ if (colorField) {
259
+ const {colorAggregation: aggregation, colorRange: range} = visConfig;
260
+ const {accessor, scale} = getColorAccessor(
261
+ colorField,
262
+ colorScale!,
263
+ {aggregation, range},
264
+ visConfig.opacity,
265
+ data
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;
322
275
  }
323
276
 
324
277
  if (layerType === 'clusterTile') {
@@ -335,8 +288,6 @@ function createChannelProps(
335
288
  return d.properties[aggregationExpAlias];
336
289
  };
337
290
 
338
- updateTriggers.getWeight = aggregationExpAlias;
339
-
340
291
  result.getPointRadius = (d: any, info: any) => {
341
292
  return calculateClusterRadius(
342
293
  d.properties,
@@ -345,10 +296,6 @@ function createChannelProps(
345
296
  aggregationExpAlias
346
297
  );
347
298
  };
348
- updateTriggers.getPointRadius = {
349
- aggregationExpAlias,
350
- radiusRange: visConfig.radiusRange,
351
- };
352
299
 
353
300
  result.textCharacterSet = 'auto';
354
301
  result.textFontFamily = 'Inter, sans';
@@ -358,8 +305,6 @@ function createChannelProps(
358
305
  result.getText = (d: any) =>
359
306
  TEXT_NUMBER_FORMATTER.format(d.properties[aggregationExpAlias]);
360
307
 
361
- updateTriggers.getText = aggregationExpAlias;
362
-
363
308
  result.getTextColor = config.textLabel[TEXT_LABEL_INDEX].color;
364
309
  result.textOutlineColor = [
365
310
  ...(config.textLabel[TEXT_LABEL_INDEX].outlineColor as number[]),
@@ -377,122 +322,73 @@ function createChannelProps(
377
322
  );
378
323
  return calculateClusterTextFontSize(radius);
379
324
  };
380
-
381
- updateTriggers.getTextSize = {
382
- aggregationExpAlias,
383
- radiusRange: visConfig.radiusRange,
384
- };
385
- }
386
-
387
- // point radius
388
- {
389
- const radiusRange = visConfig.radiusRange;
390
- const {radiusField, radiusScale} = visualChannels;
391
- if (radiusField && radiusRange && radiusScale) {
392
- const {accessor, ...scaleProps} = getSizeAccessor(
393
- radiusField,
394
- radiusScale,
395
- visConfig.sizeAggregation,
396
- radiusRange,
397
- data
398
- );
399
- result.getPointRadius = accessor;
400
- scales.pointRadius = updateTriggers.getPointRadius = {
401
- field: radiusField,
402
- type: radiusScale,
403
- ...scaleProps,
404
- };
405
- }
406
325
  }
407
326
 
408
- // stroke/ouline color
409
- {
410
- const strokeColorRange = visConfig.strokeColorRange;
411
- const {strokeColorScale, strokeColorField} = visualChannels;
412
- if (strokeColorField && strokeColorRange && strokeColorScale) {
413
- const {strokeColorAggregation: aggregation} = visConfig;
414
- const opacity =
415
- visConfig.strokeOpacity !== undefined ? visConfig.strokeOpacity : 1;
416
-
417
- const {accessor, ...scaleProps} = getColorAccessor(
418
- strokeColorField,
419
- strokeColorScale,
420
- {aggregation, range: strokeColorRange},
421
- opacity,
422
- data
423
- );
424
- result.getLineColor = accessor;
425
- scales.lineColor = updateTriggers.getLineColor = {
426
- field: strokeColorField,
427
- type: strokeColorScale,
428
- ...scaleProps,
429
- };
430
- }
327
+ if (radiusField) {
328
+ const {accessor, scale} = getSizeAccessor(
329
+ radiusField,
330
+ radiusScale,
331
+ visConfig.sizeAggregation,
332
+ visConfig.radiusRange || visConfig.sizeRange,
333
+ data
334
+ );
335
+ result.getPointRadius = accessor;
336
+ scales.pointRadius = {
337
+ field: radiusField,
338
+ type: radiusScale || 'identity',
339
+ ...domainAndRangeFromScale(scale),
340
+ };
431
341
  }
432
342
 
433
- // stroke/line width
434
- {
435
- const {sizeField: strokeWidthField, sizeScale: strokeWidthScale} =
436
- visualChannels;
437
- const {sizeRange, sizeAggregation} = visConfig;
438
-
439
- if (strokeWidthField && sizeRange) {
440
- const {accessor, ...scaleProps} = getSizeAccessor(
441
- strokeWidthField,
442
- strokeWidthScale,
443
- sizeAggregation,
444
- sizeRange,
445
- data
446
- );
447
- result.getLineWidth = accessor;
448
- scales.lineWidth = updateTriggers.getLineWidth = {
449
- field: strokeWidthField,
450
- type: strokeWidthScale || 'identity',
451
- ...scaleProps,
452
- };
453
- }
343
+ if (strokeColorField) {
344
+ const opacity =
345
+ visConfig.strokeOpacity !== undefined ? visConfig.strokeOpacity : 1;
346
+ const {strokeColorAggregation: aggregation, strokeColorRange: range} =
347
+ visConfig;
348
+ const {accessor, scale} = getColorAccessor(
349
+ strokeColorField,
350
+ strokeColorScale!,
351
+ {aggregation, range},
352
+ opacity,
353
+ data
354
+ );
355
+ result.getLineColor = accessor;
356
+ scales.lineColor = {
357
+ field: strokeColorField,
358
+ type: strokeColorScale!,
359
+ ...domainAndRangeFromScale(scale),
360
+ };
454
361
  }
455
-
456
- // height / elevation
457
- {
458
- const {enable3d, heightRange} = visConfig;
459
- const {heightField, heightScale} = visualChannels;
460
- if (heightField && heightRange && enable3d) {
461
- const {accessor, ...scaleProps} = getSizeAccessor(
462
- heightField,
463
- heightScale,
464
- visConfig.heightAggregation,
465
- heightRange,
466
- data
467
- );
468
- result.getElevation = accessor;
469
- scales.elevation = updateTriggers.getElevation = {
470
- field: heightField,
471
- type: heightScale || 'identity',
472
- ...scaleProps,
473
- };
474
- }
362
+ if (heightField && visConfig.enable3d) {
363
+ const {accessor, scale} = getSizeAccessor(
364
+ heightField,
365
+ heightScale,
366
+ visConfig.heightAggregation,
367
+ visConfig.heightRange || visConfig.sizeRange,
368
+ data
369
+ );
370
+ result.getElevation = accessor;
371
+ scales.elevation = {
372
+ field: heightField,
373
+ type: heightScale || 'identity',
374
+ ...domainAndRangeFromScale(scale),
375
+ };
475
376
  }
476
377
 
477
- // weight
478
- {
479
- const {weightField} = visualChannels;
480
- const {weightAggregation} = visConfig;
481
- if (weightField && weightAggregation) {
482
- const {accessor, ...scaleProps} = getSizeAccessor(
483
- weightField,
484
- undefined,
485
- weightAggregation,
486
- undefined,
487
- data
488
- );
489
- result.getWeight = accessor;
490
- scales.weight = updateTriggers.getWeight = {
491
- field: weightField,
492
- type: 'identity' as ScaleType,
493
- ...scaleProps,
494
- };
495
- }
378
+ if (weightField) {
379
+ const {accessor, scale} = getSizeAccessor(
380
+ weightField,
381
+ undefined,
382
+ visConfig.weightAggregation,
383
+ undefined,
384
+ data
385
+ );
386
+ result.getWeight = accessor;
387
+ scales.weight = {
388
+ field: weightField,
389
+ type: 'identity',
390
+ ...domainAndRangeFromScale(scale),
391
+ };
496
392
  }
497
393
 
498
394
  if (visConfig.customMarkers) {
@@ -511,12 +407,6 @@ function createChannelProps(
511
407
  {fallbackUrl: customMarkersUrl, maxIconSize, useMaskedIcons},
512
408
  data
513
409
  );
514
- updateTriggers.getIcon = {
515
- customMarkersUrl,
516
- customMarkersRange,
517
- maxIconSize,
518
- useMaskedIcons,
519
- };
520
410
  result._subLayerProps = {
521
411
  'points-icon': {
522
412
  loadOptions: {
@@ -534,12 +424,10 @@ function createChannelProps(
534
424
 
535
425
  if (getFillColor && useMaskedIcons) {
536
426
  result.getIconColor = getFillColor;
537
- updateTriggers.getIconColor = updateTriggers.getFillColor;
538
427
  }
539
428
 
540
429
  if (getPointRadius) {
541
430
  result.getIconSize = getPointRadius;
542
- updateTriggers.getIconSize = updateTriggers.getPointRadius;
543
431
  }
544
432
 
545
433
  if (visualChannels.rotationField) {
@@ -551,7 +439,6 @@ function createChannelProps(
551
439
  data
552
440
  );
553
441
  result.getIconAngle = negateAccessor(accessor);
554
- updateTriggers.getIconAngle = updateTriggers.getRotationField;
555
442
  }
556
443
  } else if (layerType === 'tileset') {
557
444
  result.pointType = 'circle';
@@ -607,13 +494,7 @@ function createChannelProps(
607
494
  };
608
495
  }
609
496
 
610
- return {
611
- channelProps: {
612
- ...result,
613
- updateTriggers,
614
- },
615
- scales,
616
- };
497
+ return {channelProps: result, scales};
617
498
  }
618
499
 
619
500
  function createLoadOptions(accessToken: string) {
@@ -39,7 +39,6 @@ export type ColorRange = {
39
39
  colorMap: string[][] | undefined;
40
40
  name: string;
41
41
  type: string;
42
- uiCustomScaleType?: 'logarithmic';
43
42
  };
44
43
 
45
44
  export type CustomMarkersRange = {
@@ -50,20 +49,12 @@ export type CustomMarkersRange = {
50
49
  othersMarker?: string;
51
50
  };
52
51
 
53
- export type ColorBand = 'red' | 'green' | 'blue' | 'alpha';
54
-
55
- export type RasterLayerConfigColorBand = {
56
- band: ColorBand;
57
- type: 'none' | 'band' | 'expression';
58
- value: string; // band name or expression
59
- };
60
-
61
52
  export type VisConfig = {
62
53
  filled?: boolean;
63
54
  opacity?: number;
64
55
  enable3d?: boolean;
65
56
 
66
- colorAggregation?: string;
57
+ colorAggregation?: any;
67
58
  colorRange: ColorRange;
68
59
 
69
60
  customMarkers?: boolean;
@@ -73,26 +64,21 @@ export type VisConfig = {
73
64
  radius: number;
74
65
  radiusRange?: number[];
75
66
 
76
- sizeAggregation?: string;
77
- sizeRange?: number[];
67
+ sizeAggregation?: any;
68
+ sizeRange?: any;
78
69
 
79
- strokeColorAggregation?: string;
70
+ strokeColorAggregation?: any;
80
71
  strokeOpacity?: number;
81
72
  strokeColorRange?: ColorRange;
82
73
 
83
- heightRange?: number[];
84
- heightAggregation?: string;
74
+ heightRange?: any;
75
+ heightAggregation?: any;
85
76
 
86
- weightAggregation?: string;
77
+ weightAggregation?: any;
87
78
 
88
79
  // type = clusterTile
89
80
  clusterLevel?: number;
90
81
  isTextVisible?: boolean;
91
-
92
- rasterStyleType?: 'Rgb' | 'ColorRange' | 'UniqueValues';
93
- colorBands?: RasterLayerConfigColorBand[];
94
-
95
- uniqueValuesColorRange?: ColorRange;
96
82
  };
97
83
 
98
84
  export type TextLabel = {
@@ -85,59 +85,3 @@ export function formatDate(value: string | number | Date): string {
85
85
  export function formatTimestamp(value: string | number | Date): string {
86
86
  return String(Math.floor(new Date(value).getTime() / 1000));
87
87
  }
88
-
89
- function roundedPow10(exp: number) {
90
- // Math.pow(10, less than 4) generates "0.0...009999999999999999" instead of "0.0...01"
91
- // round it ...
92
- const raw = Math.pow(10, exp);
93
- if (exp < 0) {
94
- const shift = Math.pow(10, -exp);
95
- return Math.round(raw * shift) / shift;
96
- }
97
- return raw;
98
- }
99
-
100
- /**
101
- * Create domain for D3 threshold scale with logarithmic steps.
102
- *
103
- * If min is 0, it starts with max and goes down to fill color scale.
104
- * If max is Infinity, it starts with 10 and goes up to fill color scale.
105
- * Othersise it starts on first power of 10 that is greater than min.
106
- *
107
- * Generates `steps-1` entries, as this is what d3 threshold scale expects
108
- *
109
- * @see https://d3js.org/d3-scale/threshold
110
- */
111
- export function getLog10ScaleSteps({
112
- min,
113
- max,
114
- steps,
115
- }: {
116
- min: number;
117
- max: number;
118
- steps: number;
119
- }): number[] {
120
- if (min === 0) {
121
- if (max === Infinity) {
122
- // count aggregations have [0, Infinity]
123
- // that will yield [10, 100, 1000, ...]
124
- return [...Array(steps - 1)].map((_v, i) => roundedPow10(i + 1));
125
- }
126
- // if stats.min = 0, we only can attempt to start from max and decrease powers until
127
- // we use all color buckets ...
128
- const maxLog = Math.log10(max);
129
- const endExponent = Math.ceil(maxLog);
130
- const startExponent = endExponent - steps + 1;
131
- return [...Array(steps - 1)].map((_v, i) =>
132
- roundedPow10(startExponent + i)
133
- );
134
- } else {
135
- const minLog = Math.log10(min);
136
- const startExponent =
137
- Math.ceil(minLog) === minLog ? minLog + 1 : Math.ceil(minLog);
138
-
139
- return [...Array(steps - 1)].map((_v, i) =>
140
- roundedPow10(startExponent + i)
141
- );
142
- }
143
- }