@flowmap.gl/data 8.0.0-y.14 → 8.0.2
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/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-dev.log +670 -0
- package/LICENSE +2 -2
- package/dist/FlowmapAggregateAccessors.d.ts +4 -4
- package/dist/FlowmapAggregateAccessors.d.ts.map +1 -1
- package/dist/FlowmapAggregateAccessors.js +16 -9
- package/dist/FlowmapSelectors.d.ts +39 -85
- package/dist/FlowmapSelectors.d.ts.map +1 -1
- package/dist/FlowmapSelectors.js +128 -144
- package/dist/FlowmapState.d.ts +7 -5
- package/dist/FlowmapState.d.ts.map +1 -1
- package/dist/FlowmapState.js +6 -1
- package/dist/cluster/ClusterIndex.d.ts +4 -4
- package/dist/cluster/ClusterIndex.d.ts.map +1 -1
- package/dist/cluster/ClusterIndex.js +5 -17
- package/dist/cluster/cluster.d.ts +20 -1
- package/dist/cluster/cluster.d.ts.map +1 -1
- package/dist/cluster/cluster.js +108 -52
- package/dist/colors.d.ts +3 -3
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +19 -8
- package/dist/getViewStateForLocations.d.ts +2 -2
- package/dist/getViewStateForLocations.d.ts.map +1 -1
- package/dist/getViewStateForLocations.js +18 -8
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/provider/FlowmapDataProvider.d.ts +7 -2
- package/dist/provider/FlowmapDataProvider.d.ts.map +1 -1
- package/dist/provider/FlowmapDataProvider.js +11 -6
- package/dist/provider/LocalFlowmapDataProvider.d.ts +15 -4
- package/dist/provider/LocalFlowmapDataProvider.d.ts.map +1 -1
- package/dist/provider/LocalFlowmapDataProvider.js +98 -81
- package/dist/selector-functions.d.ts +10 -0
- package/dist/selector-functions.d.ts.map +1 -0
- package/dist/selector-functions.js +65 -0
- package/dist/time.d.ts.map +1 -1
- package/dist/time.js +6 -1
- package/dist/types.d.ts +18 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -4
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +6 -1
- package/package.json +22 -23
- package/src/FlowmapAggregateAccessors.ts +21 -10
- package/src/FlowmapSelectors.ts +271 -264
- package/src/FlowmapState.ts +13 -5
- package/src/cluster/ClusterIndex.ts +23 -28
- package/src/cluster/cluster.ts +145 -56
- package/src/colors.ts +13 -9
- package/src/getViewStateForLocations.ts +6 -0
- package/src/index.ts +9 -0
- package/src/provider/FlowmapDataProvider.ts +23 -7
- package/src/provider/LocalFlowmapDataProvider.ts +68 -5
- package/src/selector-functions.ts +93 -0
- package/src/time.ts +6 -0
- package/src/types.ts +21 -13
- package/src/util.ts +6 -0
package/src/FlowmapSelectors.ts
CHANGED
|
@@ -1,40 +1,29 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
3
|
-
* Copyright 2018-2020 Teralytics
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*
|
|
2
|
+
* Copyright (c) Flowmap.gl contributors
|
|
3
|
+
* Copyright (c) 2018-2020 Teralytics
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
17
5
|
*/
|
|
18
6
|
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {nest} from 'd3-collection';
|
|
22
|
-
import {ScaleLinear, scaleLinear, scaleSqrt} from 'd3-scale';
|
|
7
|
+
import {ascending, descending, extent, min, rollup} from 'd3-array';
|
|
8
|
+
import {ScaleLinear, scaleSqrt} from 'd3-scale';
|
|
23
9
|
import KDBush from 'kdbush';
|
|
24
10
|
import {
|
|
11
|
+
ParametricSelector,
|
|
25
12
|
createSelector,
|
|
26
13
|
createSelectorCreator,
|
|
27
14
|
defaultMemoize,
|
|
28
|
-
ParametricSelector,
|
|
29
15
|
} from 'reselect';
|
|
30
16
|
import {alea} from 'seedrandom';
|
|
31
|
-
import
|
|
17
|
+
import FlowmapAggregateAccessors from './FlowmapAggregateAccessors';
|
|
18
|
+
import {FlowmapState} from './FlowmapState';
|
|
32
19
|
import {
|
|
33
|
-
buildIndex,
|
|
34
20
|
ClusterIndex,
|
|
21
|
+
LocationWeightGetter,
|
|
22
|
+
buildIndex,
|
|
35
23
|
findAppropriateZoomLevel,
|
|
36
24
|
makeLocationWeightGetter,
|
|
37
25
|
} from './cluster/ClusterIndex';
|
|
26
|
+
import {clusterLocations} from './cluster/cluster';
|
|
38
27
|
import getColors, {
|
|
39
28
|
ColorsRGBA,
|
|
40
29
|
DiffColorsRGBA,
|
|
@@ -44,17 +33,21 @@ import getColors, {
|
|
|
44
33
|
isDiffColors,
|
|
45
34
|
isDiffColorsRGBA,
|
|
46
35
|
} from './colors';
|
|
47
|
-
import FlowmapAggregateAccessors from './FlowmapAggregateAccessors';
|
|
48
|
-
import {FlowmapState} from './FlowmapState';
|
|
49
36
|
import {
|
|
37
|
+
addClusterNames,
|
|
38
|
+
getFlowThicknessScale,
|
|
39
|
+
getViewportBoundingBox,
|
|
40
|
+
} from './selector-functions';
|
|
41
|
+
import {
|
|
42
|
+
TimeGranularityKey,
|
|
50
43
|
getTimeGranularityByKey,
|
|
51
44
|
getTimeGranularityByOrder,
|
|
52
45
|
getTimeGranularityForDate,
|
|
53
|
-
TimeGranularityKey,
|
|
54
46
|
} from './time';
|
|
55
47
|
import {
|
|
56
48
|
AggregateFlow,
|
|
57
49
|
Cluster,
|
|
50
|
+
ClusterLevels,
|
|
58
51
|
ClusterNode,
|
|
59
52
|
CountByTime,
|
|
60
53
|
FlowAccessors,
|
|
@@ -62,11 +55,10 @@ import {
|
|
|
62
55
|
FlowLinesLayerAttributes,
|
|
63
56
|
FlowmapData,
|
|
64
57
|
FlowmapDataAccessors,
|
|
65
|
-
isCluster,
|
|
66
|
-
isLocationClusterNode,
|
|
67
58
|
LayersData,
|
|
68
59
|
LocationFilterMode,
|
|
69
60
|
LocationTotals,
|
|
61
|
+
isLocationClusterNode,
|
|
70
62
|
} from './types';
|
|
71
63
|
|
|
72
64
|
const MAX_CLUSTER_ZOOM_LEVEL = 20;
|
|
@@ -78,7 +70,10 @@ export type Selector<L, F, T> = ParametricSelector<
|
|
|
78
70
|
T
|
|
79
71
|
>;
|
|
80
72
|
|
|
81
|
-
export default class FlowmapSelectors<
|
|
73
|
+
export default class FlowmapSelectors<
|
|
74
|
+
L extends Record<string, any>,
|
|
75
|
+
F extends Record<string, any>,
|
|
76
|
+
> {
|
|
82
77
|
accessors: FlowmapAggregateAccessors<L, F>;
|
|
83
78
|
|
|
84
79
|
constructor(accessors: FlowmapDataAccessors<L, F>) {
|
|
@@ -90,60 +85,71 @@ export default class FlowmapSelectors<L, F> {
|
|
|
90
85
|
this.accessors = new FlowmapAggregateAccessors(accessors);
|
|
91
86
|
}
|
|
92
87
|
|
|
93
|
-
|
|
88
|
+
getAggregateAccessors(): FlowmapAggregateAccessors<L, F> {
|
|
89
|
+
return this.accessors;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getFlowsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
94
93
|
props.flows;
|
|
95
|
-
|
|
94
|
+
getLocationsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
96
95
|
props.locations;
|
|
96
|
+
getClusterLevelsFromProps = (
|
|
97
|
+
state: FlowmapState,
|
|
98
|
+
props: FlowmapData<L, F>,
|
|
99
|
+
) => {
|
|
100
|
+
return props.clusterLevels;
|
|
101
|
+
};
|
|
97
102
|
getMaxTopFlowsDisplayNum = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
98
|
-
state.
|
|
103
|
+
state.settings.maxTopFlowsDisplayNum;
|
|
99
104
|
getSelectedLocations = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
100
|
-
state.
|
|
105
|
+
state.filter?.selectedLocations;
|
|
101
106
|
getLocationFilterMode = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
102
|
-
state.
|
|
107
|
+
state.filter?.locationFilterMode;
|
|
103
108
|
getClusteringEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
104
|
-
state.
|
|
109
|
+
state.settings.clusteringEnabled;
|
|
105
110
|
getLocationTotalsEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
106
|
-
state.
|
|
111
|
+
state.settings.locationTotalsEnabled;
|
|
112
|
+
getLocationLabelsEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
113
|
+
state.settings.locationLabelsEnabled;
|
|
107
114
|
getZoom = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
108
115
|
state.viewport.zoom;
|
|
109
116
|
getViewport = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
110
117
|
state.viewport;
|
|
111
118
|
getSelectedTimeRange = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
112
|
-
state.
|
|
119
|
+
state.filter?.selectedTimeRange;
|
|
113
120
|
|
|
114
121
|
getColorScheme: Selector<L, F, string | string[] | undefined> = (
|
|
115
122
|
state: FlowmapState,
|
|
116
123
|
props: FlowmapData<L, F>,
|
|
117
|
-
) => state.
|
|
124
|
+
) => state.settings.colorScheme;
|
|
118
125
|
|
|
119
126
|
getDarkMode: Selector<L, F, boolean> = (
|
|
120
127
|
state: FlowmapState,
|
|
121
128
|
props: FlowmapData<L, F>,
|
|
122
|
-
) => state.
|
|
129
|
+
) => state.settings.darkMode;
|
|
123
130
|
|
|
124
131
|
getFadeEnabled: Selector<L, F, boolean> = (
|
|
125
132
|
state: FlowmapState,
|
|
126
133
|
props: FlowmapData<L, F>,
|
|
127
|
-
) => state.
|
|
134
|
+
) => state.settings.fadeEnabled;
|
|
128
135
|
|
|
129
136
|
getFadeOpacityEnabled: Selector<L, F, boolean> = (
|
|
130
137
|
state: FlowmapState,
|
|
131
138
|
props: FlowmapData<L, F>,
|
|
132
|
-
) => state.
|
|
139
|
+
) => state.settings.fadeOpacityEnabled;
|
|
133
140
|
|
|
134
141
|
getFadeAmount: Selector<L, F, number> = (
|
|
135
142
|
state: FlowmapState,
|
|
136
143
|
props: FlowmapData<L, F>,
|
|
137
|
-
) => state.
|
|
144
|
+
) => state.settings.fadeAmount;
|
|
138
145
|
|
|
139
146
|
getAnimate: Selector<L, F, boolean> = (
|
|
140
147
|
state: FlowmapState,
|
|
141
148
|
props: FlowmapData<L, F>,
|
|
142
|
-
) => state.
|
|
149
|
+
) => state.settings.animationEnabled;
|
|
143
150
|
|
|
144
|
-
getInvalidLocationIds: Selector<L, F, string[] | undefined> =
|
|
145
|
-
this.
|
|
146
|
-
(locations) => {
|
|
151
|
+
getInvalidLocationIds: Selector<L, F, (string | number)[] | undefined> =
|
|
152
|
+
createSelector(this.getLocationsFromProps, (locations) => {
|
|
147
153
|
if (!locations) return undefined;
|
|
148
154
|
const invalid = [];
|
|
149
155
|
for (const location of locations) {
|
|
@@ -155,11 +161,10 @@ export default class FlowmapSelectors<L, F> {
|
|
|
155
161
|
}
|
|
156
162
|
}
|
|
157
163
|
return invalid.length > 0 ? invalid : undefined;
|
|
158
|
-
}
|
|
159
|
-
);
|
|
164
|
+
});
|
|
160
165
|
|
|
161
166
|
getLocations: Selector<L, F, Iterable<L> | undefined> = createSelector(
|
|
162
|
-
this.
|
|
167
|
+
this.getLocationsFromProps,
|
|
163
168
|
this.getInvalidLocationIds,
|
|
164
169
|
(locations, invalidIds) => {
|
|
165
170
|
if (!locations) return undefined;
|
|
@@ -176,41 +181,43 @@ export default class FlowmapSelectors<L, F> {
|
|
|
176
181
|
},
|
|
177
182
|
);
|
|
178
183
|
|
|
179
|
-
getLocationIds: Selector<L, F, Set<string> | undefined> =
|
|
180
|
-
this.getLocations,
|
|
181
|
-
(locations) => {
|
|
184
|
+
getLocationIds: Selector<L, F, Set<string | number> | undefined> =
|
|
185
|
+
createSelector(this.getLocations, (locations) => {
|
|
182
186
|
if (!locations) return undefined;
|
|
183
|
-
const ids = new Set<string>();
|
|
187
|
+
const ids = new Set<string | number>();
|
|
184
188
|
for (const id of locations) {
|
|
185
189
|
ids.add(this.accessors.getLocationId(id));
|
|
186
190
|
}
|
|
187
191
|
return ids;
|
|
188
|
-
}
|
|
189
|
-
);
|
|
192
|
+
});
|
|
190
193
|
|
|
191
|
-
getSelectedLocationsSet: Selector<L, F, Set<string> | undefined> =
|
|
194
|
+
getSelectedLocationsSet: Selector<L, F, Set<string | number> | undefined> =
|
|
192
195
|
createSelector(this.getSelectedLocations, (ids) =>
|
|
193
196
|
ids && ids.length > 0 ? new Set(ids) : undefined,
|
|
194
197
|
);
|
|
195
198
|
|
|
196
199
|
getSortedFlowsForKnownLocations: Selector<L, F, F[] | undefined> =
|
|
197
|
-
createSelector(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
createSelector(
|
|
201
|
+
this.getFlowsFromProps,
|
|
202
|
+
this.getLocationIds,
|
|
203
|
+
(flows, ids) => {
|
|
204
|
+
if (!ids || !flows) return undefined;
|
|
205
|
+
const filtered = [];
|
|
206
|
+
for (const flow of flows) {
|
|
207
|
+
const srcId = this.accessors.getFlowOriginId(flow);
|
|
208
|
+
const dstId = this.accessors.getFlowDestId(flow);
|
|
209
|
+
if (ids.has(srcId) && ids.has(dstId)) {
|
|
210
|
+
filtered.push(flow);
|
|
211
|
+
}
|
|
205
212
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
return filtered.sort((a: F, b: F) =>
|
|
214
|
+
descending(
|
|
215
|
+
Math.abs(this.accessors.getFlowMagnitude(a)),
|
|
216
|
+
Math.abs(this.accessors.getFlowMagnitude(b)),
|
|
217
|
+
),
|
|
218
|
+
);
|
|
219
|
+
},
|
|
220
|
+
);
|
|
214
221
|
|
|
215
222
|
getActualTimeExtent: Selector<L, F, [Date, Date] | undefined> =
|
|
216
223
|
createSelector(this.getSortedFlowsForKnownLocations, (flows) => {
|
|
@@ -303,29 +310,33 @@ export default class FlowmapSelectors<L, F> {
|
|
|
303
310
|
},
|
|
304
311
|
);
|
|
305
312
|
|
|
306
|
-
getLocationsById: Selector<L, F, Map<string, L> | undefined> =
|
|
307
|
-
this.getLocationsHavingFlows,
|
|
308
|
-
(locations) => {
|
|
313
|
+
getLocationsById: Selector<L, F, Map<string | number, L> | undefined> =
|
|
314
|
+
createSelector(this.getLocationsHavingFlows, (locations) => {
|
|
309
315
|
if (!locations) return undefined;
|
|
310
|
-
const locationsById = new Map<string, L>();
|
|
316
|
+
const locationsById = new Map<string | number, L>();
|
|
311
317
|
for (const location of locations) {
|
|
312
318
|
locationsById.set(this.accessors.getLocationId(location), location);
|
|
313
319
|
}
|
|
314
320
|
return locationsById;
|
|
315
|
-
}
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
getClusterIndex: Selector<L, F, ClusterIndex<F> | undefined> = createSelector(
|
|
319
|
-
this.getLocationsHavingFlows,
|
|
320
|
-
this.getLocationsById,
|
|
321
|
-
this.getSortedFlowsForKnownLocations,
|
|
322
|
-
(locations, locationsById, flows) => {
|
|
323
|
-
if (!locations || !locationsById || !flows) return undefined;
|
|
321
|
+
});
|
|
324
322
|
|
|
323
|
+
getLocationWeightGetter: Selector<L, F, LocationWeightGetter | undefined> =
|
|
324
|
+
createSelector(this.getSortedFlowsForKnownLocations, (flows) => {
|
|
325
|
+
if (!flows) return undefined;
|
|
325
326
|
const getLocationWeight = makeLocationWeightGetter(
|
|
326
327
|
flows,
|
|
327
328
|
this.accessors.getFlowmapDataAccessors(),
|
|
328
329
|
);
|
|
330
|
+
return getLocationWeight;
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
getClusterLevels: Selector<L, F, ClusterLevels | undefined> = createSelector(
|
|
334
|
+
this.getClusterLevelsFromProps,
|
|
335
|
+
this.getLocationsHavingFlows,
|
|
336
|
+
this.getLocationWeightGetter,
|
|
337
|
+
(clusterLevelsFromProps, locations, getLocationWeight) => {
|
|
338
|
+
if (clusterLevelsFromProps) return clusterLevelsFromProps;
|
|
339
|
+
if (!locations || !getLocationWeight) return undefined;
|
|
329
340
|
const clusterLevels = clusterLocations(
|
|
330
341
|
locations,
|
|
331
342
|
this.accessors.getFlowmapDataAccessors(),
|
|
@@ -334,47 +345,27 @@ export default class FlowmapSelectors<L, F> {
|
|
|
334
345
|
maxZoom: MAX_CLUSTER_ZOOM_LEVEL,
|
|
335
346
|
},
|
|
336
347
|
);
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// Adding meaningful names
|
|
342
|
-
const getName = (id: string) => {
|
|
343
|
-
const loc = locationsById.get(id);
|
|
344
|
-
if (loc) {
|
|
345
|
-
return getLocationName
|
|
346
|
-
? getLocationName(loc)
|
|
347
|
-
: this.accessors.getLocationId(loc) || id;
|
|
348
|
-
}
|
|
349
|
-
return `"${id}"`;
|
|
350
|
-
};
|
|
351
|
-
for (const level of clusterLevels) {
|
|
352
|
-
for (const node of level.nodes) {
|
|
353
|
-
// Here mutating the nodes (adding names)
|
|
354
|
-
if (isCluster(node)) {
|
|
355
|
-
const leaves = clusterIndex.expandCluster(node);
|
|
356
|
-
|
|
357
|
-
leaves.sort((a, b) =>
|
|
358
|
-
descending(getLocationWeight(a), getLocationWeight(b)),
|
|
359
|
-
);
|
|
348
|
+
return clusterLevels;
|
|
349
|
+
},
|
|
350
|
+
);
|
|
360
351
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
? `"${getName(otherId)}"`
|
|
369
|
-
: `${leaves.length - 1} others`
|
|
370
|
-
}`;
|
|
371
|
-
}
|
|
372
|
-
} else {
|
|
373
|
-
(node as any).name = getName(node.id);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
352
|
+
getClusterIndex: Selector<L, F, ClusterIndex<F> | undefined> = createSelector(
|
|
353
|
+
this.getLocationsById,
|
|
354
|
+
this.getLocationWeightGetter,
|
|
355
|
+
this.getClusterLevels,
|
|
356
|
+
(locationsById, getLocationWeight, clusterLevels) => {
|
|
357
|
+
if (!locationsById || !getLocationWeight || !clusterLevels)
|
|
358
|
+
return undefined;
|
|
377
359
|
|
|
360
|
+
const clusterIndex = buildIndex<F>(clusterLevels);
|
|
361
|
+
// Adding meaningful names
|
|
362
|
+
addClusterNames(
|
|
363
|
+
clusterIndex,
|
|
364
|
+
clusterLevels,
|
|
365
|
+
locationsById,
|
|
366
|
+
this.accessors.getFlowmapDataAccessors(),
|
|
367
|
+
getLocationWeight,
|
|
368
|
+
);
|
|
378
369
|
return clusterIndex;
|
|
379
370
|
},
|
|
380
371
|
);
|
|
@@ -390,7 +381,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
390
381
|
let maxZoom = Number.POSITIVE_INFINITY;
|
|
391
382
|
let minZoom = Number.NEGATIVE_INFINITY;
|
|
392
383
|
|
|
393
|
-
const adjust = (zoneId: string) => {
|
|
384
|
+
const adjust = (zoneId: string | number) => {
|
|
394
385
|
const cluster = clusterIndex.getClusterById(zoneId);
|
|
395
386
|
if (cluster) {
|
|
396
387
|
minZoom = Math.max(minZoom, cluster.zoom);
|
|
@@ -419,7 +410,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
419
410
|
this.getAvailableClusterZoomLevels,
|
|
420
411
|
(clusterIndex, mapZoom, availableClusterZoomLevels) => {
|
|
421
412
|
if (!clusterIndex) return undefined;
|
|
422
|
-
if (!availableClusterZoomLevels) {
|
|
413
|
+
if (!availableClusterZoomLevels || mapZoom == null) {
|
|
423
414
|
return undefined;
|
|
424
415
|
}
|
|
425
416
|
|
|
@@ -432,12 +423,12 @@ export default class FlowmapSelectors<L, F> {
|
|
|
432
423
|
);
|
|
433
424
|
|
|
434
425
|
getClusterZoom = (state: FlowmapState, props: FlowmapData<L, F>) => {
|
|
435
|
-
const {
|
|
436
|
-
if (!
|
|
437
|
-
if (
|
|
426
|
+
const {settings} = state;
|
|
427
|
+
if (!settings.clusteringEnabled) return undefined;
|
|
428
|
+
if (settings.clusteringAuto || settings.clusteringLevel == null) {
|
|
438
429
|
return this._getClusterZoom(state, props);
|
|
439
430
|
}
|
|
440
|
-
return
|
|
431
|
+
return settings.clusteringLevel;
|
|
441
432
|
};
|
|
442
433
|
|
|
443
434
|
getLocationsForSearchBox: Selector<L, F, (L | Cluster)[] | undefined> =
|
|
@@ -490,7 +481,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
490
481
|
);
|
|
491
482
|
|
|
492
483
|
getDiffMode: Selector<L, F, boolean> = createSelector(
|
|
493
|
-
this.
|
|
484
|
+
this.getFlowsFromProps,
|
|
494
485
|
(flows) => {
|
|
495
486
|
if (flows) {
|
|
496
487
|
for (const f of flows) {
|
|
@@ -523,27 +514,28 @@ export default class FlowmapSelectors<L, F> {
|
|
|
523
514
|
},
|
|
524
515
|
);
|
|
525
516
|
|
|
526
|
-
getUnknownLocations: Selector<L, F, Set<string> | undefined> =
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
517
|
+
getUnknownLocations: Selector<L, F, Set<string | number> | undefined> =
|
|
518
|
+
createSelector(
|
|
519
|
+
this.getLocationIds,
|
|
520
|
+
this.getFlowsFromProps,
|
|
521
|
+
this.getSortedFlowsForKnownLocations,
|
|
522
|
+
(ids, flows, flowsForKnownLocations) => {
|
|
523
|
+
if (!ids || !flows) return undefined;
|
|
524
|
+
if (
|
|
525
|
+
flowsForKnownLocations
|
|
526
|
+
// && flows.length === flowsForKnownLocations.length
|
|
527
|
+
)
|
|
528
|
+
return undefined;
|
|
529
|
+
const missing = new Set<string | number>();
|
|
530
|
+
for (const flow of flows) {
|
|
531
|
+
if (!ids.has(this.accessors.getFlowOriginId(flow)))
|
|
532
|
+
missing.add(this.accessors.getFlowOriginId(flow));
|
|
533
|
+
if (!ids.has(this.accessors.getFlowDestId(flow)))
|
|
534
|
+
missing.add(this.accessors.getFlowDestId(flow));
|
|
535
|
+
}
|
|
536
|
+
return missing;
|
|
537
|
+
},
|
|
538
|
+
);
|
|
547
539
|
|
|
548
540
|
getSortedAggregatedFilteredFlows: Selector<
|
|
549
541
|
L,
|
|
@@ -584,31 +576,34 @@ export default class FlowmapSelectors<L, F> {
|
|
|
584
576
|
},
|
|
585
577
|
);
|
|
586
578
|
|
|
587
|
-
getExpandedSelectedLocationsSet: Selector<
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
579
|
+
getExpandedSelectedLocationsSet: Selector<
|
|
580
|
+
L,
|
|
581
|
+
F,
|
|
582
|
+
Set<string | number> | undefined
|
|
583
|
+
> = createSelector(
|
|
584
|
+
this.getClusteringEnabled,
|
|
585
|
+
this.getSelectedLocationsSet,
|
|
586
|
+
this.getClusterIndex,
|
|
587
|
+
(clusteringEnabled, selectedLocations, clusterIndex) => {
|
|
588
|
+
if (!selectedLocations || !clusterIndex) {
|
|
589
|
+
return selectedLocations;
|
|
590
|
+
}
|
|
596
591
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
} else {
|
|
606
|
-
result.add(locationId);
|
|
592
|
+
const result = new Set<string | number>();
|
|
593
|
+
for (const locationId of selectedLocations) {
|
|
594
|
+
const cluster = clusterIndex.getClusterById(locationId);
|
|
595
|
+
if (cluster) {
|
|
596
|
+
const expanded = clusterIndex.expandCluster(cluster);
|
|
597
|
+
for (const id of expanded) {
|
|
598
|
+
result.add(id);
|
|
607
599
|
}
|
|
600
|
+
} else {
|
|
601
|
+
result.add(locationId);
|
|
608
602
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
603
|
+
}
|
|
604
|
+
return result;
|
|
605
|
+
},
|
|
606
|
+
);
|
|
612
607
|
|
|
613
608
|
getTotalCountsByTime: Selector<L, F, CountByTime[] | undefined> =
|
|
614
609
|
createSelector(
|
|
@@ -663,15 +658,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
663
658
|
createSelector(
|
|
664
659
|
this.getViewport,
|
|
665
660
|
this.getMaxLocationCircleSize,
|
|
666
|
-
|
|
667
|
-
const pad = maxLocationCircleSize;
|
|
668
|
-
const bounds = new WebMercatorViewport({
|
|
669
|
-
...viewport,
|
|
670
|
-
width: viewport.width + pad * 2,
|
|
671
|
-
height: viewport.height + pad * 2,
|
|
672
|
-
}).getBounds();
|
|
673
|
-
return [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]];
|
|
674
|
-
},
|
|
661
|
+
getViewportBoundingBox,
|
|
675
662
|
);
|
|
676
663
|
|
|
677
664
|
getLocationsForZoom: Selector<L, F, Iterable<L> | ClusterNode[] | undefined> =
|
|
@@ -689,47 +676,50 @@ export default class FlowmapSelectors<L, F> {
|
|
|
689
676
|
},
|
|
690
677
|
);
|
|
691
678
|
|
|
692
|
-
getLocationTotals: Selector<
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
if (d.internalCount != null) rv.internalCount += d.internalCount;
|
|
713
|
-
return rv;
|
|
679
|
+
getLocationTotals: Selector<
|
|
680
|
+
L,
|
|
681
|
+
F,
|
|
682
|
+
Map<string | number, LocationTotals> | undefined
|
|
683
|
+
> = createSelector(
|
|
684
|
+
this.getLocationsForZoom,
|
|
685
|
+
this.getSortedAggregatedFilteredFlows,
|
|
686
|
+
this.getSelectedLocationsSet,
|
|
687
|
+
this.getLocationFilterMode,
|
|
688
|
+
(locations, flows, selectedLocationsSet, locationFilterMode) => {
|
|
689
|
+
if (!flows) return undefined;
|
|
690
|
+
const totals = new Map<string | number, LocationTotals>();
|
|
691
|
+
const add = (
|
|
692
|
+
id: string | number,
|
|
693
|
+
d: Partial<LocationTotals>,
|
|
694
|
+
): LocationTotals => {
|
|
695
|
+
const rv = totals.get(id) ?? {
|
|
696
|
+
incomingCount: 0,
|
|
697
|
+
outgoingCount: 0,
|
|
698
|
+
internalCount: 0,
|
|
714
699
|
};
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
700
|
+
if (d.incomingCount != null) rv.incomingCount += d.incomingCount;
|
|
701
|
+
if (d.outgoingCount != null) rv.outgoingCount += d.outgoingCount;
|
|
702
|
+
if (d.internalCount != null) rv.internalCount += d.internalCount;
|
|
703
|
+
return rv;
|
|
704
|
+
};
|
|
705
|
+
for (const f of flows) {
|
|
706
|
+
if (
|
|
707
|
+
this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode)
|
|
708
|
+
) {
|
|
709
|
+
const originId = this.accessors.getFlowOriginId(f);
|
|
710
|
+
const destId = this.accessors.getFlowDestId(f);
|
|
711
|
+
const count = this.accessors.getFlowMagnitude(f);
|
|
712
|
+
if (originId === destId) {
|
|
713
|
+
totals.set(originId, add(originId, {internalCount: count}));
|
|
714
|
+
} else {
|
|
715
|
+
totals.set(originId, add(originId, {outgoingCount: count}));
|
|
716
|
+
totals.set(destId, add(destId, {incomingCount: count}));
|
|
728
717
|
}
|
|
729
718
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
719
|
+
}
|
|
720
|
+
return totals;
|
|
721
|
+
},
|
|
722
|
+
);
|
|
733
723
|
|
|
734
724
|
getLocationsTree: Selector<L, F, KDBushTree> = createSelector(
|
|
735
725
|
this.getLocationsForZoom,
|
|
@@ -737,14 +727,20 @@ export default class FlowmapSelectors<L, F> {
|
|
|
737
727
|
if (!locations) {
|
|
738
728
|
return undefined;
|
|
739
729
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
locations
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
730
|
+
const nodes = Array.isArray(locations)
|
|
731
|
+
? locations
|
|
732
|
+
: Array.from(locations);
|
|
733
|
+
const bush = new KDBush(nodes.length, 64, Float32Array);
|
|
734
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
735
|
+
const node = nodes[i];
|
|
736
|
+
bush.add(
|
|
737
|
+
lngX(this.accessors.getLocationLon(node)),
|
|
738
|
+
latY(this.accessors.getLocationLat(node)),
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
bush.finish();
|
|
742
|
+
bush.points = nodes;
|
|
743
|
+
return bush;
|
|
748
744
|
},
|
|
749
745
|
);
|
|
750
746
|
|
|
@@ -756,14 +752,16 @@ export default class FlowmapSelectors<L, F> {
|
|
|
756
752
|
const ids = this._getLocationsInBboxIndices(tree, bbox);
|
|
757
753
|
if (ids) {
|
|
758
754
|
return new Set(
|
|
759
|
-
ids.map((idx: number) =>
|
|
755
|
+
ids.map((idx: number) =>
|
|
756
|
+
this.accessors.getLocationId(tree.points[idx]),
|
|
757
|
+
) as Array<string>,
|
|
760
758
|
);
|
|
761
759
|
}
|
|
762
760
|
return undefined;
|
|
763
761
|
},
|
|
764
762
|
);
|
|
765
763
|
|
|
766
|
-
getLocationIdsInViewport: Selector<L, F, Set<string> | undefined> =
|
|
764
|
+
getLocationIdsInViewport: Selector<L, F, Set<string | number> | undefined> =
|
|
767
765
|
createSelectorCreator(
|
|
768
766
|
defaultMemoize,
|
|
769
767
|
// @ts-ignore
|
|
@@ -835,7 +833,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
835
833
|
state: FlowmapState,
|
|
836
834
|
props: FlowmapData<L, F>,
|
|
837
835
|
): [number, number] | undefined => {
|
|
838
|
-
if (state.
|
|
836
|
+
if (state.settings.adaptiveScalesEnabled) {
|
|
839
837
|
return this._getLocationTotalsForViewportExtent(state, props);
|
|
840
838
|
} else {
|
|
841
839
|
return this._getLocationTotalsExtent(state, props);
|
|
@@ -930,7 +928,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
930
928
|
state: FlowmapState,
|
|
931
929
|
props: FlowmapData<L, F>,
|
|
932
930
|
): [number, number] | undefined => {
|
|
933
|
-
if (state.
|
|
931
|
+
if (state.settings.adaptiveScalesEnabled) {
|
|
934
932
|
return this._getAdaptiveFlowMagnitudeExtent(state, props);
|
|
935
933
|
} else {
|
|
936
934
|
return this._getFlowMagnitudeExtent(state, props);
|
|
@@ -953,19 +951,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
953
951
|
|
|
954
952
|
getFlowThicknessScale = createSelector(
|
|
955
953
|
this.getFlowMagnitudeExtent,
|
|
956
|
-
|
|
957
|
-
if (!magnitudeExtent) return undefined;
|
|
958
|
-
return scaleLinear()
|
|
959
|
-
.range([0.025, 0.5])
|
|
960
|
-
.domain([
|
|
961
|
-
0,
|
|
962
|
-
// should support diff mode too
|
|
963
|
-
Math.max.apply(
|
|
964
|
-
null,
|
|
965
|
-
magnitudeExtent.map((x: number | undefined) => Math.abs(x || 0)),
|
|
966
|
-
),
|
|
967
|
-
]);
|
|
968
|
-
},
|
|
954
|
+
getFlowThicknessScale,
|
|
969
955
|
);
|
|
970
956
|
|
|
971
957
|
getCircleSizeScale = createSelector(
|
|
@@ -996,7 +982,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
996
982
|
this.getCircleSizeScale,
|
|
997
983
|
this.getLocationTotals,
|
|
998
984
|
(circleSizeScale, locationTotals) => {
|
|
999
|
-
return (locationId: string) => {
|
|
985
|
+
return (locationId: string | number) => {
|
|
1000
986
|
const total = locationTotals?.get(locationId);
|
|
1001
987
|
if (total && circleSizeScale) {
|
|
1002
988
|
return (
|
|
@@ -1014,7 +1000,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1014
1000
|
this.getCircleSizeScale,
|
|
1015
1001
|
this.getLocationTotals,
|
|
1016
1002
|
(circleSizeScale, locationTotals) => {
|
|
1017
|
-
return (locationId: string) => {
|
|
1003
|
+
return (locationId: string | number) => {
|
|
1018
1004
|
const total = locationTotals?.get(locationId);
|
|
1019
1005
|
if (total && circleSizeScale) {
|
|
1020
1006
|
return (
|
|
@@ -1089,6 +1075,15 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1089
1075
|
);
|
|
1090
1076
|
});
|
|
1091
1077
|
|
|
1078
|
+
getLocationOrClusterByIdGetter = createSelector(
|
|
1079
|
+
this.getClusterIndex,
|
|
1080
|
+
this.getLocationsById,
|
|
1081
|
+
(clusterIndex, locationsById) => {
|
|
1082
|
+
return (id: string | number) =>
|
|
1083
|
+
clusterIndex?.getClusterById(id) ?? locationsById?.get(id);
|
|
1084
|
+
},
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1092
1087
|
getLayersData: Selector<L, F, LayersData> = createSelector(
|
|
1093
1088
|
this.getLocationsForFlowmapLayer,
|
|
1094
1089
|
this.getFlowsForFlowmapLayer,
|
|
@@ -1099,6 +1094,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1099
1094
|
this.getOutCircleSizeGetter,
|
|
1100
1095
|
this.getFlowThicknessScale,
|
|
1101
1096
|
this.getAnimate,
|
|
1097
|
+
this.getLocationLabelsEnabled,
|
|
1102
1098
|
(
|
|
1103
1099
|
locations,
|
|
1104
1100
|
flows,
|
|
@@ -1109,6 +1105,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1109
1105
|
getOutCircleSize,
|
|
1110
1106
|
flowThicknessScale,
|
|
1111
1107
|
animationEnabled,
|
|
1108
|
+
locationLabelsEnabled,
|
|
1112
1109
|
) => {
|
|
1113
1110
|
return this._prepareLayersData(
|
|
1114
1111
|
locations,
|
|
@@ -1120,6 +1117,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1120
1117
|
getOutCircleSize,
|
|
1121
1118
|
flowThicknessScale,
|
|
1122
1119
|
animationEnabled,
|
|
1120
|
+
locationLabelsEnabled,
|
|
1123
1121
|
);
|
|
1124
1122
|
},
|
|
1125
1123
|
);
|
|
@@ -1133,6 +1131,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1133
1131
|
const getInCircleSize = this.getInCircleSizeGetter(state, props);
|
|
1134
1132
|
const getOutCircleSize = this.getOutCircleSizeGetter(state, props);
|
|
1135
1133
|
const flowThicknessScale = this.getFlowThicknessScale(state, props);
|
|
1134
|
+
const locationLabelsEnabled = this.getLocationLabelsEnabled(state, props);
|
|
1136
1135
|
return this._prepareLayersData(
|
|
1137
1136
|
locations,
|
|
1138
1137
|
flows,
|
|
@@ -1142,7 +1141,8 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1142
1141
|
getInCircleSize,
|
|
1143
1142
|
getOutCircleSize,
|
|
1144
1143
|
flowThicknessScale,
|
|
1145
|
-
state.
|
|
1144
|
+
state.settings.animationEnabled,
|
|
1145
|
+
locationLabelsEnabled,
|
|
1146
1146
|
);
|
|
1147
1147
|
}
|
|
1148
1148
|
|
|
@@ -1150,12 +1150,13 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1150
1150
|
locations: (L | ClusterNode)[] | undefined,
|
|
1151
1151
|
flows: (F | AggregateFlow)[] | undefined,
|
|
1152
1152
|
flowmapColors: DiffColorsRGBA | ColorsRGBA,
|
|
1153
|
-
locationsById: Map<string, L | ClusterNode> | undefined,
|
|
1154
|
-
locationIdsInViewport: Set<string> | undefined,
|
|
1155
|
-
getInCircleSize: (locationId: string) => number,
|
|
1156
|
-
getOutCircleSize: (locationId: string) => number,
|
|
1153
|
+
locationsById: Map<string | number, L | ClusterNode> | undefined,
|
|
1154
|
+
locationIdsInViewport: Set<string | number> | undefined,
|
|
1155
|
+
getInCircleSize: (locationId: string | number) => number,
|
|
1156
|
+
getOutCircleSize: (locationId: string | number) => number,
|
|
1157
1157
|
flowThicknessScale: ScaleLinear<number, number, never> | undefined,
|
|
1158
1158
|
animationEnabled: boolean,
|
|
1159
|
+
locationLabelsEnabled: boolean,
|
|
1159
1160
|
): LayersData {
|
|
1160
1161
|
if (!locations) locations = [];
|
|
1161
1162
|
if (!flows) flows = [];
|
|
@@ -1166,6 +1167,7 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1166
1167
|
getLocationId,
|
|
1167
1168
|
getLocationLon,
|
|
1168
1169
|
getLocationLat,
|
|
1170
|
+
getLocationName,
|
|
1169
1171
|
} = this.accessors;
|
|
1170
1172
|
|
|
1171
1173
|
const flowMagnitudeExtent = extent(flows, (f) => getFlowMagnitude(f)) as [
|
|
@@ -1297,6 +1299,9 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1297
1299
|
: {}),
|
|
1298
1300
|
},
|
|
1299
1301
|
},
|
|
1302
|
+
...(locationLabelsEnabled
|
|
1303
|
+
? {locationLabels: locations.map(getLocationName)}
|
|
1304
|
+
: undefined),
|
|
1300
1305
|
};
|
|
1301
1306
|
}
|
|
1302
1307
|
|
|
@@ -1327,8 +1332,8 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1327
1332
|
|
|
1328
1333
|
isFlowInSelection(
|
|
1329
1334
|
flow: F | AggregateFlow,
|
|
1330
|
-
selectedLocationsSet: Set<string> | undefined,
|
|
1331
|
-
locationFilterMode
|
|
1335
|
+
selectedLocationsSet: Set<string | number> | undefined,
|
|
1336
|
+
locationFilterMode?: LocationFilterMode,
|
|
1332
1337
|
) {
|
|
1333
1338
|
const origin = this.accessors.getFlowOriginId(flow);
|
|
1334
1339
|
const dest = this.accessors.getFlowDestId(flow);
|
|
@@ -1374,8 +1379,8 @@ export default class FlowmapSelectors<L, F> {
|
|
|
1374
1379
|
}
|
|
1375
1380
|
|
|
1376
1381
|
function calcLocationTotalsExtent(
|
|
1377
|
-
locationTotals: Map<string, LocationTotals> | undefined,
|
|
1378
|
-
locationIdsInViewport: Set<string> | undefined,
|
|
1382
|
+
locationTotals: Map<string | number, LocationTotals> | undefined,
|
|
1383
|
+
locationIdsInViewport: Set<string | number> | undefined,
|
|
1379
1384
|
) {
|
|
1380
1385
|
if (!locationTotals) return undefined;
|
|
1381
1386
|
let rv: [number, number] | undefined = undefined;
|
|
@@ -1421,10 +1426,9 @@ function aggregateFlows<F>(
|
|
|
1421
1426
|
flowAccessors: FlowAccessors<F>,
|
|
1422
1427
|
): AggregateFlow[] {
|
|
1423
1428
|
// Sum up flows with same origin, dest
|
|
1424
|
-
const byOriginDest =
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
.rollup((ff: F[]) => {
|
|
1429
|
+
const byOriginDest = rollup(
|
|
1430
|
+
flows,
|
|
1431
|
+
(ff: F[]) => {
|
|
1428
1432
|
const origin = flowAccessors.getFlowOriginId(ff[0]);
|
|
1429
1433
|
const dest = flowAccessors.getFlowDestId(ff[0]);
|
|
1430
1434
|
// const color = ff[0].color;
|
|
@@ -1443,11 +1447,14 @@ function aggregateFlows<F>(
|
|
|
1443
1447
|
};
|
|
1444
1448
|
// if (color) rv.color = color;
|
|
1445
1449
|
return rv;
|
|
1446
|
-
}
|
|
1447
|
-
.
|
|
1450
|
+
},
|
|
1451
|
+
flowAccessors.getFlowOriginId,
|
|
1452
|
+
flowAccessors.getFlowDestId,
|
|
1453
|
+
);
|
|
1454
|
+
|
|
1448
1455
|
const rv: AggregateFlow[] = [];
|
|
1449
|
-
for (const
|
|
1450
|
-
for (const
|
|
1456
|
+
for (const values of byOriginDest.values()) {
|
|
1457
|
+
for (const value of values.values()) {
|
|
1451
1458
|
rv.push(value);
|
|
1452
1459
|
}
|
|
1453
1460
|
}
|