@flowmap.gl/data 8.0.0-alpha.18 → 8.0.0-alpha.21
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/dist/FlowmapAggregateAccessors.d.ts +3 -3
- package/dist/FlowmapAggregateAccessors.d.ts.map +1 -1
- package/dist/FlowmapAggregateAccessors.js +2 -2
- package/dist/FlowmapSelectors.d.ts +44 -33
- package/dist/FlowmapSelectors.d.ts.map +1 -1
- package/dist/FlowmapSelectors.js +50 -49
- package/dist/FlowmapState.d.ts +5 -5
- package/dist/FlowmapState.d.ts.map +1 -1
- package/dist/FlowmapState.js +1 -1
- package/dist/cluster/ClusterIndex.d.ts +3 -3
- package/dist/cluster/ClusterIndex.d.ts.map +1 -1
- package/dist/cluster/ClusterIndex.js +1 -1
- package/dist/cluster/cluster.d.ts.map +1 -1
- package/dist/cluster/cluster.js +64 -13
- package/dist/colors.d.ts +1 -1
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +3 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/provider/FlowmapDataProvider.d.ts +7 -2
- package/dist/provider/FlowmapDataProvider.d.ts.map +1 -1
- package/dist/provider/FlowmapDataProvider.js +1 -1
- package/dist/provider/LocalFlowmapDataProvider.d.ts +3 -2
- package/dist/provider/LocalFlowmapDataProvider.d.ts.map +1 -1
- package/dist/provider/LocalFlowmapDataProvider.js +6 -1
- package/dist/selector-functions.d.ts +4 -0
- package/dist/selector-functions.d.ts.map +1 -0
- package/dist/selector-functions.js +20 -0
- package/dist/types.d.ts +12 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/package.json +2 -3
- package/src/FlowmapAggregateAccessors.ts +2 -2
- package/src/FlowmapSelectors.ts +171 -160
- package/src/FlowmapState.ts +5 -5
- package/src/cluster/ClusterIndex.ts +19 -12
- package/src/cluster/cluster.ts +71 -16
- package/src/colors.ts +7 -9
- package/src/index.ts +3 -0
- package/src/provider/FlowmapDataProvider.ts +13 -2
- package/src/provider/LocalFlowmapDataProvider.ts +10 -2
- package/src/selector-functions.ts +34 -0
- package/src/types.ts +12 -11
package/src/FlowmapSelectors.ts
CHANGED
|
@@ -16,9 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {ScaleLinear, scaleLinear, scaleSqrt} from 'd3-scale';
|
|
19
|
+
import {ascending, descending, extent, min, rollup} from 'd3-array';
|
|
20
|
+
import {ScaleLinear, scaleSqrt} from 'd3-scale';
|
|
22
21
|
import KDBush from 'kdbush';
|
|
23
22
|
import {
|
|
24
23
|
createSelector,
|
|
@@ -32,6 +31,7 @@ import {
|
|
|
32
31
|
buildIndex,
|
|
33
32
|
ClusterIndex,
|
|
34
33
|
findAppropriateZoomLevel,
|
|
34
|
+
LocationWeightGetter,
|
|
35
35
|
makeLocationWeightGetter,
|
|
36
36
|
} from './cluster/ClusterIndex';
|
|
37
37
|
import getColors, {
|
|
@@ -45,6 +45,10 @@ import getColors, {
|
|
|
45
45
|
} from './colors';
|
|
46
46
|
import FlowmapAggregateAccessors from './FlowmapAggregateAccessors';
|
|
47
47
|
import {FlowmapState} from './FlowmapState';
|
|
48
|
+
import {
|
|
49
|
+
getFlowThicknessScale,
|
|
50
|
+
getViewportBoundingBox,
|
|
51
|
+
} from './selector-functions';
|
|
48
52
|
import {
|
|
49
53
|
getTimeGranularityByKey,
|
|
50
54
|
getTimeGranularityByOrder,
|
|
@@ -54,6 +58,7 @@ import {
|
|
|
54
58
|
import {
|
|
55
59
|
AggregateFlow,
|
|
56
60
|
Cluster,
|
|
61
|
+
ClusterLevels,
|
|
57
62
|
ClusterNode,
|
|
58
63
|
CountByTime,
|
|
59
64
|
FlowAccessors,
|
|
@@ -89,60 +94,65 @@ export default class FlowmapSelectors<L, F> {
|
|
|
89
94
|
this.accessors = new FlowmapAggregateAccessors(accessors);
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
getFlowsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
93
98
|
props.flows;
|
|
94
|
-
|
|
99
|
+
getLocationsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
95
100
|
props.locations;
|
|
101
|
+
getClusterLevelsFromProps = (
|
|
102
|
+
state: FlowmapState,
|
|
103
|
+
props: FlowmapData<L, F>,
|
|
104
|
+
) => {
|
|
105
|
+
return props.clusterLevels;
|
|
106
|
+
};
|
|
96
107
|
getMaxTopFlowsDisplayNum = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
97
|
-
state.
|
|
108
|
+
state.settings.maxTopFlowsDisplayNum;
|
|
98
109
|
getSelectedLocations = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
99
|
-
state.
|
|
110
|
+
state.filter?.selectedLocations;
|
|
100
111
|
getLocationFilterMode = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
101
|
-
state.
|
|
112
|
+
state.filter?.locationFilterMode;
|
|
102
113
|
getClusteringEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
103
|
-
state.
|
|
114
|
+
state.settings.clusteringEnabled;
|
|
104
115
|
getLocationTotalsEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
105
|
-
state.
|
|
116
|
+
state.settings.locationTotalsEnabled;
|
|
106
117
|
getZoom = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
107
118
|
state.viewport.zoom;
|
|
108
119
|
getViewport = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
109
120
|
state.viewport;
|
|
110
121
|
getSelectedTimeRange = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
111
|
-
state.
|
|
122
|
+
state.filter?.selectedTimeRange;
|
|
112
123
|
|
|
113
124
|
getColorScheme: Selector<L, F, string | string[] | undefined> = (
|
|
114
125
|
state: FlowmapState,
|
|
115
126
|
props: FlowmapData<L, F>,
|
|
116
|
-
) => state.
|
|
127
|
+
) => state.settings.colorScheme;
|
|
117
128
|
|
|
118
129
|
getDarkMode: Selector<L, F, boolean> = (
|
|
119
130
|
state: FlowmapState,
|
|
120
131
|
props: FlowmapData<L, F>,
|
|
121
|
-
) => state.
|
|
132
|
+
) => state.settings.darkMode;
|
|
122
133
|
|
|
123
134
|
getFadeEnabled: Selector<L, F, boolean> = (
|
|
124
135
|
state: FlowmapState,
|
|
125
136
|
props: FlowmapData<L, F>,
|
|
126
|
-
) => state.
|
|
137
|
+
) => state.settings.fadeEnabled;
|
|
127
138
|
|
|
128
139
|
getFadeOpacityEnabled: Selector<L, F, boolean> = (
|
|
129
140
|
state: FlowmapState,
|
|
130
141
|
props: FlowmapData<L, F>,
|
|
131
|
-
) => state.
|
|
142
|
+
) => state.settings.fadeOpacityEnabled;
|
|
132
143
|
|
|
133
144
|
getFadeAmount: Selector<L, F, number> = (
|
|
134
145
|
state: FlowmapState,
|
|
135
146
|
props: FlowmapData<L, F>,
|
|
136
|
-
) => state.
|
|
147
|
+
) => state.settings.fadeAmount;
|
|
137
148
|
|
|
138
149
|
getAnimate: Selector<L, F, boolean> = (
|
|
139
150
|
state: FlowmapState,
|
|
140
151
|
props: FlowmapData<L, F>,
|
|
141
|
-
) => state.
|
|
152
|
+
) => state.settings.animationEnabled;
|
|
142
153
|
|
|
143
|
-
getInvalidLocationIds: Selector<L, F, string[] | undefined> =
|
|
144
|
-
this.
|
|
145
|
-
(locations) => {
|
|
154
|
+
getInvalidLocationIds: Selector<L, F, (string | number)[] | undefined> =
|
|
155
|
+
createSelector(this.getLocationsFromProps, (locations) => {
|
|
146
156
|
if (!locations) return undefined;
|
|
147
157
|
const invalid = [];
|
|
148
158
|
for (const location of locations) {
|
|
@@ -154,11 +164,10 @@ export default class FlowmapSelectors<L, F> {
|
|
|
154
164
|
}
|
|
155
165
|
}
|
|
156
166
|
return invalid.length > 0 ? invalid : undefined;
|
|
157
|
-
}
|
|
158
|
-
);
|
|
167
|
+
});
|
|
159
168
|
|
|
160
169
|
getLocations: Selector<L, F, Iterable<L> | undefined> = createSelector(
|
|
161
|
-
this.
|
|
170
|
+
this.getLocationsFromProps,
|
|
162
171
|
this.getInvalidLocationIds,
|
|
163
172
|
(locations, invalidIds) => {
|
|
164
173
|
if (!locations) return undefined;
|
|
@@ -175,17 +184,15 @@ export default class FlowmapSelectors<L, F> {
|
|
|
175
184
|
},
|
|
176
185
|
);
|
|
177
186
|
|
|
178
|
-
getLocationIds: Selector<L, F, Set<string> | undefined> =
|
|
179
|
-
this.getLocations,
|
|
180
|
-
(locations) => {
|
|
187
|
+
getLocationIds: Selector<L, F, Set<string | number> | undefined> =
|
|
188
|
+
createSelector(this.getLocations, (locations) => {
|
|
181
189
|
if (!locations) return undefined;
|
|
182
|
-
const ids = new Set<string>();
|
|
190
|
+
const ids = new Set<string | number>();
|
|
183
191
|
for (const id of locations) {
|
|
184
192
|
ids.add(this.accessors.getLocationId(id));
|
|
185
193
|
}
|
|
186
194
|
return ids;
|
|
187
|
-
}
|
|
188
|
-
);
|
|
195
|
+
});
|
|
189
196
|
|
|
190
197
|
getSelectedLocationsSet: Selector<L, F, Set<string> | undefined> =
|
|
191
198
|
createSelector(this.getSelectedLocations, (ids) =>
|
|
@@ -193,23 +200,27 @@ export default class FlowmapSelectors<L, F> {
|
|
|
193
200
|
);
|
|
194
201
|
|
|
195
202
|
getSortedFlowsForKnownLocations: Selector<L, F, F[] | undefined> =
|
|
196
|
-
createSelector(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
createSelector(
|
|
204
|
+
this.getFlowsFromProps,
|
|
205
|
+
this.getLocationIds,
|
|
206
|
+
(flows, ids) => {
|
|
207
|
+
if (!ids || !flows) return undefined;
|
|
208
|
+
const filtered = [];
|
|
209
|
+
for (const flow of flows) {
|
|
210
|
+
const srcId = this.accessors.getFlowOriginId(flow);
|
|
211
|
+
const dstId = this.accessors.getFlowDestId(flow);
|
|
212
|
+
if (ids.has(srcId) && ids.has(dstId)) {
|
|
213
|
+
filtered.push(flow);
|
|
214
|
+
}
|
|
204
215
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
return filtered.sort((a: F, b: F) =>
|
|
217
|
+
descending(
|
|
218
|
+
Math.abs(this.accessors.getFlowMagnitude(a)),
|
|
219
|
+
Math.abs(this.accessors.getFlowMagnitude(b)),
|
|
220
|
+
),
|
|
221
|
+
);
|
|
222
|
+
},
|
|
223
|
+
);
|
|
213
224
|
|
|
214
225
|
getActualTimeExtent: Selector<L, F, [Date, Date] | undefined> =
|
|
215
226
|
createSelector(this.getSortedFlowsForKnownLocations, (flows) => {
|
|
@@ -302,29 +313,33 @@ export default class FlowmapSelectors<L, F> {
|
|
|
302
313
|
},
|
|
303
314
|
);
|
|
304
315
|
|
|
305
|
-
getLocationsById: Selector<L, F, Map<string, L> | undefined> =
|
|
306
|
-
this.getLocationsHavingFlows,
|
|
307
|
-
(locations) => {
|
|
316
|
+
getLocationsById: Selector<L, F, Map<string | number, L> | undefined> =
|
|
317
|
+
createSelector(this.getLocationsHavingFlows, (locations) => {
|
|
308
318
|
if (!locations) return undefined;
|
|
309
|
-
const locationsById = new Map<string, L>();
|
|
319
|
+
const locationsById = new Map<string | number, L>();
|
|
310
320
|
for (const location of locations) {
|
|
311
321
|
locationsById.set(this.accessors.getLocationId(location), location);
|
|
312
322
|
}
|
|
313
323
|
return locationsById;
|
|
314
|
-
}
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
getClusterIndex: Selector<L, F, ClusterIndex<F> | undefined> = createSelector(
|
|
318
|
-
this.getLocationsHavingFlows,
|
|
319
|
-
this.getLocationsById,
|
|
320
|
-
this.getSortedFlowsForKnownLocations,
|
|
321
|
-
(locations, locationsById, flows) => {
|
|
322
|
-
if (!locations || !locationsById || !flows) return undefined;
|
|
324
|
+
});
|
|
323
325
|
|
|
326
|
+
getLocationWeightGetter: Selector<L, F, LocationWeightGetter | undefined> =
|
|
327
|
+
createSelector(this.getSortedFlowsForKnownLocations, (flows) => {
|
|
328
|
+
if (!flows) return undefined;
|
|
324
329
|
const getLocationWeight = makeLocationWeightGetter(
|
|
325
330
|
flows,
|
|
326
331
|
this.accessors.getFlowmapDataAccessors(),
|
|
327
332
|
);
|
|
333
|
+
return getLocationWeight;
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
getClusterLevels: Selector<L, F, ClusterLevels | undefined> = createSelector(
|
|
337
|
+
this.getClusterLevelsFromProps,
|
|
338
|
+
this.getLocationsHavingFlows,
|
|
339
|
+
this.getLocationWeightGetter,
|
|
340
|
+
(clusterLevelsFromProps, locations, getLocationWeight) => {
|
|
341
|
+
if (clusterLevelsFromProps) return clusterLevelsFromProps;
|
|
342
|
+
if (!locations || !getLocationWeight) return undefined;
|
|
328
343
|
const clusterLevels = clusterLocations(
|
|
329
344
|
locations,
|
|
330
345
|
this.accessors.getFlowmapDataAccessors(),
|
|
@@ -333,12 +348,24 @@ export default class FlowmapSelectors<L, F> {
|
|
|
333
348
|
maxZoom: MAX_CLUSTER_ZOOM_LEVEL,
|
|
334
349
|
},
|
|
335
350
|
);
|
|
351
|
+
return clusterLevels;
|
|
352
|
+
},
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
getClusterIndex: Selector<L, F, ClusterIndex<F> | undefined> = createSelector(
|
|
356
|
+
this.getLocationsById,
|
|
357
|
+
this.getLocationWeightGetter,
|
|
358
|
+
this.getClusterLevels,
|
|
359
|
+
(locationsById, getLocationWeight, clusterLevels) => {
|
|
360
|
+
if (!locationsById || !getLocationWeight || !clusterLevels)
|
|
361
|
+
return undefined;
|
|
362
|
+
|
|
336
363
|
const clusterIndex = buildIndex<F>(clusterLevels);
|
|
337
364
|
const {getLocationName, getLocationClusterName} =
|
|
338
365
|
this.accessors.getFlowmapDataAccessors();
|
|
339
366
|
|
|
340
367
|
// Adding meaningful names
|
|
341
|
-
const getName = (id: string) => {
|
|
368
|
+
const getName = (id: string | number) => {
|
|
342
369
|
const loc = locationsById.get(id);
|
|
343
370
|
if (loc) {
|
|
344
371
|
return getLocationName
|
|
@@ -418,7 +445,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
418
445
|
this.getAvailableClusterZoomLevels,
|
|
419
446
|
(clusterIndex, mapZoom, availableClusterZoomLevels) => {
|
|
420
447
|
if (!clusterIndex) return undefined;
|
|
421
|
-
if (!availableClusterZoomLevels) {
|
|
448
|
+
if (!availableClusterZoomLevels || mapZoom == null) {
|
|
422
449
|
return undefined;
|
|
423
450
|
}
|
|
424
451
|
|
|
@@ -431,12 +458,12 @@ export default class FlowmapSelectors<L, F> {
|
|
|
431
458
|
);
|
|
432
459
|
|
|
433
460
|
getClusterZoom = (state: FlowmapState, props: FlowmapData<L, F>) => {
|
|
434
|
-
const {
|
|
435
|
-
if (!
|
|
436
|
-
if (
|
|
461
|
+
const {settings} = state;
|
|
462
|
+
if (!settings.clusteringEnabled) return undefined;
|
|
463
|
+
if (settings.clusteringAuto || settings.clusteringLevel == null) {
|
|
437
464
|
return this._getClusterZoom(state, props);
|
|
438
465
|
}
|
|
439
|
-
return
|
|
466
|
+
return settings.clusteringLevel;
|
|
440
467
|
};
|
|
441
468
|
|
|
442
469
|
getLocationsForSearchBox: Selector<L, F, (L | Cluster)[] | undefined> =
|
|
@@ -489,7 +516,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
489
516
|
);
|
|
490
517
|
|
|
491
518
|
getDiffMode: Selector<L, F, boolean> = createSelector(
|
|
492
|
-
this.
|
|
519
|
+
this.getFlowsFromProps,
|
|
493
520
|
(flows) => {
|
|
494
521
|
if (flows) {
|
|
495
522
|
for (const f of flows) {
|
|
@@ -522,27 +549,28 @@ export default class FlowmapSelectors<L, F> {
|
|
|
522
549
|
},
|
|
523
550
|
);
|
|
524
551
|
|
|
525
|
-
getUnknownLocations: Selector<L, F, Set<string> | undefined> =
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
552
|
+
getUnknownLocations: Selector<L, F, Set<string | number> | undefined> =
|
|
553
|
+
createSelector(
|
|
554
|
+
this.getLocationIds,
|
|
555
|
+
this.getFlowsFromProps,
|
|
556
|
+
this.getSortedFlowsForKnownLocations,
|
|
557
|
+
(ids, flows, flowsForKnownLocations) => {
|
|
558
|
+
if (!ids || !flows) return undefined;
|
|
559
|
+
if (
|
|
560
|
+
flowsForKnownLocations
|
|
561
|
+
// && flows.length === flowsForKnownLocations.length
|
|
562
|
+
)
|
|
563
|
+
return undefined;
|
|
564
|
+
const missing = new Set<string | number>();
|
|
565
|
+
for (const flow of flows) {
|
|
566
|
+
if (!ids.has(this.accessors.getFlowOriginId(flow)))
|
|
567
|
+
missing.add(this.accessors.getFlowOriginId(flow));
|
|
568
|
+
if (!ids.has(this.accessors.getFlowDestId(flow)))
|
|
569
|
+
missing.add(this.accessors.getFlowDestId(flow));
|
|
570
|
+
}
|
|
571
|
+
return missing;
|
|
572
|
+
},
|
|
573
|
+
);
|
|
546
574
|
|
|
547
575
|
getSortedAggregatedFilteredFlows: Selector<
|
|
548
576
|
L,
|
|
@@ -662,15 +690,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
662
690
|
createSelector(
|
|
663
691
|
this.getViewport,
|
|
664
692
|
this.getMaxLocationCircleSize,
|
|
665
|
-
|
|
666
|
-
const pad = maxLocationCircleSize;
|
|
667
|
-
const bounds = new WebMercatorViewport({
|
|
668
|
-
...viewport,
|
|
669
|
-
width: viewport.width + pad * 2,
|
|
670
|
-
height: viewport.height + pad * 2,
|
|
671
|
-
}).getBounds();
|
|
672
|
-
return [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]];
|
|
673
|
-
},
|
|
693
|
+
getViewportBoundingBox,
|
|
674
694
|
);
|
|
675
695
|
|
|
676
696
|
getLocationsForZoom: Selector<L, F, Iterable<L> | ClusterNode[] | undefined> =
|
|
@@ -688,47 +708,50 @@ export default class FlowmapSelectors<L, F> {
|
|
|
688
708
|
},
|
|
689
709
|
);
|
|
690
710
|
|
|
691
|
-
getLocationTotals: Selector<
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if (d.internalCount != null) rv.internalCount += d.internalCount;
|
|
712
|
-
return rv;
|
|
711
|
+
getLocationTotals: Selector<
|
|
712
|
+
L,
|
|
713
|
+
F,
|
|
714
|
+
Map<string | number, LocationTotals> | undefined
|
|
715
|
+
> = createSelector(
|
|
716
|
+
this.getLocationsForZoom,
|
|
717
|
+
this.getSortedAggregatedFilteredFlows,
|
|
718
|
+
this.getSelectedLocationsSet,
|
|
719
|
+
this.getLocationFilterMode,
|
|
720
|
+
(locations, flows, selectedLocationsSet, locationFilterMode) => {
|
|
721
|
+
if (!flows) return undefined;
|
|
722
|
+
const totals = new Map<string | number, LocationTotals>();
|
|
723
|
+
const add = (
|
|
724
|
+
id: string | number,
|
|
725
|
+
d: Partial<LocationTotals>,
|
|
726
|
+
): LocationTotals => {
|
|
727
|
+
const rv = totals.get(id) ?? {
|
|
728
|
+
incomingCount: 0,
|
|
729
|
+
outgoingCount: 0,
|
|
730
|
+
internalCount: 0,
|
|
713
731
|
};
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
732
|
+
if (d.incomingCount != null) rv.incomingCount += d.incomingCount;
|
|
733
|
+
if (d.outgoingCount != null) rv.outgoingCount += d.outgoingCount;
|
|
734
|
+
if (d.internalCount != null) rv.internalCount += d.internalCount;
|
|
735
|
+
return rv;
|
|
736
|
+
};
|
|
737
|
+
for (const f of flows) {
|
|
738
|
+
if (
|
|
739
|
+
this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode)
|
|
740
|
+
) {
|
|
741
|
+
const originId = this.accessors.getFlowOriginId(f);
|
|
742
|
+
const destId = this.accessors.getFlowDestId(f);
|
|
743
|
+
const count = this.accessors.getFlowMagnitude(f);
|
|
744
|
+
if (originId === destId) {
|
|
745
|
+
totals.set(originId, add(originId, {internalCount: count}));
|
|
746
|
+
} else {
|
|
747
|
+
totals.set(originId, add(originId, {outgoingCount: count}));
|
|
748
|
+
totals.set(destId, add(destId, {incomingCount: count}));
|
|
727
749
|
}
|
|
728
750
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
751
|
+
}
|
|
752
|
+
return totals;
|
|
753
|
+
},
|
|
754
|
+
);
|
|
732
755
|
|
|
733
756
|
getLocationsTree: Selector<L, F, KDBushTree> = createSelector(
|
|
734
757
|
this.getLocationsForZoom,
|
|
@@ -762,7 +785,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
762
785
|
},
|
|
763
786
|
);
|
|
764
787
|
|
|
765
|
-
getLocationIdsInViewport: Selector<L, F, Set<string> | undefined> =
|
|
788
|
+
getLocationIdsInViewport: Selector<L, F, Set<string | number> | undefined> =
|
|
766
789
|
createSelectorCreator(
|
|
767
790
|
defaultMemoize,
|
|
768
791
|
// @ts-ignore
|
|
@@ -834,7 +857,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
834
857
|
state: FlowmapState,
|
|
835
858
|
props: FlowmapData<L, F>,
|
|
836
859
|
): [number, number] | undefined => {
|
|
837
|
-
if (state.
|
|
860
|
+
if (state.settings.adaptiveScalesEnabled) {
|
|
838
861
|
return this._getLocationTotalsForViewportExtent(state, props);
|
|
839
862
|
} else {
|
|
840
863
|
return this._getLocationTotalsExtent(state, props);
|
|
@@ -929,7 +952,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
929
952
|
state: FlowmapState,
|
|
930
953
|
props: FlowmapData<L, F>,
|
|
931
954
|
): [number, number] | undefined => {
|
|
932
|
-
if (state.
|
|
955
|
+
if (state.settings.adaptiveScalesEnabled) {
|
|
933
956
|
return this._getAdaptiveFlowMagnitudeExtent(state, props);
|
|
934
957
|
} else {
|
|
935
958
|
return this._getFlowMagnitudeExtent(state, props);
|
|
@@ -952,19 +975,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
952
975
|
|
|
953
976
|
getFlowThicknessScale = createSelector(
|
|
954
977
|
this.getFlowMagnitudeExtent,
|
|
955
|
-
|
|
956
|
-
if (!magnitudeExtent) return undefined;
|
|
957
|
-
return scaleLinear()
|
|
958
|
-
.range([0.025, 0.5])
|
|
959
|
-
.domain([
|
|
960
|
-
0,
|
|
961
|
-
// should support diff mode too
|
|
962
|
-
Math.max.apply(
|
|
963
|
-
null,
|
|
964
|
-
magnitudeExtent.map((x: number | undefined) => Math.abs(x || 0)),
|
|
965
|
-
),
|
|
966
|
-
]);
|
|
967
|
-
},
|
|
978
|
+
getFlowThicknessScale,
|
|
968
979
|
);
|
|
969
980
|
|
|
970
981
|
getCircleSizeScale = createSelector(
|
|
@@ -995,7 +1006,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
995
1006
|
this.getCircleSizeScale,
|
|
996
1007
|
this.getLocationTotals,
|
|
997
1008
|
(circleSizeScale, locationTotals) => {
|
|
998
|
-
return (locationId: string) => {
|
|
1009
|
+
return (locationId: string | number) => {
|
|
999
1010
|
const total = locationTotals?.get(locationId);
|
|
1000
1011
|
if (total && circleSizeScale) {
|
|
1001
1012
|
return (
|
|
@@ -1013,7 +1024,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1013
1024
|
this.getCircleSizeScale,
|
|
1014
1025
|
this.getLocationTotals,
|
|
1015
1026
|
(circleSizeScale, locationTotals) => {
|
|
1016
|
-
return (locationId: string) => {
|
|
1027
|
+
return (locationId: string | number) => {
|
|
1017
1028
|
const total = locationTotals?.get(locationId);
|
|
1018
1029
|
if (total && circleSizeScale) {
|
|
1019
1030
|
return (
|
|
@@ -1141,7 +1152,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1141
1152
|
getInCircleSize,
|
|
1142
1153
|
getOutCircleSize,
|
|
1143
1154
|
flowThicknessScale,
|
|
1144
|
-
state.
|
|
1155
|
+
state.settings.animationEnabled,
|
|
1145
1156
|
);
|
|
1146
1157
|
}
|
|
1147
1158
|
|
|
@@ -1149,10 +1160,10 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1149
1160
|
locations: (L | ClusterNode)[] | undefined,
|
|
1150
1161
|
flows: (F | AggregateFlow)[] | undefined,
|
|
1151
1162
|
flowmapColors: DiffColorsRGBA | ColorsRGBA,
|
|
1152
|
-
locationsById: Map<string, L | ClusterNode> | undefined,
|
|
1153
|
-
locationIdsInViewport: Set<string> | undefined,
|
|
1154
|
-
getInCircleSize: (locationId: string) => number,
|
|
1155
|
-
getOutCircleSize: (locationId: string) => number,
|
|
1163
|
+
locationsById: Map<string | number, L | ClusterNode> | undefined,
|
|
1164
|
+
locationIdsInViewport: Set<string | number> | undefined,
|
|
1165
|
+
getInCircleSize: (locationId: string | number) => number,
|
|
1166
|
+
getOutCircleSize: (locationId: string | number) => number,
|
|
1156
1167
|
flowThicknessScale: ScaleLinear<number, number, never> | undefined,
|
|
1157
1168
|
animationEnabled: boolean,
|
|
1158
1169
|
): LayersData {
|
|
@@ -1326,8 +1337,8 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1326
1337
|
|
|
1327
1338
|
isFlowInSelection(
|
|
1328
1339
|
flow: F | AggregateFlow,
|
|
1329
|
-
selectedLocationsSet: Set<string> | undefined,
|
|
1330
|
-
locationFilterMode
|
|
1340
|
+
selectedLocationsSet: Set<string | number> | undefined,
|
|
1341
|
+
locationFilterMode?: LocationFilterMode,
|
|
1331
1342
|
) {
|
|
1332
1343
|
const origin = this.accessors.getFlowOriginId(flow);
|
|
1333
1344
|
const dest = this.accessors.getFlowDestId(flow);
|
|
@@ -1373,8 +1384,8 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1373
1384
|
}
|
|
1374
1385
|
|
|
1375
1386
|
function calcLocationTotalsExtent(
|
|
1376
|
-
locationTotals: Map<string, LocationTotals> | undefined,
|
|
1377
|
-
locationIdsInViewport: Set<string> | undefined,
|
|
1387
|
+
locationTotals: Map<string | number, LocationTotals> | undefined,
|
|
1388
|
+
locationIdsInViewport: Set<string | number> | undefined,
|
|
1378
1389
|
) {
|
|
1379
1390
|
if (!locationTotals) return undefined;
|
|
1380
1391
|
let rv: [number, number] | undefined = undefined;
|
package/src/FlowmapState.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {LocationFilterMode, ViewportProps} from './types';
|
|
2
2
|
|
|
3
3
|
export interface FilterState {
|
|
4
|
-
selectedLocations
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
selectedLocations?: string[];
|
|
5
|
+
locationFilterMode?: LocationFilterMode;
|
|
6
|
+
selectedTimeRange?: [Date, Date];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface SettingsState {
|
|
@@ -23,7 +23,7 @@ export interface SettingsState {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export interface FlowmapState {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
filter?: FilterState;
|
|
27
|
+
settings: SettingsState;
|
|
28
28
|
viewport: ViewportProps;
|
|
29
29
|
}
|