@carto/kepler.gl 0.0.65-alpha.labels → 0.0.65-alpha.labels.11

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
@@ -2,7 +2,7 @@
2
2
  "name": "@carto/kepler.gl",
3
3
  "author": "CARTO",
4
4
  "homepage": "https://github.com/CartoDB/carto-kepler#readme",
5
- "version": "0.0.65-alpha.labels",
5
+ "version": "0.0.65-alpha.labels.11",
6
6
  "description": "kepler.gl is a webgl based application to visualize large scale location data in the browser",
7
7
  "license": "MIT",
8
8
  "main": "dist/index.js",
@@ -238,13 +238,14 @@
238
238
  "tough-cookie": "4.0.0"
239
239
  },
240
240
  "peerDependencies": {
241
- "@deck.gl/aggregation-layers": "^8.7.3",
242
- "@deck.gl/core": "^8.7.3",
243
- "@deck.gl/extensions": "^8.7.3",
244
- "@deck.gl/geo-layers": "^8.7.3",
245
- "@deck.gl/layers": "^8.7.3",
246
- "@deck.gl/mesh-layers": "^8.7.3",
247
- "@deck.gl/react": "^8.7.3",
241
+ "@deck.gl/aggregation-layers": "^8.9.5",
242
+ "@deck.gl/carto": "^8.9.5",
243
+ "@deck.gl/core": "^8.9.5",
244
+ "@deck.gl/extensions": "^8.9.5",
245
+ "@deck.gl/geo-layers": "^8.9.5",
246
+ "@deck.gl/layers": "^8.9.5",
247
+ "@deck.gl/mesh-layers": "^8.9.5",
248
+ "@deck.gl/react": "^8.9.5",
248
249
  "@loaders.gl/core": "^3.1.5",
249
250
  "@loaders.gl/csv": "^3.1.5",
250
251
  "@loaders.gl/gltf": "^3.1.5",
@@ -40,7 +40,7 @@ export const ConfigGroupCollapsibleContent = styled.div.attrs({
40
40
  overflow: visible;
41
41
  transition: max-height 0.3s ease-out;
42
42
  height: max-content;
43
- max-height: 600px;
43
+ max-height: 650px;
44
44
  `;
45
45
 
46
46
  export const ConfigGroupCollapsibleHeader = styled.div.attrs({
@@ -62,7 +62,7 @@ export const StyledLayerConfigGroup = styled.div`
62
62
  &.collapsed {
63
63
  .layer-config-group__header__collapsible {
64
64
  overflow: visible;
65
- max-height: 600px;
65
+ max-height: 650px;
66
66
  }
67
67
  .layer-config-group__content {
68
68
  .layer-config-group__content__collapsible {
@@ -246,7 +246,6 @@ export default function LayerConfiguratorFactory(
246
246
  {/* text label */}
247
247
  <TextLabelPanel
248
248
  fields={visConfiguratorProps.fields}
249
- layer={layer}
250
249
  updateLayerTextLabel={this.props.updateLayerTextLabel}
251
250
  textLabel={layer.config.textLabel}
252
251
  colorPalette={visConfiguratorProps.colorPalette}
@@ -65,20 +65,10 @@ function TextLabelPanelFactory(RangeSlider, LayerConfigGroup, FieldSelector, Col
65
65
  };
66
66
 
67
67
  render() {
68
- const {layer, updateLayerTextLabel, textLabel, fields, AddButton} = this.props;
68
+ const {updateLayerTextLabel, textLabel, fields, AddButton} = this.props;
69
69
  const currentFields = textLabel.map(tl => tl.field && tl.field.name).filter(d => d);
70
70
 
71
71
  return (
72
- // Switch only works with group/property set to customMarkers???
73
- /* <LayerConfigGroup
74
- type="boolean"
75
- disabled={undefined}
76
- layer={layer}
77
- group="labels"
78
- property="labelsEnabled"
79
- label="panel.text.label"
80
- collapsible
81
- >*/
82
72
  <LayerConfigGroup label={'panel.text.label'} collapsible>
83
73
  <ConfigGroupCollapsibleHeader>
84
74
  <FieldSelector
Binary file
@@ -22,53 +22,7 @@ import {console as Console} from 'global/window';
22
22
  import keymirror from 'keymirror';
23
23
  import {CollisionFilterExtension, DataFilterExtension} from '@deck.gl/extensions';
24
24
  import {COORDINATE_SYSTEM, Layer as DeckLayer} from '@deck.gl/core';
25
- import {TextLayer} from '@deck.gl/layers';
26
-
27
- // TODO remove prior to release
28
- const SHOW_TEXT_BORDER = false;
29
-
30
- const [LEFT, TOP, RIGHT, BOTTOM] = [0, 1, 2, 3];
31
- class EnhancedTextLayer extends TextLayer {
32
- filterSubLayer({layer, renderPass}) {
33
- const background = layer.id.includes('background');
34
- if (renderPass === 'collision') {
35
- return background; // Only draw background layer in collision pass
36
- } else {
37
- return !background || (SHOW_TEXT_BORDER && this.props.getBorderWidth); // Do not draw background layer in other passes
38
- }
39
- }
40
- }
41
-
42
- // Workaround until https://github.com/visgl/deck.gl/pull/7513 is released
43
- DeckLayer.defaultProps.getFilterValue = {type: 'accessor', value: 0};
44
-
45
- // Workaround until https://github.com/visgl/deck.gl/pull/7507 is released
46
- function deepEqual(a, b) {
47
- if (a === b) {
48
- return true;
49
- }
50
- if (!a || !b) {
51
- return false;
52
- }
53
- for (const key in a) {
54
- const aValue = a[key];
55
- const bValue = b[key];
56
- const equals = aValue === bValue || deepEqual(aValue, bValue);
57
- if (!equals) {
58
- return false;
59
- }
60
- }
61
- return true;
62
- }
63
-
64
- DeckLayer.defaultProps.loadOptions = {
65
- type: 'object',
66
- value: {},
67
- optional: true,
68
- compare: true,
69
- equal: deepEqual
70
- };
71
-
25
+ import {_PointLabelLayer as PointLabelLayer} from '@deck.gl/carto';
72
26
  import DefaultLayerIcon from './default-layer-icon';
73
27
  import {diffUpdateTriggers} from './layer-update';
74
28
 
@@ -1357,110 +1311,89 @@ class Layer {
1357
1311
  };
1358
1312
  }
1359
1313
 
1360
- renderTextLabelLayer({getPosition, getPixelOffset, updateTriggers, sharedProps}, renderOpts) {
1314
+ renderTextLabelLayer(
1315
+ {getPosition, getRadius, radiusScale, updateTriggers, sharedProps},
1316
+ renderOpts
1317
+ ) {
1361
1318
  const {data, mapState} = renderOpts;
1362
1319
  const {textLabel, visibilityByZoom} = this.config;
1363
- // const extensions = [...sharedProps.extensions];
1364
1320
  const extensions = [...sharedProps.extensions, collisionFilterExtension];
1365
1321
 
1366
- return data.textLabels.reduce((accu, d, i) => {
1367
- if (d.getText) {
1368
- // Alignment based only on first label
1369
- const mainLabel = textLabel[0];
1370
- const {alignment, anchor, size} = mainLabel;
1371
- // Heuristics to avoid label overlap (should we base this on the text length?)
1372
- const paddingX = 12 * size;
1373
- const paddingY = 3 * size;
1374
- const backgroundPadding = [0, 0, 0, 0];
1375
- if (alignment === 'top') {
1376
- backgroundPadding[TOP] = paddingY;
1377
- } else if (alignment === 'bottom') {
1378
- backgroundPadding[BOTTOM] = paddingY;
1379
- } else {
1380
- backgroundPadding[TOP] = 0.5 * paddingY;
1381
- backgroundPadding[BOTTOM] = 0.5 * paddingY;
1382
- }
1383
- if (anchor === 'start') {
1384
- backgroundPadding[LEFT] = paddingX;
1385
- } else if (anchor === 'end') {
1386
- backgroundPadding[RIGHT] = paddingX;
1387
- } else {
1388
- backgroundPadding[LEFT] = 0.5 * paddingX;
1389
- backgroundPadding[RIGHT] = 0.5 * paddingX;
1390
- }
1322
+ if (!data || !data.textLabels || !data.textLabels[0] || !data.textLabels[0].getText) {
1323
+ return [];
1324
+ }
1391
1325
 
1392
- const pixelOffset = getPixelOffset(mainLabel);
1393
- if (i !== 0) {
1394
- // Place secondary label under main label (spacing 1 / 3 main label font size)
1395
- pixelOffset[1] += (1 + 1 / 3) * size;
1396
- }
1326
+ const [mainLabel, secondaryLabel] = textLabel;
1327
+ const {
1328
+ alignment,
1329
+ anchor,
1330
+ color: getColor,
1331
+ field: mainField,
1332
+ outlineColor,
1333
+ size: sizeScale
1334
+ } = mainLabel;
1335
+ const {
1336
+ color: getSecondaryColor,
1337
+ field: secondaryField,
1338
+ outlineColor: secondaryOutlineColor,
1339
+ size: secondarySizeScale
1340
+ } = secondaryLabel || {};
1341
+
1342
+ return [
1343
+ new PointLabelLayer({
1344
+ ...sharedProps,
1345
+
1346
+ // Recreate layer when labels change otherwise rendering breaks
1347
+ // TODO could be more efficient with updateTriggers?
1348
+ id: `${this.id}-${mainField?.name}-${secondaryField?.name}-label`,
1349
+ data: data.data,
1350
+ visibilityByZoom,
1351
+ extensions,
1352
+
1353
+ characterSet: 'auto',
1354
+ collisionEnabled: true,
1355
+ collisionGroup: `${this.id}-labels`,
1356
+
1357
+ getText: data.textLabels[0].getText,
1358
+ getColor,
1359
+ sizeScale,
1360
+ outlineColor,
1361
+
1362
+ ...(secondaryField && {
1363
+ getSecondaryText: data.textLabels[1].getText,
1364
+ getSecondaryColor,
1365
+ secondarySizeScale,
1366
+ secondaryOutlineColor
1367
+ }),
1397
1368
 
1398
- // TODO handle function case
1399
- backgroundPadding[LEFT] += pixelOffset[0];
1400
- backgroundPadding[RIGHT] -= pixelOffset[0];
1401
- backgroundPadding[TOP] += pixelOffset[1];
1402
- backgroundPadding[BOTTOM] -= pixelOffset[1];
1403
-
1404
- const {size: sizeScale} = textLabel[i];
1405
-
1406
- accu.push(
1407
- new EnhancedTextLayer({
1408
- ...sharedProps,
1409
- extensions,
1410
- collisionEnabled: true,
1411
- collisionGroup: 'labels',
1412
- id: `${this.id}-label-${textLabel[i].field?.name}`,
1413
- data: data.data,
1414
- visibilityByZoom,
1415
- getText: d.getText,
1416
- getPosition,
1417
- characterSet: d.characterSet,
1418
- getPixelOffset: pixelOffset,
1419
- getSize: 1,
1420
- sizeScale,
1421
- getTextAnchor: anchor,
1422
- getAlignmentBaseline: alignment,
1423
-
1424
- background: true,
1425
- backgroundPadding,
1426
-
1427
- // DEBUG
1428
- ...(SHOW_TEXT_BORDER && {
1429
- getBorderColor: [255, 0, 0, 80],
1430
- getBackgroundColor: [0, 0, 0, 0],
1431
- getBorderWidth: 1
1432
- }),
1433
-
1434
- getColor: textLabel[i].color,
1435
- fontFamily: 'Inter, sans',
1436
- fontSettings: {sdf: true},
1437
- fontWeight: 500,
1438
- outlineColor: textLabel[i].outlineColor,
1439
- outlineWidth: 4, // 1 seems to be thinner than 1px in CSS
1440
- parameters: {
1441
- // text will always show on top of all layers
1442
- depthTest: false
1443
- },
1444
-
1445
- getFilterValue: data.getFilterValue,
1446
- updateTriggers: {
1447
- ...updateTriggers,
1448
- getText: textLabel[i].field?.name,
1449
- getPixelOffset: {
1450
- ...updateTriggers.getPixelOffset,
1451
- ...updateTriggers.getRadius,
1452
- anchor: mainLabel.anchor,
1453
- alignment: mainLabel.alignment
1454
- },
1455
- getTextAnchor: mainLabel.anchor,
1456
- getAlignmentBaseline: mainLabel.alignment,
1457
- getColor: textLabel[i].color
1458
- }
1459
- })
1460
- );
1461
- }
1462
- return accu;
1463
- }, []);
1369
+ // Position
1370
+ getPosition,
1371
+ getRadius,
1372
+ radiusScale,
1373
+ getTextAnchor: anchor,
1374
+ getAlignmentBaseline: alignment,
1375
+
1376
+ // Style
1377
+ fontFamily: 'Inter, sans',
1378
+ fontSettings: {sdf: true},
1379
+ fontWeight: 500,
1380
+ outlineWidth: 4,
1381
+ parameters: {
1382
+ // text will always show on top of all layers
1383
+ depthTest: false
1384
+ },
1385
+
1386
+ getFilterValue: data.getFilterValue,
1387
+ updateTriggers: {
1388
+ ...updateTriggers,
1389
+ getText: mainField?.name,
1390
+ getColor: mainField?.color,
1391
+ getTextAnchor: mainLabel.anchor,
1392
+ getAlignmentBaseline: mainLabel.alignment,
1393
+ getSecondaryText: secondaryField?.name
1394
+ }
1395
+ })
1396
+ ];
1464
1397
  }
1465
1398
 
1466
1399
  /**
@@ -27,7 +27,6 @@ export const PROPERTY_GROUPS = keyMirror({
27
27
  color: null,
28
28
  stroke: null,
29
29
  customMarkers: null,
30
- labels: null,
31
30
  radius: null,
32
31
  height: null,
33
32
  angle: null,
@@ -45,13 +44,20 @@ export {DEFAULT_COLOR_RANGE};
45
44
  export const DEFAULT_TEXT_LABEL = {
46
45
  field: null,
47
46
  color: [44, 48, 50],
48
- outlineColor: [0, 0, 0],
47
+ outlineColor: [255, 255, 255],
49
48
  size: 12,
50
49
  offset: [0, 0],
51
50
  anchor: 'start',
52
51
  alignment: 'center'
53
52
  };
54
53
 
54
+ /** @type {import('./layer-factory').LayerTextLabel} */
55
+ export const DEFAULT_SECONDARY_TEXT_LABEL = {
56
+ ...DEFAULT_TEXT_LABEL,
57
+ color: [128, 130, 132],
58
+ size: 10
59
+ };
60
+
55
61
  /** @type {import('./layer-factory').ColorRange} */
56
62
  export const DEFAULT_CUSTOM_PALETTE = {
57
63
  name: 'color.customPalette',
@@ -389,13 +395,6 @@ export const LAYER_VIS_CONFIGS = {
389
395
  group: PROPERTY_GROUPS.display,
390
396
  property: 'filled'
391
397
  },
392
- labels: {
393
- type: 'boolean',
394
- label: 'layerVisConfigs.labels',
395
- defaultValue: false,
396
- group: PROPERTY_GROUPS.labels,
397
- property: 'labels'
398
- },
399
398
  extruded: {
400
399
  type: 'boolean',
401
400
  defaultValue: false,
@@ -439,13 +438,6 @@ export const LAYER_VIS_CONFIGS = {
439
438
  group: PROPERTY_GROUPS.customMarkers,
440
439
  property: 'customMarkers'
441
440
  },
442
- labelsEnabled: {
443
- type: 'boolean',
444
- defaultValue: true,
445
- label: 'layerVisConfigs.labelsEnabled',
446
- group: PROPERTY_GROUPS.labels,
447
- property: 'labelsEnabled'
448
- },
449
441
  customMarkersUrl: {
450
442
  type: 'text',
451
443
  defaultValue: '',
@@ -35,17 +35,16 @@ export function getTextOffsetByRadius(radiusScale, getRadius) {
35
35
  ? textLabel.size
36
36
  : textLabel.size;
37
37
 
38
- const padding = textLabel.size / 4; // Padding based on font size (font size / 4)
39
- const pointPadding = 1 + 1 / 4; // Padding based on point size (point size / 4)
38
+ const padding = defaultPadding;
40
39
 
41
40
  return typeof getRadius === 'function'
42
41
  ? d => [
43
- xMult * (getRadius(d) * radiusScale * pointPadding + padding),
44
- yMult * (getRadius(d) * radiusScale * pointPadding + sizeOffset)
42
+ xMult * (getRadius(d) * radiusScale + padding),
43
+ yMult * (getRadius(d) * radiusScale + padding + sizeOffset)
45
44
  ]
46
45
  : [
47
- xMult * (getRadius * radiusScale * pointPadding + padding),
48
- yMult * (getRadius * radiusScale * pointPadding + sizeOffset)
46
+ xMult * (getRadius * radiusScale + padding),
47
+ yMult * (getRadius * radiusScale + padding + sizeOffset)
49
48
  ];
50
49
  };
51
50
  }
@@ -55,40 +54,9 @@ export const textLabelAccessor = textLabel => dc => d => {
55
54
  return notNullorUndefined(val) ? String(val) : '';
56
55
  };
57
56
 
58
- export const formatTextLabelData = ({
59
- textLabel,
60
- triggerChanged,
61
- oldLayerData,
62
- data,
63
- dataContainer
64
- }) => {
57
+ export const formatTextLabelData = ({textLabel, dataContainer}) => {
65
58
  return textLabel.map((tl, i) => {
66
- if (!tl.field) {
67
- // if no field selected,
68
- return {
69
- getText: null,
70
- characterSet: []
71
- };
72
- }
73
-
74
- const getText = textLabelAccessor(tl)(dataContainer);
75
- let characterSet;
76
-
77
- if (
78
- !triggerChanged[`getLabelCharacterSet-${i}`] &&
79
- oldLayerData &&
80
- oldLayerData.textLabels &&
81
- oldLayerData.textLabels[i]
82
- ) {
83
- characterSet = oldLayerData.textLabels[i].characterSet;
84
- } else {
85
- const allLabels = tl.field ? data.map(getText) : [];
86
- characterSet = uniq(allLabels.join(''));
87
- }
88
-
89
- return {
90
- characterSet,
91
- getText
92
- };
59
+ const getText = tl.field ? textLabelAccessor(tl)(dataContainer) : null;
60
+ return {getText};
93
61
  });
94
62
  };
@@ -29,7 +29,7 @@ import {findDefaultColorField} from 'utils/dataset-utils';
29
29
  import PointLayerIcon from './point-layer-icon';
30
30
  import {DEFAULT_LAYER_COLOR, CHANNEL_SCALES, UNKNOWN_COLOR} from 'constants/default-settings';
31
31
 
32
- import {getTextOffsetByRadius, formatTextLabelData} from '../layer-text-label';
32
+ import {formatTextLabelData} from '../layer-text-label';
33
33
 
34
34
  export const pointPosAccessor = ({lat, lng, altitude}) => dc => d => [
35
35
  dc.valueAt(d.index, lng.fieldIdx),
@@ -47,7 +47,6 @@ export const pointVisConfigs = {
47
47
  customMarkers: 'customMarkers',
48
48
  customMarkersUrl: 'customMarkersUrl',
49
49
  customMarkersRange: 'customMarkersRange',
50
- labelsEnabled: 'labelsEnabled',
51
50
  opacity: 'opacity',
52
51
  outline: 'outline',
53
52
  thickness: 'thickness',
@@ -243,13 +242,7 @@ export default class PointLayer extends Layer {
243
242
  const getPosition = this.getPositionAccessor(dataContainer);
244
243
 
245
244
  // get all distinct characters in the text labels
246
- const textLabels = formatTextLabelData({
247
- textLabel,
248
- triggerChanged,
249
- oldLayerData,
250
- data,
251
- dataContainer
252
- });
245
+ const textLabels = formatTextLabelData({textLabel, dataContainer});
253
246
 
254
247
  const accessors = this.getAttributeAccessors({dataContainer});
255
248
 
@@ -285,11 +278,6 @@ export default class PointLayer extends Layer {
285
278
 
286
279
  const defaultLayerProps = this.getDefaultDeckLayerProps(opts);
287
280
  const brushingProps = this.getBrushingExtensionProps(interactionConfig);
288
- const getPixelOffset = getTextOffsetByRadius(
289
- // TODO remove this scaling by half here and elsewhere
290
- customMarkers ? radiusScale / 2 : radiusScale,
291
- data.getRadius
292
- );
293
281
  const extensions = [...defaultLayerProps.extensions, brushingExtension];
294
282
 
295
283
  const updateTriggers = {
@@ -302,6 +290,7 @@ export default class PointLayer extends Layer {
302
290
  getFilterValue: data.getFilterValue,
303
291
  extensions,
304
292
  filterRange: defaultLayerProps.filterRange,
293
+ maskId: defaultLayerProps.maskId,
305
294
  visible: defaultLayerProps.visible,
306
295
  ...brushingProps
307
296
  };
@@ -311,7 +300,9 @@ export default class PointLayer extends Layer {
311
300
  {
312
301
  getPosition: data.getPosition,
313
302
  sharedProps,
314
- getPixelOffset,
303
+ // TODO remove this scaling by half here and elsewhere
304
+ radiusScale: customMarkers ? radiusScale / 2 : radiusScale,
305
+ getRadius: data.getRadius,
315
306
  updateTriggers
316
307
  },
317
308
  opts
@@ -72,10 +72,10 @@ export default {
72
72
  secondaryLabel: 'Secondary Label',
73
73
  fontSize: 'Font size',
74
74
  fontColor: 'Font color',
75
- outlineColor: 'Outline Color',
75
+ outlineColor: 'Outline color',
76
76
  textAnchor: 'Text anchor',
77
77
  alignment: 'Alignment',
78
- addMoreLabel: 'Add More Label'
78
+ addMoreLabel: 'Add secondary label'
79
79
  }
80
80
  },
81
81
  sidebar: {
@@ -76,7 +76,7 @@ import {
76
76
  } from 'utils/split-map-utils';
77
77
 
78
78
  import {Layer, LayerClasses, LAYER_ID_LENGTH} from 'layers';
79
- import {DEFAULT_TEXT_LABEL} from 'layers/layer-factory';
79
+ import {DEFAULT_TEXT_LABEL, DEFAULT_SECONDARY_TEXT_LABEL} from 'layers/layer-factory';
80
80
  import {EDITOR_MODES, SORT_ORDER, FILTER_TYPES} from 'constants/default-settings';
81
81
  import {pick_, merge_, swap_} from './composer-helpers';
82
82
  import {processFileContent} from 'actions/vis-state-actions';
@@ -315,16 +315,19 @@ function addOrRemoveTextLabels(newFields, textLabel) {
315
315
 
316
316
  // delete
317
317
  newTextLabel = newTextLabel.filter(tl => tl.field && !deleteFields.includes(tl.field.name));
318
- newTextLabel = !newTextLabel.length ? [DEFAULT_TEXT_LABEL] : newTextLabel;
319
318
 
320
319
  // add
321
- newTextLabel = [
322
- ...newTextLabel.filter(tl => tl.field),
323
- ...addFields.map(af => ({
324
- ...DEFAULT_TEXT_LABEL,
325
- field: af
326
- }))
327
- ];
320
+ newTextLabel = newTextLabel.filter(tl => tl.field);
321
+ const defaultLabel = newTextLabel.length ? DEFAULT_SECONDARY_TEXT_LABEL : DEFAULT_TEXT_LABEL;
322
+ newTextLabel = [...newTextLabel, ...addFields.map(af => ({...defaultLabel, field: af}))];
323
+
324
+ // limit to 2 labels (overwrite 2nd label with 3rd)
325
+ if (newTextLabel.length > 2) {
326
+ newTextLabel = [newTextLabel[0], newTextLabel[2]];
327
+ }
328
+
329
+ // default
330
+ newTextLabel = !newTextLabel.length ? [DEFAULT_TEXT_LABEL] : newTextLabel;
328
331
 
329
332
  return newTextLabel;
330
333
  }
@@ -359,7 +362,7 @@ export function layerTextLabelChangeUpdater(state, action) {
359
362
  let newTextLabel = textLabel.slice();
360
363
  if (!textLabel[idx] && idx === textLabel.length) {
361
364
  // if idx is set to length, add empty text label
362
- newTextLabel = [...textLabel, DEFAULT_TEXT_LABEL];
365
+ newTextLabel = [...textLabel, idx === 0 ? DEFAULT_TEXT_LABEL : DEFAULT_SECONDARY_TEXT_LABEL];
363
366
  }
364
367
 
365
368
  if (idx === 'all' && prop === 'fields') {