@carto/kepler.gl 0.0.33 → 0.0.34-alpha.0

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.
@@ -28,7 +28,8 @@ import {
28
28
  scaleOrdinal,
29
29
  scaleSqrt,
30
30
  scaleLog,
31
- scalePoint
31
+ scalePoint,
32
+ scaleThreshold
32
33
  } from 'd3-scale';
33
34
  import {
34
35
  Layers,
@@ -306,7 +307,8 @@ export const SCALE_TYPES = keyMirror({
306
307
  log: null,
307
308
 
308
309
  // ordinal domain to linear range
309
- point: null
310
+ point: null,
311
+ custom: null
310
312
  });
311
313
 
312
314
  export const SCALE_FUNC = {
@@ -316,7 +318,8 @@ export const SCALE_FUNC = {
316
318
  [SCALE_TYPES.ordinal]: scaleOrdinal,
317
319
  [SCALE_TYPES.sqrt]: scaleSqrt,
318
320
  [SCALE_TYPES.log]: scaleLog,
319
- [SCALE_TYPES.point]: scalePoint
321
+ [SCALE_TYPES.point]: scalePoint,
322
+ [SCALE_TYPES.custom]: scaleThreshold
320
323
  };
321
324
 
322
325
  export const ALL_FIELD_TYPES = keyMirror({
@@ -449,20 +452,20 @@ export const AGGREGATION_TYPES = {
449
452
  };
450
453
 
451
454
  export const linearFieldScaleFunctions = {
452
- [CHANNEL_SCALES.color]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile],
455
+ [CHANNEL_SCALES.color]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom],
453
456
  [CHANNEL_SCALES.radius]: [SCALE_TYPES.sqrt],
454
457
  [CHANNEL_SCALES.size]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log]
455
458
  };
456
459
 
457
460
  export const linearFieldAggrScaleFunctions = {
458
461
  [CHANNEL_SCALES.colorAggr]: {
459
- [AGGREGATION_TYPES.average]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile],
460
- [AGGREGATION_TYPES.maximum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile],
461
- [AGGREGATION_TYPES.minimum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile],
462
- [AGGREGATION_TYPES.median]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile],
463
- [AGGREGATION_TYPES.stdev]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile],
464
- [AGGREGATION_TYPES.sum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile],
465
- [AGGREGATION_TYPES.variance]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile]
462
+ [AGGREGATION_TYPES.average]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom],
463
+ [AGGREGATION_TYPES.maximum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom],
464
+ [AGGREGATION_TYPES.minimum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom],
465
+ [AGGREGATION_TYPES.median]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom],
466
+ [AGGREGATION_TYPES.stdev]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom],
467
+ [AGGREGATION_TYPES.sum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom],
468
+ [AGGREGATION_TYPES.variance]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom]
466
469
  },
467
470
 
468
471
  [CHANNEL_SCALES.sizeAggr]: {
@@ -486,7 +489,11 @@ export const ordinalFieldAggrScaleFunctions = {
486
489
  // [CHANNEL_SCALES.colorAggr]: [SCALE_TYPES.ordinal, SCALE_TYPES.linear],
487
490
  [CHANNEL_SCALES.colorAggr]: {
488
491
  [AGGREGATION_TYPES.mode]: [SCALE_TYPES.ordinal],
489
- [AGGREGATION_TYPES.countUnique]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile]
492
+ [AGGREGATION_TYPES.countUnique]: [
493
+ SCALE_TYPES.quantize,
494
+ SCALE_TYPES.quantile,
495
+ SCALE_TYPES.custom
496
+ ]
490
497
  },
491
498
 
492
499
  // Currently doesn't support yet
@@ -509,7 +516,7 @@ export const notSupportAggrOpts = {
509
516
  */
510
517
  export const DEFAULT_AGGREGATION = {
511
518
  [CHANNEL_SCALES.colorAggr]: {
512
- [AGGREGATION_TYPES.count]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile]
519
+ [AGGREGATION_TYPES.count]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile, SCALE_TYPES.custom]
513
520
  },
514
521
  [CHANNEL_SCALES.sizeAggr]: {
515
522
  [AGGREGATION_TYPES.count]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log]
@@ -629,6 +636,7 @@ export const DEFAULT_LAYER_COLOR = {
629
636
  export const DEFAULT_TOOLTIP_FIELDS = [];
630
637
 
631
638
  export const NO_VALUE_COLOR = [0, 0, 0, 0];
639
+ export const UNKNOWN_COLOR = [134, 141, 145];
632
640
 
633
641
  export const LAYER_BLENDINGS = {
634
642
  additive: {
@@ -66,7 +66,8 @@ export {
66
66
  SPEED_CONTROL_RANGE,
67
67
  RESOLUTIONS,
68
68
  SORT_ORDER,
69
- MAP_CONTROLS
69
+ MAP_CONTROLS,
70
+ UNKNOWN_COLOR
70
71
  } from './default-settings';
71
72
 
72
73
  export {BUG_REPORT_LINK, USER_GUIDE_DOC} from './user-guides';
@@ -23,7 +23,12 @@ import {AGGREGATION_OPERATION, _BinSorter as BinSorter} from '@deck.gl/aggregati
23
23
  import {console as Console} from 'global/window';
24
24
 
25
25
  import {aggregate} from 'utils/aggregate-utils';
26
- import {AGGREGATION_TYPES, SCALE_FUNC} from 'constants/default-settings';
26
+ import {
27
+ AGGREGATION_TYPES,
28
+ SCALE_FUNC,
29
+ UNKNOWN_COLOR,
30
+ SCALE_TYPES
31
+ } from 'constants/default-settings';
27
32
 
28
33
  export const DECK_AGGREGATION_MAP = {
29
34
  [AGGREGATION_OPERATION.SUM]: AGGREGATION_TYPES.sum,
@@ -97,7 +102,10 @@ export function getDimensionValueDomain(step, props, dimensionUpdater) {
97
102
  [props[lowerPercentile.prop], props[upperPercentile.prop]]
98
103
  );
99
104
 
100
- this._setDimensionState(key, {valueDomain});
105
+ this._setDimensionState(key, {
106
+ // By default sortedBins order by ascending frecuency but if we've an ordinal scale we want to order by descending frecuency
107
+ valueDomain: props[scaleType.prop] === SCALE_TYPES.ordinal ? valueDomain.reverse() : valueDomain
108
+ });
101
109
  }
102
110
 
103
111
  export function getDimensionScale(step, props, dimensionUpdater) {
@@ -112,12 +120,20 @@ export function getDimensionScale(step, props, dimensionUpdater) {
112
120
  const dimensionRange = props[range.prop];
113
121
  const dimensionDomain = props[domain.prop] || this.state.dimensions[key].valueDomain;
114
122
 
115
- const scaleFunctor = getScaleFunctor(scaleType && props[scaleType.prop])();
123
+ const scaleTypeProp = scaleType && props[scaleType.prop];
124
+ const scaleFunctor = getScaleFunctor(scaleTypeProp)();
116
125
 
117
- const scaleFunc = scaleFunctor.domain(dimensionDomain).range(dimensionRange);
126
+ const scaleFunc = scaleFunctor
127
+ .domain(
128
+ scaleTypeProp === SCALE_TYPES.ordinal
129
+ ? dimensionDomain.slice(0, dimensionRange.length)
130
+ : dimensionDomain
131
+ )
132
+ .range(dimensionRange)
133
+ .unknown(UNKNOWN_COLOR);
118
134
 
119
135
  if (typeof onSet === 'object' && typeof props[onSet.props] === 'function') {
120
- props[onSet.props](scaleFunc.domain());
136
+ props[onSet.props](this.state.dimensions[key].valueDomain);
121
137
  }
122
138
  this._setDimensionState(key, {scaleFunc});
123
139
  }
@@ -180,12 +196,13 @@ function getSubLayerAccessor(dimensionState, dimension, layerProps) {
180
196
  }
181
197
 
182
198
  const cv = bin && bin.value;
183
- const domain = scaleFunc.domain();
199
+ // const domain = scaleFunc.domain();
184
200
 
185
- const isValueInDomain = cv >= domain[0] && cv <= domain[domain.length - 1];
201
+ // const isValueInDomain = cv >= domain[0] && cv <= domain[domain.length - 1];
186
202
 
187
203
  // if cell value is outside domain, set alpha to 0
188
- return isValueInDomain ? scaleFunc(cv) : dimension.nullValue;
204
+ // return isValueInDomain ? scaleFunc(cv) : dimension.nullValue;
205
+ return scaleFunc(cv);
189
206
  };
190
207
  }
191
208
 
@@ -53,7 +53,23 @@ export const getValueAggrFunc = (field, aggregation) => {
53
53
  export const getFilterDataFunc = (filterRange, getFilterValue) => pt =>
54
54
  getFilterValue(pt).every((val, i) => val >= filterRange[i][0] && val <= filterRange[i][1]);
55
55
 
56
- const getLayerColorRange = colorRange => colorRange.colors.map(hexToRgb);
56
+ const getLayerColorRange = colorRange => {
57
+ const {colorMap} = colorRange;
58
+
59
+ if (colorMap) {
60
+ return colorMap.map(cMap => hexToRgb(cMap[1]));
61
+ }
62
+ return colorRange.colors.map(hexToRgb);
63
+ };
64
+
65
+ const getLayerColorDomain = colorDomain => {
66
+ const {colorMap} = colorDomain;
67
+
68
+ if (colorMap) {
69
+ return colorMap.map(cMap => cMap[0]);
70
+ }
71
+ return null;
72
+ };
57
73
 
58
74
  export const aggregateRequiredColumns = ['lat', 'lng'];
59
75
 
@@ -64,6 +80,7 @@ export default class AggregationLayer extends Layer {
64
80
  this.getPositionAccessor = dataContainer =>
65
81
  pointPosAccessor(this.config.columns)(dataContainer);
66
82
  this.getColorRange = memoize(getLayerColorRange);
83
+ this.getColorDomain = memoize(getLayerColorDomain);
67
84
  }
68
85
 
69
86
  get isAggregated() {
@@ -315,6 +332,7 @@ export default class AggregationLayer extends Layer {
315
332
 
316
333
  // color
317
334
  colorRange: this.getColorRange(visConfig.colorRange),
335
+ colorDomain: this.getColorDomain(visConfig.colorRange),
318
336
  colorScaleType: this.config.colorScale,
319
337
  upperPercentile: visConfig.percentile[1],
320
338
  lowerPercentile: visConfig.percentile[0],
@@ -35,7 +35,8 @@ import {
35
35
  FIELD_OPTS,
36
36
  SCALE_FUNC,
37
37
  CHANNEL_SCALE_SUPPORTED_FIELDS,
38
- MAX_GPU_FILTERS
38
+ MAX_GPU_FILTERS,
39
+ UNKNOWN_COLOR
39
40
  } from 'constants/default-settings';
40
41
  import {COLOR_RANGES} from 'constants/color-ranges';
41
42
  import {DataVizColors} from 'constants/custom-color-ranges';
@@ -43,7 +44,6 @@ import {
43
44
  LAYER_VIS_CONFIGS,
44
45
  DEFAULT_TEXT_LABEL,
45
46
  DEFAULT_COLOR_UI,
46
- UNKNOWN_COLOR_KEY,
47
47
  DEFAULT_HIGHLIGHT_COLOR,
48
48
  DEFAULT_LAYER_LABEL
49
49
  } from './layer-factory';
@@ -636,6 +636,7 @@ class Layer {
636
636
  });
637
637
  }
638
638
 
639
+ // eslint-disable-next-line complexity
639
640
  updateColorRangeByColorUI(newConfig, previous, prop) {
640
641
  // only update colorRange if changes in UI is made to 'reversed', 'steps' or steps
641
642
  const shouldUpdate =
@@ -664,6 +665,10 @@ class Layer {
664
665
  if (update && colorRange.reversed) {
665
666
  update = reverseColorRange(true, update);
666
667
  }
668
+
669
+ if (update && colorRange.colorMap) {
670
+ update = this.updateColorMap(prop, colorRange.colorMap, update);
671
+ }
667
672
  }
668
673
  }
669
674
 
@@ -676,6 +681,69 @@ class Layer {
676
681
  }
677
682
  }
678
683
 
684
+ updateColorMap(prop, colorMap, colorRange) {
685
+ // @ts-ignore
686
+ const {field, domain} = Object.values(this.visualChannels).find(value => value.range === prop);
687
+ const type = this.config[field]?.type;
688
+
689
+ if (type === 'string' || type === 'boolean' || type === 'date') {
690
+ return this.updateCategoryColorMap(colorMap, this.config[domain], colorRange);
691
+ }
692
+
693
+ // If no type or type is not string, boolean or date
694
+ return this.updateNumericColorMap(colorMap, this.config[domain], colorRange);
695
+ }
696
+
697
+ updateCategoryColorMap(colorMap, domain, colorRange) {
698
+ const colors = colorRange.colors;
699
+ const assignedCategories = [];
700
+ const newColorMap = colorMap.slice(0, colors.length).map((cMap, index) => {
701
+ assignedCategories.push(cMap[0]);
702
+ return [cMap[0], colors[index]];
703
+ });
704
+ const unassignedColors = colors.slice(newColorMap.length, colors.length);
705
+
706
+ for (const color of unassignedColors) {
707
+ const category = domain.find(d => !assignedCategories.includes(d));
708
+ if (!category) {
709
+ break;
710
+ }
711
+ assignedCategories.push(category);
712
+ newColorMap.push([category, color]);
713
+ }
714
+ return {
715
+ ...colorRange,
716
+ colorMap: newColorMap
717
+ };
718
+ }
719
+
720
+ updateNumericColorMap(colorMap, domain, update) {
721
+ const colors = update.colors;
722
+ let newColorMap = colorMap.slice(0, colors.length);
723
+
724
+ newColorMap = newColorMap.map((cMap, index) => {
725
+ const value = index === newColorMap.length - 1 ? null : cMap[0];
726
+ return [value, colors[index]];
727
+ });
728
+ const unassignedColors = colors.slice(newColorMap.length, colors.length);
729
+
730
+ if (unassignedColors.length > 0) {
731
+ const maxValue = domain[domain.length - 1];
732
+ const prevValue = newColorMap[newColorMap.length - 2][0];
733
+ const step = (maxValue - prevValue) / (unassignedColors.length + 1);
734
+ newColorMap[newColorMap.length - 1][0] = prevValue + step;
735
+
736
+ for (const [i, color] of unassignedColors.entries()) {
737
+ const value = i === unassignedColors.length - 1 ? null : prevValue + (step * (i + 2));
738
+ newColorMap.push([value, color]);
739
+ }
740
+ }
741
+ return {
742
+ ...update,
743
+ colorMap: newColorMap
744
+ };
745
+ }
746
+
679
747
  /**
680
748
  * Check whether layer has all columns
681
749
  * @returns {boolean} yes or no
@@ -724,10 +792,10 @@ class Layer {
724
792
  });
725
793
 
726
794
  // @ts-ignore d3 scale
727
- const scale = SCALE_FUNC[SCALE_TYPES.ordinal]()
728
- .domain(cMap.keys())
729
- .range(cMap.values())
730
- .unknown(cMap.get(UNKNOWN_COLOR_KEY) || NO_VALUE_COLOR);
795
+ const scale = SCALE_FUNC[colorScale]()
796
+ .domain(Array.from(cMap.keys()))
797
+ .range(Array.from(cMap.values()))
798
+ .unknown(UNKNOWN_COLOR);
731
799
  return scale;
732
800
  }
733
801
 
@@ -795,6 +863,14 @@ class Layer {
795
863
  }
796
864
 
797
865
  getVisChannelScale(scale, domain, range, fixed) {
866
+ if (scale === SCALE_TYPES.ordinal) {
867
+ // @ts-ignore d3-scale type
868
+ return SCALE_FUNC[scale]()
869
+ .domain(domain.slice(0, range.length))
870
+ .range(range)
871
+ .unknown(UNKNOWN_COLOR);
872
+ }
873
+
798
874
  // @ts-ignore d3-scale type
799
875
  return SCALE_FUNC[fixed ? 'linear' : scale]()
800
876
  .domain(domain)
@@ -93,6 +93,7 @@ export default class ClusterLayer extends AggregationLayer {
93
93
 
94
94
  // color
95
95
  colorRange: this.getColorRange(visConfig.colorRange),
96
+ colorDomain: this.getColorDomain(visConfig.colorRange),
96
97
  colorScaleType: this.config.colorScale,
97
98
  colorAggregation: visConfig.colorAggregation,
98
99
 
@@ -20,6 +20,7 @@
20
20
 
21
21
  import {notNullorUndefined, unique} from './data-utils';
22
22
  import {extent} from 'd3-array';
23
+ import {uniqueSortedByFrequency} from './data-utils';
23
24
 
24
25
  /**
25
26
  * return quantile domain for an array of data
@@ -37,10 +38,7 @@ export function getQuantileDomain(data, valueAccessor, sortFunc) {
37
38
  */
38
39
  export function getOrdinalDomain(dataContainer, valueAccessor) {
39
40
  const values = dataContainer.mapIndex(valueAccessor);
40
-
41
- return unique(values)
42
- .filter(notNullorUndefined)
43
- .sort();
41
+ return uniqueSortedByFrequency(values);
44
42
  }
45
43
 
46
44
  /**
@@ -12,6 +12,7 @@ export function isNumber(d: any): boolean;
12
12
  export function isPlainObject(d: any): boolean;
13
13
  export function numberSort(a: any, b: any): any;
14
14
  export function unique(values: any[]): any[];
15
+ export function uniqueSortedByFrequency(values: any[]): any[];
15
16
  export function arrayMove(array: any[], from: number, to: number);
16
17
  export function getSortingFunction(fieldType: string): numberSort | undefined;
17
18
  export function preciseRound(num: number, decimals: number): string;
@@ -47,6 +47,20 @@ export function unique(values) {
47
47
  return results;
48
48
  }
49
49
 
50
+ export function uniqueSortedByFrequency(values) {
51
+ const frequency = values.reduce((res, value) => {
52
+ if (notNullorUndefined(value)) {
53
+ if (!res[value]) {
54
+ res[value] = 0;
55
+ }
56
+ res[value] += 1;
57
+ }
58
+ return res;
59
+ }, {});
60
+
61
+ return Object.keys(frequency).sort((a, b) => frequency[b] - frequency[a]);
62
+ }
63
+
50
64
  /* eslint-disable max-statements */
51
65
  /**
52
66
  * return center of map from given points