@carto/kepler.gl 0.0.71-alpha.4 → 0.0.71-alpha.6

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.
@@ -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,13 +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
1132
  fields,
1149
- handleCustomOnSelectHexColumn
1133
+ handleCustomOnSelectHexColumn = () => null
1150
1134
  } = this.props;
1135
+ const {range: property, field, scale} = layer.visualChannels[channel];
1151
1136
  return (
1152
1137
  <SidePanelSection>
1153
1138
  <PanelLabel>
@@ -1160,23 +1145,20 @@ export function LayerColorRangeSelectorFactory(ColorSelector) {
1160
1145
  selectedColor: layer.config.visConfig[property],
1161
1146
  isRange: true,
1162
1147
  setColor: colorRange => {
1163
- layer.updateLayerConfig({
1164
- [scale]: SCALE_TYPES.ordinal
1165
- });
1166
1148
  const {colorMap} = layer.config.visConfig[property];
1167
1149
  const colorRangeUpdated = colorMap
1168
1150
  ? layer.updateColorMap(property, colorMap, colorRange)
1169
1151
  : colorRange;
1170
1152
  onChange({[property]: colorRangeUpdated});
1153
+ layer.validateScale(channel); // Validate scale to ensure that it changes from `identity` to the correct one.
1171
1154
  }
1172
1155
  }
1173
1156
  ]}
1174
1157
  colorUI={layer.config.colorUI[property]}
1175
1158
  setColorUI={newConfig => setColorUI(property, newConfig)}
1176
1159
  onSelectHexColorColumn={colorColumn => {
1177
- if (handleCustomOnSelectHexColumn) {
1178
- handleCustomOnSelectHexColumn(layer, colorColumn);
1179
- }
1160
+ // It's defined in CN: see workspace-www/src/features/builder/ui/KeplerGl/SidePanel/LayerPanel/LayerColorRangeSelector.tsx
1161
+ handleCustomOnSelectHexColumn(layer, colorColumn);
1180
1162
 
1181
1163
  layer.updateLayerConfig({
1182
1164
  [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
  }
@@ -1276,13 +1287,36 @@ class Layer {
1276
1287
  if (channelConfig?.type === hexColor) {
1277
1288
  const dataSetField = dataset.fields.find(f => f.name === field.colorColumn);
1278
1289
  const colors = dataset.getColumnLayerDomain(dataSetField, SCALE_TYPES.ordinal);
1279
- this.updateLayerVisConfig({[channel.range]: {...channelConfig, colors}});
1290
+ this.updateLayerVisConfig({
1291
+ [channel.range]: {...channelConfig, colors: colors.slice(0, 20)}
1292
+ });
1280
1293
  }
1281
1294
  });
1282
1295
 
1283
1296
  return this;
1284
1297
  }
1285
1298
 
1299
+ calculateHexColumnLegend(datasets) {
1300
+ const dataset = this.getDataset(datasets);
1301
+ if (!datasets) {
1302
+ return;
1303
+ }
1304
+
1305
+ const legendSettings = Object.keys(this.visualChannels).reduce((legends, channel) => {
1306
+ const {range, field: channelField} = this.visualChannels[channel];
1307
+ const channelConfig = this.config.visConfig[range];
1308
+ const field = this.config[channelField];
1309
+
1310
+ if (channelConfig?.type === hexColor) {
1311
+ const dataSetField = dataset.fields.find(f => f.name === field.colorColumn);
1312
+ const legend = dataset.calculateLegend(field, dataSetField);
1313
+ legends[channel] = legend;
1314
+ }
1315
+ return legends;
1316
+ }, {});
1317
+ this.updateLayerLegend(legendSettings);
1318
+ }
1319
+
1286
1320
  hasHoveredObject(objectInfo) {
1287
1321
  return this.isLayerHovered(objectInfo) && objectInfo.object ? objectInfo.object : null;
1288
1322
  }
@@ -1374,7 +1408,7 @@ class Layer {
1374
1408
  {getPosition, getRadius, radiusScale, updateTriggers, sharedProps},
1375
1409
  renderOpts
1376
1410
  ) {
1377
- const {data, mapState} = renderOpts;
1411
+ const {data} = renderOpts;
1378
1412
  const {textLabel, visibilityByZoom} = this.config;
1379
1413
  const extensions = [...sharedProps.extensions, collisionFilterExtension];
1380
1414
 
@@ -1494,12 +1528,16 @@ class Layer {
1494
1528
  return () => null;
1495
1529
  }
1496
1530
 
1497
- isColoredByColorColumn(range) {
1531
+ getHexColumn(range) {
1498
1532
  const field = Object.values(this.visualChannels)?.find(value => value.range === range)?.field;
1499
1533
  if (field) {
1500
1534
  return this.config[field].colorColumn;
1501
1535
  }
1502
1536
  }
1537
+
1538
+ isColoredByColorColumn(range) {
1539
+ return this.config.visConfig[range]?.hexColor;
1540
+ }
1503
1541
  }
1504
1542
 
1505
1543
  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
  */