@flowmap.gl/data 8.0.0-alpha.21 → 8.0.0-alpha.24

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.
@@ -46,6 +46,7 @@ import getColors, {
46
46
  import FlowmapAggregateAccessors from './FlowmapAggregateAccessors';
47
47
  import {FlowmapState} from './FlowmapState';
48
48
  import {
49
+ addClusterNames,
49
50
  getFlowThicknessScale,
50
51
  getViewportBoundingBox,
51
52
  } from './selector-functions';
@@ -94,6 +95,10 @@ export default class FlowmapSelectors<L, F> {
94
95
  this.accessors = new FlowmapAggregateAccessors(accessors);
95
96
  }
96
97
 
98
+ getAggregateAccessors(): FlowmapAggregateAccessors<L, F> {
99
+ return this.accessors;
100
+ }
101
+
97
102
  getFlowsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
98
103
  props.flows;
99
104
  getLocationsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
@@ -114,6 +119,8 @@ export default class FlowmapSelectors<L, F> {
114
119
  state.settings.clusteringEnabled;
115
120
  getLocationTotalsEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
116
121
  state.settings.locationTotalsEnabled;
122
+ getLocationLabelsEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
123
+ state.settings.locationLabelsEnabled;
117
124
  getZoom = (state: FlowmapState, props: FlowmapData<L, F>) =>
118
125
  state.viewport.zoom;
119
126
  getViewport = (state: FlowmapState, props: FlowmapData<L, F>) =>
@@ -194,7 +201,7 @@ export default class FlowmapSelectors<L, F> {
194
201
  return ids;
195
202
  });
196
203
 
197
- getSelectedLocationsSet: Selector<L, F, Set<string> | undefined> =
204
+ getSelectedLocationsSet: Selector<L, F, Set<string | number> | undefined> =
198
205
  createSelector(this.getSelectedLocations, (ids) =>
199
206
  ids && ids.length > 0 ? new Set(ids) : undefined,
200
207
  );
@@ -361,46 +368,14 @@ export default class FlowmapSelectors<L, F> {
361
368
  return undefined;
362
369
 
363
370
  const clusterIndex = buildIndex<F>(clusterLevels);
364
- const {getLocationName, getLocationClusterName} =
365
- this.accessors.getFlowmapDataAccessors();
366
-
367
371
  // Adding meaningful names
368
- const getName = (id: string | number) => {
369
- const loc = locationsById.get(id);
370
- if (loc) {
371
- return getLocationName
372
- ? getLocationName(loc)
373
- : this.accessors.getLocationId(loc) || id;
374
- }
375
- return `"${id}"`;
376
- };
377
- for (const level of clusterLevels) {
378
- for (const node of level.nodes) {
379
- // Here mutating the nodes (adding names)
380
- if (isCluster(node)) {
381
- const leaves = clusterIndex.expandCluster(node);
382
-
383
- leaves.sort((a, b) =>
384
- descending(getLocationWeight(a), getLocationWeight(b)),
385
- );
386
-
387
- if (getLocationClusterName) {
388
- node.name = getLocationClusterName(leaves);
389
- } else {
390
- const topId = leaves[0];
391
- const otherId = leaves.length === 2 ? leaves[1] : undefined;
392
- node.name = `"${getName(topId)}" and ${
393
- otherId
394
- ? `"${getName(otherId)}"`
395
- : `${leaves.length - 1} others`
396
- }`;
397
- }
398
- } else {
399
- (node as any).name = getName(node.id);
400
- }
401
- }
402
- }
403
-
372
+ addClusterNames(
373
+ clusterIndex,
374
+ clusterLevels,
375
+ locationsById,
376
+ this.accessors.getFlowmapDataAccessors(),
377
+ getLocationWeight,
378
+ );
404
379
  return clusterIndex;
405
380
  },
406
381
  );
@@ -416,7 +391,7 @@ export default class FlowmapSelectors<L, F> {
416
391
  let maxZoom = Number.POSITIVE_INFINITY;
417
392
  let minZoom = Number.NEGATIVE_INFINITY;
418
393
 
419
- const adjust = (zoneId: string) => {
394
+ const adjust = (zoneId: string | number) => {
420
395
  const cluster = clusterIndex.getClusterById(zoneId);
421
396
  if (cluster) {
422
397
  minZoom = Math.max(minZoom, cluster.zoom);
@@ -611,31 +586,34 @@ export default class FlowmapSelectors<L, F> {
611
586
  },
612
587
  );
613
588
 
614
- getExpandedSelectedLocationsSet: Selector<L, F, Set<string> | undefined> =
615
- createSelector(
616
- this.getClusteringEnabled,
617
- this.getSelectedLocationsSet,
618
- this.getClusterIndex,
619
- (clusteringEnabled, selectedLocations, clusterIndex) => {
620
- if (!selectedLocations || !clusterIndex) {
621
- return selectedLocations;
622
- }
589
+ getExpandedSelectedLocationsSet: Selector<
590
+ L,
591
+ F,
592
+ Set<string | number> | undefined
593
+ > = createSelector(
594
+ this.getClusteringEnabled,
595
+ this.getSelectedLocationsSet,
596
+ this.getClusterIndex,
597
+ (clusteringEnabled, selectedLocations, clusterIndex) => {
598
+ if (!selectedLocations || !clusterIndex) {
599
+ return selectedLocations;
600
+ }
623
601
 
624
- const result = new Set<string>();
625
- for (const locationId of selectedLocations) {
626
- const cluster = clusterIndex.getClusterById(locationId);
627
- if (cluster) {
628
- const expanded = clusterIndex.expandCluster(cluster);
629
- for (const id of expanded) {
630
- result.add(id);
631
- }
632
- } else {
633
- result.add(locationId);
602
+ const result = new Set<string | number>();
603
+ for (const locationId of selectedLocations) {
604
+ const cluster = clusterIndex.getClusterById(locationId);
605
+ if (cluster) {
606
+ const expanded = clusterIndex.expandCluster(cluster);
607
+ for (const id of expanded) {
608
+ result.add(id);
634
609
  }
610
+ } else {
611
+ result.add(locationId);
635
612
  }
636
- return result;
637
- },
638
- );
613
+ }
614
+ return result;
615
+ },
616
+ );
639
617
 
640
618
  getTotalCountsByTime: Selector<L, F, CountByTime[] | undefined> =
641
619
  createSelector(
@@ -1099,6 +1077,15 @@ export default class FlowmapSelectors<L, F> {
1099
1077
  );
1100
1078
  });
1101
1079
 
1080
+ getLocationOrClusterByIdGetter = createSelector(
1081
+ this.getClusterIndex,
1082
+ this.getLocationsById,
1083
+ (clusterIndex, locationsById) => {
1084
+ return (id: string | number) =>
1085
+ clusterIndex?.getClusterById(id) ?? locationsById?.get(id);
1086
+ },
1087
+ );
1088
+
1102
1089
  getLayersData: Selector<L, F, LayersData> = createSelector(
1103
1090
  this.getLocationsForFlowmapLayer,
1104
1091
  this.getFlowsForFlowmapLayer,
@@ -1109,6 +1096,7 @@ export default class FlowmapSelectors<L, F> {
1109
1096
  this.getOutCircleSizeGetter,
1110
1097
  this.getFlowThicknessScale,
1111
1098
  this.getAnimate,
1099
+ this.getLocationLabelsEnabled,
1112
1100
  (
1113
1101
  locations,
1114
1102
  flows,
@@ -1119,6 +1107,7 @@ export default class FlowmapSelectors<L, F> {
1119
1107
  getOutCircleSize,
1120
1108
  flowThicknessScale,
1121
1109
  animationEnabled,
1110
+ locationLabelsEnabled,
1122
1111
  ) => {
1123
1112
  return this._prepareLayersData(
1124
1113
  locations,
@@ -1130,6 +1119,7 @@ export default class FlowmapSelectors<L, F> {
1130
1119
  getOutCircleSize,
1131
1120
  flowThicknessScale,
1132
1121
  animationEnabled,
1122
+ locationLabelsEnabled,
1133
1123
  );
1134
1124
  },
1135
1125
  );
@@ -1143,6 +1133,7 @@ export default class FlowmapSelectors<L, F> {
1143
1133
  const getInCircleSize = this.getInCircleSizeGetter(state, props);
1144
1134
  const getOutCircleSize = this.getOutCircleSizeGetter(state, props);
1145
1135
  const flowThicknessScale = this.getFlowThicknessScale(state, props);
1136
+ const locationLabelsEnabled = this.getLocationLabelsEnabled(state, props);
1146
1137
  return this._prepareLayersData(
1147
1138
  locations,
1148
1139
  flows,
@@ -1153,6 +1144,7 @@ export default class FlowmapSelectors<L, F> {
1153
1144
  getOutCircleSize,
1154
1145
  flowThicknessScale,
1155
1146
  state.settings.animationEnabled,
1147
+ locationLabelsEnabled,
1156
1148
  );
1157
1149
  }
1158
1150
 
@@ -1166,6 +1158,7 @@ export default class FlowmapSelectors<L, F> {
1166
1158
  getOutCircleSize: (locationId: string | number) => number,
1167
1159
  flowThicknessScale: ScaleLinear<number, number, never> | undefined,
1168
1160
  animationEnabled: boolean,
1161
+ locationLabelsEnabled: boolean,
1169
1162
  ): LayersData {
1170
1163
  if (!locations) locations = [];
1171
1164
  if (!flows) flows = [];
@@ -1176,6 +1169,7 @@ export default class FlowmapSelectors<L, F> {
1176
1169
  getLocationId,
1177
1170
  getLocationLon,
1178
1171
  getLocationLat,
1172
+ getLocationName,
1179
1173
  } = this.accessors;
1180
1174
 
1181
1175
  const flowMagnitudeExtent = extent(flows, (f) => getFlowMagnitude(f)) as [
@@ -1307,6 +1301,9 @@ export default class FlowmapSelectors<L, F> {
1307
1301
  : {}),
1308
1302
  },
1309
1303
  },
1304
+ ...(locationLabelsEnabled
1305
+ ? {locationLabels: locations.map(getLocationName)}
1306
+ : undefined),
1310
1307
  };
1311
1308
  }
1312
1309
 
@@ -1,7 +1,7 @@
1
1
  import {LocationFilterMode, ViewportProps} from './types';
2
2
 
3
3
  export interface FilterState {
4
- selectedLocations?: string[];
4
+ selectedLocations?: (string | number)[];
5
5
  locationFilterMode?: LocationFilterMode;
6
6
  selectedTimeRange?: [Date, Date];
7
7
  }
@@ -11,6 +11,7 @@ export interface SettingsState {
11
11
  fadeEnabled: boolean;
12
12
  fadeOpacityEnabled: boolean;
13
13
  locationTotalsEnabled: boolean;
14
+ locationLabelsEnabled: boolean;
14
15
  adaptiveScalesEnabled: boolean;
15
16
  clusteringEnabled: boolean;
16
17
  clusteringAuto: boolean;
@@ -42,7 +42,7 @@ export interface ClusterIndex<F> {
42
42
  /**
43
43
  * Get the min zoom level on which the location is not clustered.
44
44
  */
45
- getMinZoomForLocation: (locationId: string) => number;
45
+ getMinZoomForLocation: (locationId: string | number) => number;
46
46
  /**
47
47
  * List the IDs of all locations in the cluster (leaves of the subtree starting in the cluster).
48
48
  */
@@ -15,6 +15,7 @@ import {
15
15
  GetViewStateOptions,
16
16
  getViewStateForLocations,
17
17
  } from '../getViewStateForLocations';
18
+ import {ClusterIndex} from '../cluster/ClusterIndex';
18
19
 
19
20
  export default class LocalFlowmapDataProvider<L, F>
20
21
  implements FlowmapDataProvider<L, F>
@@ -34,14 +35,26 @@ export default class LocalFlowmapDataProvider<L, F>
34
35
  this.selectors.setAccessors(accessors);
35
36
  }
36
37
 
37
- async setFlowmapData(flowmapData: FlowmapData<L, F>): Promise<void> {
38
+ setFlowmapData(flowmapData: FlowmapData<L, F>): void {
38
39
  this.flowmapData = flowmapData;
39
40
  }
40
41
 
42
+ getSelectors(): FlowmapSelectors<L, F> {
43
+ return this.selectors;
44
+ }
45
+
46
+ getFlowmapData(): FlowmapData<L, F> | undefined {
47
+ return this.flowmapData;
48
+ }
49
+
41
50
  async setFlowmapState(flowmapState: FlowmapState): Promise<void> {
42
51
  this.flowmapState = flowmapState;
43
52
  }
44
53
 
54
+ getFlowmapState(): FlowmapState | undefined {
55
+ return this.flowmapState;
56
+ }
57
+
45
58
  async getFlowByIndex(idx: number): Promise<F | AggregateFlow | undefined> {
46
59
  if (!this.flowmapState || !this.flowmapData) {
47
60
  return undefined;
@@ -128,4 +141,37 @@ export default class LocalFlowmapDataProvider<L, F>
128
141
  ) {
129
142
  setLayersData(await this.getLayersData());
130
143
  }
144
+
145
+ getClusterZoom(): number | undefined {
146
+ return this.flowmapState && this.flowmapData
147
+ ? this.selectors.getClusterZoom(this.flowmapState, this.flowmapData)
148
+ : undefined;
149
+ }
150
+
151
+ getClusterIndex(): ClusterIndex<F> | undefined {
152
+ return this.flowmapState && this.flowmapData
153
+ ? this.selectors.getClusterIndex(this.flowmapState, this.flowmapData)
154
+ : undefined;
155
+ }
156
+
157
+ getLocationsById(): Map<string | number, L> | undefined {
158
+ return this.flowmapState && this.flowmapData
159
+ ? this.selectors.getLocationsById(this.flowmapState, this.flowmapData)
160
+ : undefined;
161
+ }
162
+
163
+ getLocationTotals(): Map<string | number, LocationTotals> | undefined {
164
+ return this.flowmapState && this.flowmapData
165
+ ? this.selectors.getLocationTotals(this.flowmapState, this.flowmapData)
166
+ : undefined;
167
+ }
168
+
169
+ getFlowsForFlowmapLayer(): Array<F | AggregateFlow> | undefined {
170
+ return this.flowmapState && this.flowmapData
171
+ ? this.selectors.getFlowsForFlowmapLayer(
172
+ this.flowmapState,
173
+ this.flowmapData,
174
+ )
175
+ : undefined;
176
+ }
131
177
  }
@@ -1,6 +1,13 @@
1
1
  import {WebMercatorViewport} from '@math.gl/web-mercator';
2
- import {ViewportProps} from './types';
2
+ import {
3
+ ClusterLevel,
4
+ isCluster,
5
+ LocationAccessors,
6
+ ViewportProps,
7
+ } from './types';
3
8
  import {scaleLinear} from 'd3-scale';
9
+ import {ClusterIndex, LocationWeightGetter} from './cluster/ClusterIndex';
10
+ import {descending} from 'd3-array';
4
11
 
5
12
  // TODO: use re-reselect
6
13
 
@@ -32,3 +39,49 @@ export const getFlowThicknessScale = (
32
39
  ),
33
40
  ]);
34
41
  };
42
+
43
+ /**
44
+ * Adding meaningful cluster names.
45
+ * NOTE: this will mutate the nodes in clusterIndex
46
+ */
47
+ export function addClusterNames<L, F>(
48
+ clusterIndex: ClusterIndex<F>,
49
+ clusterLevels: ClusterLevel[],
50
+ locationsById: Map<string | number, L>,
51
+ locationAccessors: LocationAccessors<L>,
52
+ getLocationWeight: LocationWeightGetter,
53
+ ): void {
54
+ const {getLocationId, getLocationName, getLocationClusterName} =
55
+ locationAccessors;
56
+ const getName = (id: string | number) => {
57
+ const loc = locationsById.get(id);
58
+ if (loc) {
59
+ return getLocationName ? getLocationName(loc) : getLocationId(loc) || id;
60
+ }
61
+ return `"${id}"`;
62
+ };
63
+ for (const level of clusterLevels) {
64
+ for (const node of level.nodes) {
65
+ // Here mutating the nodes (adding names)
66
+ if (isCluster(node)) {
67
+ const leaves = clusterIndex.expandCluster(node);
68
+
69
+ leaves.sort((a, b) =>
70
+ descending(getLocationWeight(a), getLocationWeight(b)),
71
+ );
72
+
73
+ if (getLocationClusterName) {
74
+ node.name = getLocationClusterName(leaves);
75
+ } else {
76
+ const topId = leaves[0];
77
+ const otherId = leaves.length === 2 ? leaves[1] : undefined;
78
+ node.name = `"${getName(topId)}" and ${
79
+ otherId ? `"${getName(otherId)}"` : `${leaves.length - 1} others`
80
+ }`;
81
+ }
82
+ } else {
83
+ (node as any).name = getName(node.id);
84
+ }
85
+ }
86
+ }
87
+ }
package/src/types.ts CHANGED
@@ -26,7 +26,7 @@ export interface FlowAccessors<F> {
26
26
 
27
27
  export interface LocationAccessors<L> {
28
28
  getLocationId: LocationAccessor<L, string | number>;
29
- getLocationName?: LocationAccessor<L, string | number>;
29
+ getLocationName?: LocationAccessor<L, string>;
30
30
  getLocationLat: LocationAccessor<L, number>;
31
31
  getLocationLon: LocationAccessor<L, number>;
32
32
  getLocationClusterName?: (locationIds: (string | number)[]) => string;
@@ -160,6 +160,7 @@ export interface FlowLinesLayerAttributes {
160
160
  export interface LayersData {
161
161
  circleAttributes: FlowCirclesLayerAttributes;
162
162
  lineAttributes: FlowLinesLayerAttributes;
163
+ locationLabels?: string[];
163
164
  }
164
165
 
165
166
  export type LayersDataAttrValues<T> = {value: T; size: number};