@carto/kepler.gl 0.0.71-alpha.3 → 0.0.71-alpha.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.
@@ -125,10 +125,6 @@ function ColorSelectorFactory(SingleColorPalette, ColorRangeSelector) {
125
125
  }
126
126
  };
127
127
 
128
- _onSelectColorColumn = column => {
129
- this.props.onSelectHexColorColumn(column);
130
- };
131
-
132
128
  _showDropdown = (e, i) => {
133
129
  e.stopPropagation();
134
130
  e.preventDefault();
@@ -177,7 +173,7 @@ function ColorSelectorFactory(SingleColorPalette, ColorRangeSelector) {
177
173
  <ColorRangeSelector
178
174
  selectedColorRange={colorSets[editing].selectedColor}
179
175
  onSelectColorRange={this._onSelectColor}
180
- onSelectColorColumn={this._onSelectColorColumn}
176
+ onSelectColorColumn={this.props.onSelectHexColorColumn}
181
177
  setColorPaletteUI={this.props.setColorUI}
182
178
  fields={this.props.fields}
183
179
  colorPaletteUI={colorUI}
@@ -177,8 +177,6 @@ export default function LayerConfiguratorFactory(
177
177
  channel={layer.visualChannels.color}
178
178
  {...layerChannelConfigProps}
179
179
  />
180
- </ConfigGroupCollapsibleContent>
181
- <ConfigGroupCollapsibleContent>
182
180
  <VisConfigSlider {...layer.visConfigSettings.opacity} {...visConfiguratorProps} />
183
181
  </ConfigGroupCollapsibleContent>
184
182
  </LayerConfigGroup>
@@ -192,11 +190,7 @@ export default function LayerConfiguratorFactory(
192
190
  disabled={Boolean(layer.config.visConfig.customMarkers)}
193
191
  >
194
192
  {layer.config.strokeColorField ? (
195
- <LayerColorRangeSelector
196
- {...visConfiguratorProps}
197
- property="strokeColorRange"
198
- field="strokeColorField"
199
- />
193
+ <LayerColorRangeSelector {...visConfiguratorProps} channel="strokeColor" />
200
194
  ) : (
201
195
  <LayerColorSelector
202
196
  {...visConfiguratorProps}
@@ -712,11 +706,7 @@ export default function LayerConfiguratorFactory(
712
706
  collapsible
713
707
  >
714
708
  {layer.config.strokeColorField ? (
715
- <LayerColorRangeSelector
716
- {...visConfiguratorProps}
717
- property="strokeColorRange"
718
- field="strokeColorField"
719
- />
709
+ <LayerColorRangeSelector {...visConfiguratorProps} channel="strokeColor" />
720
710
  ) : (
721
711
  <LayerColorSelector
722
712
  {...visConfiguratorProps}
@@ -899,11 +889,7 @@ export default function LayerConfiguratorFactory(
899
889
  collapsible
900
890
  >
901
891
  {layer.config.strokeColorField ? (
902
- <LayerColorRangeSelector
903
- {...visConfiguratorProps}
904
- property="strokeColorRange"
905
- field="strokeColorField"
906
- />
892
+ <LayerColorRangeSelector {...visConfiguratorProps} channel="strokeColor" />
907
893
  ) : (
908
894
  <LayerColorSelector
909
895
  {...visConfiguratorProps}
@@ -1141,12 +1127,12 @@ export function LayerColorRangeSelectorFactory(ColorSelector) {
1141
1127
  const {
1142
1128
  layer,
1143
1129
  onChange,
1144
- property = 'colorRange',
1145
- field = 'colorField',
1146
- scale = 'colorScale',
1130
+ channel = 'color',
1147
1131
  setColorUI,
1148
- fields
1132
+ fields,
1133
+ handleCustomOnSelectHexColumn = () => null
1149
1134
  } = this.props;
1135
+ const {range: property, field, scale} = layer.visualChannels[channel];
1150
1136
  return (
1151
1137
  <SidePanelSection>
1152
1138
  <PanelLabel>
@@ -1159,20 +1145,21 @@ export function LayerColorRangeSelectorFactory(ColorSelector) {
1159
1145
  selectedColor: layer.config.visConfig[property],
1160
1146
  isRange: true,
1161
1147
  setColor: colorRange => {
1162
- layer.updateLayerConfig({
1163
- [scale]: SCALE_TYPES.ordinal
1164
- });
1165
1148
  const {colorMap} = layer.config.visConfig[property];
1166
1149
  const colorRangeUpdated = colorMap
1167
1150
  ? layer.updateColorMap(property, colorMap, colorRange)
1168
1151
  : colorRange;
1169
1152
  onChange({[property]: colorRangeUpdated});
1153
+ layer.validateScale(channel); // Validate scale to ensure that it changes from `identity` to the correct one.
1170
1154
  }
1171
1155
  }
1172
1156
  ]}
1173
1157
  colorUI={layer.config.colorUI[property]}
1174
1158
  setColorUI={newConfig => setColorUI(property, newConfig)}
1175
1159
  onSelectHexColorColumn={colorColumn => {
1160
+ // It's defined in CN: see workspace-www/src/features/builder/ui/KeplerGl/SidePanel/LayerPanel/LayerColorRangeSelector.tsx
1161
+ handleCustomOnSelectHexColumn(layer, colorColumn);
1162
+
1176
1163
  layer.updateLayerConfig({
1177
1164
  [field]: {
1178
1165
  ...layer.config[field],
@@ -103,6 +103,9 @@ class Layer {
103
103
  // meta
104
104
  this.meta = {};
105
105
 
106
+ // legendSettings
107
+ this.legendSettings = {};
108
+
106
109
  // visConfigSettings
107
110
  this.visConfigSettings = {};
108
111
 
@@ -665,7 +668,8 @@ class Layer {
665
668
  type: visConfig[prop].type,
666
669
  steps: visConfig[prop].colors?.length,
667
670
  reversed: Boolean(visConfig[prop].reversed),
668
- hexColumn: this.isColoredByColorColumn(prop)
671
+ hexColumn: this.getHexColumn(prop),
672
+ isHexColumn: this.isColoredByColorColumn(prop)
669
673
  }
670
674
  }
671
675
  }
@@ -1057,6 +1061,10 @@ class Layer {
1057
1061
  this.meta = {...this.meta, ...meta};
1058
1062
  }
1059
1063
 
1064
+ updateLayerLegend(settings) {
1065
+ this.legendSettings = {...this.legendSettings, ...settings};
1066
+ }
1067
+
1060
1068
  getDataUpdateTriggers({filteredIndex, id, allData}) {
1061
1069
  const {columns} = this.config;
1062
1070
 
@@ -1111,7 +1119,7 @@ class Layer {
1111
1119
  updateVisualChannelFields(dataset) {
1112
1120
  for (const {field, range} of Object.values(this.visualChannels)) {
1113
1121
  const oldField = this.config[field];
1114
- const isUsingHexColorCode = this.config.visConfig[range].hexColor
1122
+ const isUsingHexColorCode = this.config.visConfig[range].hexColor;
1115
1123
  if (!oldField) {
1116
1124
  return;
1117
1125
  }
@@ -1194,8 +1202,11 @@ class Layer {
1194
1202
  */
1195
1203
  validateScale(channel) {
1196
1204
  const visualChannel = this.visualChannels[channel];
1197
- const {scale} = visualChannel;
1198
- if (!scale || this.config[scale] === SCALE_TYPES.identity) {
1205
+ const {scale, range} = visualChannel;
1206
+ if (
1207
+ !scale ||
1208
+ (this.isColoredByColorColumn(range) && this.config[scale] === SCALE_TYPES.identity)
1209
+ ) {
1199
1210
  // visualChannel doesn't have scale
1200
1211
  return;
1201
1212
  }
@@ -1283,6 +1294,27 @@ class Layer {
1283
1294
  return this;
1284
1295
  }
1285
1296
 
1297
+ calculateHexColumnLegend(datasets) {
1298
+ const dataset = this.getDataset(datasets);
1299
+ if (!datasets) {
1300
+ return;
1301
+ }
1302
+
1303
+ const legendSettings = Object.keys(this.visualChannels).reduce((legends, channel) => {
1304
+ const {range, field: channelField} = this.visualChannels[channel];
1305
+ const channelConfig = this.config.visConfig[range];
1306
+ const field = this.config[channelField];
1307
+
1308
+ if (channelConfig?.type === hexColor) {
1309
+ const dataSetField = dataset.fields.find(f => f.name === field.colorColumn);
1310
+ const legend = dataset.calculateLegend(field, dataSetField);
1311
+ legends[channel] = legend;
1312
+ }
1313
+ return legends;
1314
+ }, {});
1315
+ this.updateLayerLegend(legendSettings);
1316
+ }
1317
+
1286
1318
  hasHoveredObject(objectInfo) {
1287
1319
  return this.isLayerHovered(objectInfo) && objectInfo.object ? objectInfo.object : null;
1288
1320
  }
@@ -1374,7 +1406,7 @@ class Layer {
1374
1406
  {getPosition, getRadius, radiusScale, updateTriggers, sharedProps},
1375
1407
  renderOpts
1376
1408
  ) {
1377
- const {data, mapState} = renderOpts;
1409
+ const {data} = renderOpts;
1378
1410
  const {textLabel, visibilityByZoom} = this.config;
1379
1411
  const extensions = [...sharedProps.extensions, collisionFilterExtension];
1380
1412
 
@@ -1494,12 +1526,16 @@ class Layer {
1494
1526
  return () => null;
1495
1527
  }
1496
1528
 
1497
- isColoredByColorColumn(range) {
1529
+ getHexColumn(range) {
1498
1530
  const field = Object.values(this.visualChannels)?.find(value => value.range === range)?.field;
1499
1531
  if (field) {
1500
1532
  return this.config[field].colorColumn;
1501
1533
  }
1502
1534
  }
1535
+
1536
+ isColoredByColorColumn(range) {
1537
+ return this.config.visConfig[range]?.hexColor;
1538
+ }
1503
1539
  }
1504
1540
 
1505
1541
  export default Layer;
@@ -92,6 +92,8 @@ export class Layer {
92
92
  hasInteractivity: boolean;
93
93
  hasAllColumns(): boolean;
94
94
  updateLayerConfig(p: Partial<LayerConfig>): Layer;
95
+ updateLayerMeta(p: any): Layer
96
+ updateLayerLegend(p: any): Layer
95
97
  updateLayerDomain(datasets: Datasets, filter?: Filter): Layer;
96
98
  updateLayerVisualChannel(dataset: KeplerTable, channel: string): Layer;
97
99
  shouldCalculateLayerData(props: string[]): boolean;
@@ -100,7 +102,7 @@ export class Layer {
100
102
  isValidToSave(): boolean;
101
103
  validateVisualChannel(channel: string);
102
104
  getVisualChannelDescription(key: string): {label: string, measure: string};
103
-
105
+ calculateHexColumnLegend(datasets: Datasets): any;
104
106
  static findDefaultLayerProps(dataset: KeplerTable, foundLayers?: any[]);
105
107
  // static findDefaultColumnField(defaultFields, allFields)
106
108
  }
@@ -365,6 +365,25 @@ class VisualChannelSchemaV1 extends Schema {
365
365
  };
366
366
  }
367
367
  }
368
+ /**
369
+ * V1: save [channel]: [{ label, color }, ...] for legend
370
+ */
371
+ class LegendSettingsV1 extends Schema {
372
+ save(legendSettings, parents) {
373
+ return {
374
+ [this.key]: Object.keys(legendSettings).reduce((accu, key) => {
375
+ accu[key] = legendSettings[key];
376
+ return accu;
377
+ }, {})
378
+ };
379
+ }
380
+ load(ls, parents, accumulator) {
381
+ return {
382
+ ...accumulator,
383
+ [this.key]: ls
384
+ };
385
+ }
386
+ }
368
387
  const visConfigModificationV1 = {
369
388
  point: (visConfig, parents, accumulated) => {
370
389
  const modified = {};
@@ -459,6 +478,10 @@ export const layerPropsV1 = {
459
478
  })
460
479
  }
461
480
  }),
481
+ legendSettings: new LegendSettingsV1({
482
+ version: VERSIONS.v1,
483
+ key: 'legendSettings'
484
+ }),
462
485
  visualChannels: new VisualChannelSchemaV1({
463
486
  version: VERSIONS.v1,
464
487
  key: 'visualChannels'
@@ -13,6 +13,7 @@ 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
15
  export function uniqueSortedByFrequency(values: any[]): any[];
16
+ export function uniqueSortedPairByFrequency(values: any[]): any[];
16
17
  export function arrayMove(array: any[], from: number, to: number);
17
18
  export function getSortingFunction(fieldType: string): numberSort | undefined;
18
19
  export function preciseRound(num: number, decimals: number): string;
@@ -426,3 +426,22 @@ export function datetimeFormatter(timezone) {
426
426
  .format(format)
427
427
  : format => ts => moment.utc(ts).format(format);
428
428
  }
429
+
430
+ export function uniqueSortedPairByFrequency(values) {
431
+ const frequencies = values.reduce((res, value) => {
432
+ if (notNullorUndefined(value)) {
433
+ if (!res[value]) {
434
+ res[value] = {
435
+ key: value,
436
+ frequency: 1
437
+ };
438
+ }
439
+ res[value].frequency += 1;
440
+ }
441
+ return res;
442
+ }, {});
443
+
444
+ return Object.values(frequencies)
445
+ .sort((a, b) => b.frequency - a.frequency)
446
+ .map(({key: [left, right]}) => ({left, right}));
447
+ }
@@ -70,6 +70,7 @@ export function calculateLayerData(layer, state, oldLayerData) {
70
70
  }
71
71
 
72
72
  const layerData = layer.formatLayerData(state.datasets, oldLayerData);
73
+ layer.calculateHexColumnLegend(state.datasets);
73
74
  return {layerData, layer};
74
75
  }
75
76
 
@@ -45,6 +45,7 @@ import {
45
45
  import {ALL_FIELD_TYPES, SCALE_TYPES} from 'constants/default-settings';
46
46
 
47
47
  import {createDataContainer} from './data-container-utils';
48
+ import {uniqueSortedPairByFrequency} from 'utils/data-utils';
48
49
 
49
50
  // Unique identifier of each field
50
51
  const FID_KEY = 'name';
@@ -342,6 +343,17 @@ class KeplerTable {
342
343
  }
343
344
  }
344
345
 
346
+ calculateLegend(leftField, rightField) {
347
+ const leftValues = this.dataContainer.mapIndex(leftField.valueAccessor);
348
+ const rightValues = this.dataContainer.mapIndex(rightField.valueAccessor);
349
+
350
+ const pairing = leftValues.reduce((previous, current, idx) => {
351
+ previous.push([current, rightValues[idx]]);
352
+ return previous;
353
+ }, []);
354
+ return uniqueSortedPairByFrequency(pairing).map(row => ({label: row.left, color: row.right}));
355
+ }
356
+
345
357
  /**
346
358
  * Get a sample of rows to calculate layer boundaries
347
359
  */