@flowmap.gl/data 8.0.0-alpha.2 → 8.0.0-alpha.20
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 +16 -0
- package/dist/FlowmapAggregateAccessors.d.ts.map +1 -0
- package/dist/FlowmapAggregateAccessors.js +46 -0
- package/dist/{FlowMapSelectors.d.ts → FlowmapSelectors.d.ts} +87 -70
- package/dist/FlowmapSelectors.d.ts.map +1 -0
- package/dist/FlowmapSelectors.js +888 -0
- package/dist/{FlowMapState.d.ts → FlowmapState.d.ts} +11 -8
- package/dist/FlowmapState.d.ts.map +1 -0
- package/dist/FlowmapState.js +2 -0
- package/dist/cluster/cluster.d.ts +6 -5
- package/dist/cluster/cluster.d.ts.map +1 -1
- package/dist/cluster/cluster.js +76 -20
- package/dist/colors.d.ts +7 -7
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +55 -20
- package/dist/getViewStateForLocations.d.ts +18 -11
- package/dist/getViewStateForLocations.d.ts.map +1 -1
- package/dist/getViewStateForLocations.js +37 -23
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- package/dist/provider/FlowmapDataProvider.d.ts +21 -0
- package/dist/provider/FlowmapDataProvider.d.ts.map +1 -0
- package/dist/provider/FlowmapDataProvider.js +17 -0
- package/dist/provider/LocalFlowmapDataProvider.d.ts +21 -0
- package/dist/provider/LocalFlowmapDataProvider.d.ts.map +1 -0
- package/dist/provider/LocalFlowmapDataProvider.js +104 -0
- 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 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -4
- package/dist/util.d.ts +0 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -4
- package/package.json +10 -12
- package/src/{FlowMapAggregateAccessors.ts → FlowmapAggregateAccessors.ts} +14 -9
- package/src/{FlowMapSelectors.ts → FlowmapSelectors.ts} +349 -282
- package/src/{FlowMapState.ts → FlowmapState.ts} +10 -7
- package/src/cluster/cluster.ts +95 -34
- package/src/colors.ts +70 -28
- package/src/getViewStateForLocations.ts +56 -40
- package/src/index.ts +9 -6
- package/src/provider/{FlowMapDataProvider.ts → FlowmapDataProvider.ts} +27 -17
- package/src/provider/LocalFlowmapDataProvider.ts +129 -0
- package/src/selector-functions.ts +34 -0
- package/src/types.ts +15 -12
- package/src/util.ts +0 -4
- package/dist/FlowMapAggregateAccessors.d.ts +0 -15
- package/dist/FlowMapAggregateAccessors.d.ts.map +0 -1
- package/dist/FlowMapAggregateAccessors.js +0 -43
- package/dist/FlowMapSelectors.d.ts.map +0 -1
- package/dist/FlowMapSelectors.js +0 -834
- package/dist/FlowMapState.d.ts.map +0 -1
- package/dist/FlowMapState.js +0 -2
- package/dist/provider/FlowMapDataProvider.d.ts +0 -16
- package/dist/provider/FlowMapDataProvider.d.ts.map +0 -1
- package/dist/provider/FlowMapDataProvider.js +0 -17
- package/dist/provider/LocalFlowMapDataProvider.d.ts +0 -20
- package/dist/provider/LocalFlowMapDataProvider.d.ts.map +0 -1
- package/dist/provider/LocalFlowMapDataProvider.js +0 -87
- package/src/provider/LocalFlowMapDataProvider.ts +0 -105
|
@@ -16,10 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {nest} from 'd3-collection';
|
|
22
|
-
import {ScaleLinear, scaleLinear, scaleSqrt} from 'd3-scale';
|
|
19
|
+
import {ascending, descending, extent, min, rollup} from 'd3-array';
|
|
20
|
+
import {ScaleLinear, scaleSqrt} from 'd3-scale';
|
|
23
21
|
import KDBush from 'kdbush';
|
|
24
22
|
import {
|
|
25
23
|
createSelector,
|
|
@@ -33,6 +31,7 @@ import {
|
|
|
33
31
|
buildIndex,
|
|
34
32
|
ClusterIndex,
|
|
35
33
|
findAppropriateZoomLevel,
|
|
34
|
+
LocationWeightGetter,
|
|
36
35
|
makeLocationWeightGetter,
|
|
37
36
|
} from './cluster/ClusterIndex';
|
|
38
37
|
import getColors, {
|
|
@@ -44,8 +43,12 @@ import getColors, {
|
|
|
44
43
|
isDiffColors,
|
|
45
44
|
isDiffColorsRGBA,
|
|
46
45
|
} from './colors';
|
|
47
|
-
import
|
|
48
|
-
import {
|
|
46
|
+
import FlowmapAggregateAccessors from './FlowmapAggregateAccessors';
|
|
47
|
+
import {FlowmapState} from './FlowmapState';
|
|
48
|
+
import {
|
|
49
|
+
getFlowThicknessScale,
|
|
50
|
+
getViewportBoundingBox,
|
|
51
|
+
} from './selector-functions';
|
|
49
52
|
import {
|
|
50
53
|
getTimeGranularityByKey,
|
|
51
54
|
getTimeGranularityByOrder,
|
|
@@ -55,98 +58,108 @@ import {
|
|
|
55
58
|
import {
|
|
56
59
|
AggregateFlow,
|
|
57
60
|
Cluster,
|
|
61
|
+
ClusterLevels,
|
|
58
62
|
ClusterNode,
|
|
59
63
|
CountByTime,
|
|
60
64
|
FlowAccessors,
|
|
61
65
|
FlowCirclesLayerAttributes,
|
|
62
66
|
FlowLinesLayerAttributes,
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
FlowmapData,
|
|
68
|
+
FlowmapDataAccessors,
|
|
65
69
|
isCluster,
|
|
66
70
|
isLocationClusterNode,
|
|
67
71
|
LayersData,
|
|
68
72
|
LocationFilterMode,
|
|
69
73
|
LocationTotals,
|
|
70
74
|
} from './types';
|
|
71
|
-
import {flatMap} from './util';
|
|
72
75
|
|
|
73
76
|
const MAX_CLUSTER_ZOOM_LEVEL = 20;
|
|
74
|
-
const NUMBER_OF_FLOWS_TO_DISPLAY = 5000;
|
|
75
77
|
type KDBushTree = any;
|
|
76
78
|
|
|
77
79
|
export type Selector<L, F, T> = ParametricSelector<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
FlowmapState,
|
|
81
|
+
FlowmapData<L, F>,
|
|
80
82
|
T
|
|
81
83
|
>;
|
|
82
84
|
|
|
83
|
-
export default class
|
|
84
|
-
accessors:
|
|
85
|
+
export default class FlowmapSelectors<L, F> {
|
|
86
|
+
accessors: FlowmapAggregateAccessors<L, F>;
|
|
85
87
|
|
|
86
|
-
constructor(accessors:
|
|
87
|
-
this.accessors = new
|
|
88
|
+
constructor(accessors: FlowmapDataAccessors<L, F>) {
|
|
89
|
+
this.accessors = new FlowmapAggregateAccessors(accessors);
|
|
88
90
|
this.setAccessors(accessors);
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
setAccessors(accessors:
|
|
92
|
-
this.accessors = new
|
|
93
|
+
setAccessors(accessors: FlowmapDataAccessors<L, F>) {
|
|
94
|
+
this.accessors = new FlowmapAggregateAccessors(accessors);
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
|
|
97
|
+
getFlowsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
96
98
|
props.flows;
|
|
97
|
-
|
|
99
|
+
getLocationsFromProps = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
98
100
|
props.locations;
|
|
99
|
-
|
|
100
|
-
state
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
state.
|
|
107
|
-
|
|
101
|
+
getClusterLevelsFromProps = (
|
|
102
|
+
state: FlowmapState,
|
|
103
|
+
props: FlowmapData<L, F>,
|
|
104
|
+
) => {
|
|
105
|
+
return props.clusterLevels;
|
|
106
|
+
};
|
|
107
|
+
getMaxTopFlowsDisplayNum = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
108
|
+
state.settings.maxTopFlowsDisplayNum;
|
|
109
|
+
getSelectedLocations = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
110
|
+
state.filter?.selectedLocations;
|
|
111
|
+
getLocationFilterMode = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
112
|
+
state.filter?.locationFilterMode;
|
|
113
|
+
getClusteringEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
114
|
+
state.settings.clusteringEnabled;
|
|
115
|
+
getLocationTotalsEnabled = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
116
|
+
state.settings.locationTotalsEnabled;
|
|
117
|
+
getZoom = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
108
118
|
state.viewport.zoom;
|
|
109
|
-
getViewport = (state:
|
|
119
|
+
getViewport = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
110
120
|
state.viewport;
|
|
111
|
-
getSelectedTimeRange = (state:
|
|
112
|
-
state.
|
|
121
|
+
getSelectedTimeRange = (state: FlowmapState, props: FlowmapData<L, F>) =>
|
|
122
|
+
state.filter?.selectedTimeRange;
|
|
113
123
|
|
|
114
|
-
|
|
115
|
-
state:
|
|
116
|
-
props:
|
|
117
|
-
) => state.
|
|
124
|
+
getColorScheme: Selector<L, F, string | string[] | undefined> = (
|
|
125
|
+
state: FlowmapState,
|
|
126
|
+
props: FlowmapData<L, F>,
|
|
127
|
+
) => state.settings.colorScheme;
|
|
118
128
|
|
|
119
129
|
getDarkMode: Selector<L, F, boolean> = (
|
|
120
|
-
state:
|
|
121
|
-
props:
|
|
122
|
-
) => state.
|
|
130
|
+
state: FlowmapState,
|
|
131
|
+
props: FlowmapData<L, F>,
|
|
132
|
+
) => state.settings.darkMode;
|
|
123
133
|
|
|
124
134
|
getFadeEnabled: Selector<L, F, boolean> = (
|
|
125
|
-
state:
|
|
126
|
-
props:
|
|
127
|
-
) => state.
|
|
135
|
+
state: FlowmapState,
|
|
136
|
+
props: FlowmapData<L, F>,
|
|
137
|
+
) => state.settings.fadeEnabled;
|
|
138
|
+
|
|
139
|
+
getFadeOpacityEnabled: Selector<L, F, boolean> = (
|
|
140
|
+
state: FlowmapState,
|
|
141
|
+
props: FlowmapData<L, F>,
|
|
142
|
+
) => state.settings.fadeOpacityEnabled;
|
|
128
143
|
|
|
129
144
|
getFadeAmount: Selector<L, F, number> = (
|
|
130
|
-
state:
|
|
131
|
-
props:
|
|
132
|
-
) => state.
|
|
145
|
+
state: FlowmapState,
|
|
146
|
+
props: FlowmapData<L, F>,
|
|
147
|
+
) => state.settings.fadeAmount;
|
|
133
148
|
|
|
134
149
|
getAnimate: Selector<L, F, boolean> = (
|
|
135
|
-
state:
|
|
136
|
-
props:
|
|
137
|
-
) => state.
|
|
150
|
+
state: FlowmapState,
|
|
151
|
+
props: FlowmapData<L, F>,
|
|
152
|
+
) => state.settings.animationEnabled;
|
|
138
153
|
|
|
139
154
|
getInvalidLocationIds: Selector<L, F, string[] | undefined> = createSelector(
|
|
140
|
-
this.
|
|
155
|
+
this.getLocationsFromProps,
|
|
141
156
|
(locations) => {
|
|
142
157
|
if (!locations) return undefined;
|
|
143
158
|
const invalid = [];
|
|
144
159
|
for (const location of locations) {
|
|
145
160
|
const id = this.accessors.getLocationId(location);
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
NaN,
|
|
149
|
-
];
|
|
161
|
+
const lon = this.accessors.getLocationLon(location);
|
|
162
|
+
const lat = this.accessors.getLocationLat(location);
|
|
150
163
|
if (!(-90 <= lat && lat <= 90) || !(-180 <= lon && lon <= 180)) {
|
|
151
164
|
invalid.push(id);
|
|
152
165
|
}
|
|
@@ -155,25 +168,34 @@ export default class FlowMapSelectors<L, F> {
|
|
|
155
168
|
},
|
|
156
169
|
);
|
|
157
170
|
|
|
158
|
-
getLocations: Selector<L, F, L
|
|
159
|
-
this.
|
|
171
|
+
getLocations: Selector<L, F, Iterable<L> | undefined> = createSelector(
|
|
172
|
+
this.getLocationsFromProps,
|
|
160
173
|
this.getInvalidLocationIds,
|
|
161
174
|
(locations, invalidIds) => {
|
|
162
175
|
if (!locations) return undefined;
|
|
163
176
|
if (!invalidIds || invalidIds.length === 0) return locations;
|
|
164
177
|
const invalid = new Set(invalidIds);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
const filtered: L[] = [];
|
|
179
|
+
for (const location of locations) {
|
|
180
|
+
const id = this.accessors.getLocationId(location);
|
|
181
|
+
if (!invalid.has(id)) {
|
|
182
|
+
filtered.push(location);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return filtered;
|
|
168
186
|
},
|
|
169
187
|
);
|
|
170
188
|
|
|
171
189
|
getLocationIds: Selector<L, F, Set<string> | undefined> = createSelector(
|
|
172
190
|
this.getLocations,
|
|
173
|
-
(locations) =>
|
|
174
|
-
locations
|
|
175
|
-
|
|
176
|
-
|
|
191
|
+
(locations) => {
|
|
192
|
+
if (!locations) return undefined;
|
|
193
|
+
const ids = new Set<string>();
|
|
194
|
+
for (const id of locations) {
|
|
195
|
+
ids.add(this.accessors.getLocationId(id));
|
|
196
|
+
}
|
|
197
|
+
return ids;
|
|
198
|
+
},
|
|
177
199
|
);
|
|
178
200
|
|
|
179
201
|
getSelectedLocationsSet: Selector<L, F, Set<string> | undefined> =
|
|
@@ -182,21 +204,27 @@ export default class FlowMapSelectors<L, F> {
|
|
|
182
204
|
);
|
|
183
205
|
|
|
184
206
|
getSortedFlowsForKnownLocations: Selector<L, F, F[] | undefined> =
|
|
185
|
-
createSelector(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
207
|
+
createSelector(
|
|
208
|
+
this.getFlowsFromProps,
|
|
209
|
+
this.getLocationIds,
|
|
210
|
+
(flows, ids) => {
|
|
211
|
+
if (!ids || !flows) return undefined;
|
|
212
|
+
const filtered = [];
|
|
213
|
+
for (const flow of flows) {
|
|
214
|
+
const srcId = this.accessors.getFlowOriginId(flow);
|
|
215
|
+
const dstId = this.accessors.getFlowDestId(flow);
|
|
216
|
+
if (ids.has(srcId) && ids.has(dstId)) {
|
|
217
|
+
filtered.push(flow);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return filtered.sort((a: F, b: F) =>
|
|
194
221
|
descending(
|
|
195
222
|
Math.abs(this.accessors.getFlowMagnitude(a)),
|
|
196
223
|
Math.abs(this.accessors.getFlowMagnitude(b)),
|
|
197
224
|
),
|
|
198
225
|
);
|
|
199
|
-
|
|
226
|
+
},
|
|
227
|
+
);
|
|
200
228
|
|
|
201
229
|
getActualTimeExtent: Selector<L, F, [Date, Date] | undefined> =
|
|
202
230
|
createSelector(this.getSortedFlowsForKnownLocations, (flows) => {
|
|
@@ -268,55 +296,79 @@ export default class FlowMapSelectors<L, F> {
|
|
|
268
296
|
},
|
|
269
297
|
);
|
|
270
298
|
|
|
271
|
-
getLocationsHavingFlows: Selector<L, F, L
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
299
|
+
getLocationsHavingFlows: Selector<L, F, Iterable<L> | undefined> =
|
|
300
|
+
createSelector(
|
|
301
|
+
this.getSortedFlowsForKnownLocations,
|
|
302
|
+
this.getLocations,
|
|
303
|
+
(flows, locations) => {
|
|
304
|
+
if (!locations || !flows) return locations;
|
|
305
|
+
const withFlows = new Set();
|
|
306
|
+
for (const flow of flows) {
|
|
307
|
+
withFlows.add(this.accessors.getFlowOriginId(flow));
|
|
308
|
+
withFlows.add(this.accessors.getFlowDestId(flow));
|
|
309
|
+
}
|
|
310
|
+
const filtered = [];
|
|
311
|
+
for (const location of locations) {
|
|
312
|
+
if (withFlows.has(this.accessors.getLocationId(location))) {
|
|
313
|
+
filtered.push(location);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return filtered;
|
|
317
|
+
},
|
|
318
|
+
);
|
|
286
319
|
|
|
287
320
|
getLocationsById: Selector<L, F, Map<string, L> | undefined> = createSelector(
|
|
288
321
|
this.getLocationsHavingFlows,
|
|
289
322
|
(locations) => {
|
|
290
323
|
if (!locations) return undefined;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
.
|
|
294
|
-
|
|
324
|
+
const locationsById = new Map<string, L>();
|
|
325
|
+
for (const location of locations) {
|
|
326
|
+
locationsById.set(this.accessors.getLocationId(location), location);
|
|
327
|
+
}
|
|
328
|
+
return locationsById;
|
|
295
329
|
},
|
|
296
330
|
);
|
|
297
331
|
|
|
298
|
-
|
|
299
|
-
this.
|
|
300
|
-
|
|
301
|
-
this.getSortedFlowsForKnownLocations,
|
|
302
|
-
(locations, locationsById, flows) => {
|
|
303
|
-
if (!locations || !locationsById || !flows) return undefined;
|
|
304
|
-
|
|
332
|
+
getLocationWeightGetter: Selector<L, F, LocationWeightGetter | undefined> =
|
|
333
|
+
createSelector(this.getSortedFlowsForKnownLocations, (flows) => {
|
|
334
|
+
if (!flows) return undefined;
|
|
305
335
|
const getLocationWeight = makeLocationWeightGetter(
|
|
306
336
|
flows,
|
|
307
|
-
this.accessors.
|
|
337
|
+
this.accessors.getFlowmapDataAccessors(),
|
|
308
338
|
);
|
|
339
|
+
return getLocationWeight;
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
getClusterLevels: Selector<L, F, ClusterLevels | undefined> = createSelector(
|
|
343
|
+
this.getClusterLevelsFromProps,
|
|
344
|
+
this.getLocationsHavingFlows,
|
|
345
|
+
this.getLocationWeightGetter,
|
|
346
|
+
(clusterLevelsFromProps, locations, getLocationWeight) => {
|
|
347
|
+
if (clusterLevelsFromProps) return clusterLevelsFromProps;
|
|
348
|
+
if (!locations || !getLocationWeight) return undefined;
|
|
309
349
|
const clusterLevels = clusterLocations(
|
|
310
350
|
locations,
|
|
311
|
-
this.accessors.
|
|
351
|
+
this.accessors.getFlowmapDataAccessors(),
|
|
312
352
|
getLocationWeight,
|
|
313
353
|
{
|
|
314
354
|
maxZoom: MAX_CLUSTER_ZOOM_LEVEL,
|
|
315
355
|
},
|
|
316
356
|
);
|
|
357
|
+
return clusterLevels;
|
|
358
|
+
},
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
getClusterIndex: Selector<L, F, ClusterIndex<F> | undefined> = createSelector(
|
|
362
|
+
this.getLocationsById,
|
|
363
|
+
this.getLocationWeightGetter,
|
|
364
|
+
this.getClusterLevels,
|
|
365
|
+
(locationsById, getLocationWeight, clusterLevels) => {
|
|
366
|
+
if (!locationsById || !getLocationWeight || !clusterLevels)
|
|
367
|
+
return undefined;
|
|
368
|
+
|
|
317
369
|
const clusterIndex = buildIndex<F>(clusterLevels);
|
|
318
370
|
const {getLocationName, getLocationClusterName} =
|
|
319
|
-
this.accessors.
|
|
371
|
+
this.accessors.getFlowmapDataAccessors();
|
|
320
372
|
|
|
321
373
|
// Adding meaningful names
|
|
322
374
|
const getName = (id: string) => {
|
|
@@ -399,7 +451,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
399
451
|
this.getAvailableClusterZoomLevels,
|
|
400
452
|
(clusterIndex, mapZoom, availableClusterZoomLevels) => {
|
|
401
453
|
if (!clusterIndex) return undefined;
|
|
402
|
-
if (!availableClusterZoomLevels) {
|
|
454
|
+
if (!availableClusterZoomLevels || mapZoom == null) {
|
|
403
455
|
return undefined;
|
|
404
456
|
}
|
|
405
457
|
|
|
@@ -411,13 +463,13 @@ export default class FlowMapSelectors<L, F> {
|
|
|
411
463
|
},
|
|
412
464
|
);
|
|
413
465
|
|
|
414
|
-
getClusterZoom = (state:
|
|
415
|
-
const {
|
|
416
|
-
if (!
|
|
417
|
-
if (
|
|
466
|
+
getClusterZoom = (state: FlowmapState, props: FlowmapData<L, F>) => {
|
|
467
|
+
const {settings} = state;
|
|
468
|
+
if (!settings.clusteringEnabled) return undefined;
|
|
469
|
+
if (settings.clusteringAuto || settings.clusteringLevel == null) {
|
|
418
470
|
return this._getClusterZoom(state, props);
|
|
419
471
|
}
|
|
420
|
-
return
|
|
472
|
+
return settings.clusteringLevel;
|
|
421
473
|
};
|
|
422
474
|
|
|
423
475
|
getLocationsForSearchBox: Selector<L, F, (L | Cluster)[] | undefined> =
|
|
@@ -435,7 +487,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
435
487
|
clusterIndex,
|
|
436
488
|
) => {
|
|
437
489
|
if (!locations) return undefined;
|
|
438
|
-
let result: (L | Cluster)[] = locations;
|
|
490
|
+
let result: (L | Cluster)[] = Array.from(locations);
|
|
439
491
|
// if (clusteringEnabled) {
|
|
440
492
|
// if (clusterIndex) {
|
|
441
493
|
// const zoomItems = clusterIndex.getClusterNodesFor(clusterZoom);
|
|
@@ -445,7 +497,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
445
497
|
// }
|
|
446
498
|
// }
|
|
447
499
|
|
|
448
|
-
if (
|
|
500
|
+
if (clusterIndex && selectedLocations) {
|
|
449
501
|
const toAppend = [];
|
|
450
502
|
for (const id of selectedLocations) {
|
|
451
503
|
const cluster = clusterIndex.getClusterById(id);
|
|
@@ -470,46 +522,48 @@ export default class FlowMapSelectors<L, F> {
|
|
|
470
522
|
);
|
|
471
523
|
|
|
472
524
|
getDiffMode: Selector<L, F, boolean> = createSelector(
|
|
473
|
-
this.
|
|
525
|
+
this.getFlowsFromProps,
|
|
474
526
|
(flows) => {
|
|
475
|
-
if (
|
|
476
|
-
flows
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
527
|
+
if (flows) {
|
|
528
|
+
for (const f of flows) {
|
|
529
|
+
if (this.accessors.getFlowMagnitude(f) < 0) {
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
480
533
|
}
|
|
481
534
|
return false;
|
|
482
535
|
},
|
|
483
536
|
);
|
|
484
537
|
|
|
485
|
-
|
|
538
|
+
_getFlowmapColors = createSelector(
|
|
486
539
|
this.getDiffMode,
|
|
487
|
-
this.
|
|
540
|
+
this.getColorScheme,
|
|
488
541
|
this.getDarkMode,
|
|
489
542
|
this.getFadeEnabled,
|
|
543
|
+
this.getFadeOpacityEnabled,
|
|
490
544
|
this.getFadeAmount,
|
|
491
545
|
this.getAnimate,
|
|
492
546
|
getColors,
|
|
493
547
|
);
|
|
494
548
|
|
|
495
|
-
|
|
496
|
-
this.
|
|
497
|
-
(
|
|
498
|
-
return isDiffColors(
|
|
499
|
-
? getDiffColorsRGBA(
|
|
500
|
-
: getColorsRGBA(
|
|
549
|
+
getFlowmapColorsRGBA = createSelector(
|
|
550
|
+
this._getFlowmapColors,
|
|
551
|
+
(flowmapColors) => {
|
|
552
|
+
return isDiffColors(flowmapColors)
|
|
553
|
+
? getDiffColorsRGBA(flowmapColors)
|
|
554
|
+
: getColorsRGBA(flowmapColors);
|
|
501
555
|
},
|
|
502
556
|
);
|
|
503
557
|
|
|
504
558
|
getUnknownLocations: Selector<L, F, Set<string> | undefined> = createSelector(
|
|
505
559
|
this.getLocationIds,
|
|
506
|
-
this.
|
|
560
|
+
this.getFlowsFromProps,
|
|
507
561
|
this.getSortedFlowsForKnownLocations,
|
|
508
562
|
(ids, flows, flowsForKnownLocations) => {
|
|
509
563
|
if (!ids || !flows) return undefined;
|
|
510
564
|
if (
|
|
511
|
-
flowsForKnownLocations
|
|
512
|
-
flows.length === flowsForKnownLocations.length
|
|
565
|
+
flowsForKnownLocations
|
|
566
|
+
// && flows.length === flowsForKnownLocations.length
|
|
513
567
|
)
|
|
514
568
|
return undefined;
|
|
515
569
|
const missing = new Set<string>();
|
|
@@ -544,12 +598,12 @@ export default class FlowMapSelectors<L, F> {
|
|
|
544
598
|
// : flows,
|
|
545
599
|
flows,
|
|
546
600
|
clusterZoom,
|
|
547
|
-
this.accessors.
|
|
601
|
+
this.accessors.getFlowmapDataAccessors(),
|
|
548
602
|
);
|
|
549
603
|
} else {
|
|
550
604
|
aggregated = aggregateFlows(
|
|
551
605
|
flows,
|
|
552
|
-
this.accessors.
|
|
606
|
+
this.accessors.getFlowmapDataAccessors(),
|
|
553
607
|
);
|
|
554
608
|
}
|
|
555
609
|
aggregated.sort((a, b) =>
|
|
@@ -562,33 +616,6 @@ export default class FlowMapSelectors<L, F> {
|
|
|
562
616
|
},
|
|
563
617
|
);
|
|
564
618
|
|
|
565
|
-
getFlowMagnitudeExtent: Selector<L, F, [number, number] | undefined> =
|
|
566
|
-
createSelector(
|
|
567
|
-
this.getSortedAggregatedFilteredFlows,
|
|
568
|
-
this.getSelectedLocationsSet,
|
|
569
|
-
this.getLocationFilterMode,
|
|
570
|
-
(flows, selectedLocationsSet, locationFilterMode) => {
|
|
571
|
-
if (!flows) return undefined;
|
|
572
|
-
let rv: [number, number] | undefined = undefined;
|
|
573
|
-
for (const f of flows) {
|
|
574
|
-
if (
|
|
575
|
-
this.accessors.getFlowOriginId(f) !==
|
|
576
|
-
this.accessors.getFlowDestId(f) &&
|
|
577
|
-
this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode)
|
|
578
|
-
) {
|
|
579
|
-
const count = this.accessors.getFlowMagnitude(f);
|
|
580
|
-
if (rv == null) {
|
|
581
|
-
rv = [count, count];
|
|
582
|
-
} else {
|
|
583
|
-
if (count < rv[0]) rv[0] = count;
|
|
584
|
-
if (count > rv[1]) rv[1] = count;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return rv;
|
|
589
|
-
},
|
|
590
|
-
);
|
|
591
|
-
|
|
592
619
|
getExpandedSelectedLocationsSet: Selector<L, F, Set<string> | undefined> =
|
|
593
620
|
createSelector(
|
|
594
621
|
this.getClusteringEnabled,
|
|
@@ -668,18 +695,10 @@ export default class FlowMapSelectors<L, F> {
|
|
|
668
695
|
createSelector(
|
|
669
696
|
this.getViewport,
|
|
670
697
|
this.getMaxLocationCircleSize,
|
|
671
|
-
|
|
672
|
-
const pad = maxLocationCircleSize;
|
|
673
|
-
return bounds(
|
|
674
|
-
[viewport.longitude, viewport.latitude],
|
|
675
|
-
viewport.zoom,
|
|
676
|
-
[viewport.width + pad * 2, viewport.height + pad * 2],
|
|
677
|
-
512,
|
|
678
|
-
);
|
|
679
|
-
},
|
|
698
|
+
getViewportBoundingBox,
|
|
680
699
|
);
|
|
681
700
|
|
|
682
|
-
getLocationsForZoom: Selector<L, F, L
|
|
701
|
+
getLocationsForZoom: Selector<L, F, Iterable<L> | ClusterNode[] | undefined> =
|
|
683
702
|
createSelector(
|
|
684
703
|
this.getClusteringEnabled,
|
|
685
704
|
this.getLocationsHavingFlows,
|
|
@@ -746,17 +765,9 @@ export default class FlowMapSelectors<L, F> {
|
|
|
746
765
|
// @ts-ignore
|
|
747
766
|
locations,
|
|
748
767
|
(location: L | ClusterNode) =>
|
|
749
|
-
lngX(
|
|
750
|
-
isLocationClusterNode(location)
|
|
751
|
-
? location.centroid[0]
|
|
752
|
-
: this.accessors.getLocationCentroid(location)[0],
|
|
753
|
-
),
|
|
768
|
+
lngX(this.accessors.getLocationLon(location)),
|
|
754
769
|
(location: L | ClusterNode) =>
|
|
755
|
-
latY(
|
|
756
|
-
isLocationClusterNode(location)
|
|
757
|
-
? location.centroid[1]
|
|
758
|
-
: this.accessors.getLocationCentroid(location)[1],
|
|
759
|
-
),
|
|
770
|
+
latY(this.accessors.getLocationLat(location)),
|
|
760
771
|
);
|
|
761
772
|
},
|
|
762
773
|
);
|
|
@@ -845,27 +856,29 @@ export default class FlowMapSelectors<L, F> {
|
|
|
845
856
|
);
|
|
846
857
|
|
|
847
858
|
getLocationTotalsExtent = (
|
|
848
|
-
state:
|
|
849
|
-
props:
|
|
859
|
+
state: FlowmapState,
|
|
860
|
+
props: FlowmapData<L, F>,
|
|
850
861
|
): [number, number] | undefined => {
|
|
851
|
-
if (state.
|
|
862
|
+
if (state.settings.adaptiveScalesEnabled) {
|
|
852
863
|
return this._getLocationTotalsForViewportExtent(state, props);
|
|
853
864
|
} else {
|
|
854
865
|
return this._getLocationTotalsExtent(state, props);
|
|
855
866
|
}
|
|
856
867
|
};
|
|
857
868
|
|
|
858
|
-
|
|
869
|
+
getFlowsForFlowmapLayer: Selector<L, F, (F | AggregateFlow)[] | undefined> =
|
|
859
870
|
createSelector(
|
|
860
871
|
this.getSortedAggregatedFilteredFlows,
|
|
861
872
|
this.getLocationIdsInViewport,
|
|
862
873
|
this.getSelectedLocationsSet,
|
|
863
874
|
this.getLocationFilterMode,
|
|
875
|
+
this.getMaxTopFlowsDisplayNum,
|
|
864
876
|
(
|
|
865
877
|
flows,
|
|
866
878
|
locationIdsInViewport,
|
|
867
879
|
selectedLocationsSet,
|
|
868
880
|
locationFilterMode,
|
|
881
|
+
maxTopFlowsDisplayNum,
|
|
869
882
|
) => {
|
|
870
883
|
if (!flows || !locationIdsInViewport) return undefined;
|
|
871
884
|
const picked: (F | AggregateFlow)[] = [];
|
|
@@ -892,7 +905,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
892
905
|
}
|
|
893
906
|
}
|
|
894
907
|
// Only keep top
|
|
895
|
-
if (pickedCount >
|
|
908
|
+
if (pickedCount > maxTopFlowsDisplayNum) break;
|
|
896
909
|
}
|
|
897
910
|
// assuming they are sorted in descending order,
|
|
898
911
|
// we need ascending for rendering
|
|
@@ -900,20 +913,51 @@ export default class FlowMapSelectors<L, F> {
|
|
|
900
913
|
},
|
|
901
914
|
);
|
|
902
915
|
|
|
903
|
-
_getFlowMagnitudeExtent =
|
|
904
|
-
|
|
905
|
-
|
|
916
|
+
_getFlowMagnitudeExtent: Selector<L, F, [number, number] | undefined> =
|
|
917
|
+
createSelector(
|
|
918
|
+
this.getSortedAggregatedFilteredFlows,
|
|
919
|
+
this.getSelectedLocationsSet,
|
|
920
|
+
this.getLocationFilterMode,
|
|
921
|
+
(flows, selectedLocationsSet, locationFilterMode) => {
|
|
922
|
+
if (!flows) return undefined;
|
|
923
|
+
let rv: [number, number] | undefined = undefined;
|
|
924
|
+
for (const f of flows) {
|
|
925
|
+
if (
|
|
926
|
+
this.accessors.getFlowOriginId(f) !==
|
|
927
|
+
this.accessors.getFlowDestId(f) &&
|
|
928
|
+
this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode)
|
|
929
|
+
) {
|
|
930
|
+
const count = this.accessors.getFlowMagnitude(f);
|
|
931
|
+
if (rv == null) {
|
|
932
|
+
rv = [count, count];
|
|
933
|
+
} else {
|
|
934
|
+
if (count < rv[0]) rv[0] = count;
|
|
935
|
+
if (count > rv[1]) rv[1] = count;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return rv;
|
|
940
|
+
},
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
_getAdaptiveFlowMagnitudeExtent: Selector<
|
|
944
|
+
L,
|
|
945
|
+
F,
|
|
946
|
+
[number, number] | undefined
|
|
947
|
+
> = createSelector(this.getFlowsForFlowmapLayer, (flows) => {
|
|
948
|
+
if (!flows) return undefined;
|
|
949
|
+
const rv = extent(flows, this.accessors.getFlowMagnitude);
|
|
950
|
+
return rv[0] !== undefined && rv[1] !== undefined ? rv : undefined;
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
getFlowMagnitudeExtent = (
|
|
954
|
+
state: FlowmapState,
|
|
955
|
+
props: FlowmapData<L, F>,
|
|
906
956
|
): [number, number] | undefined => {
|
|
907
|
-
if (state.
|
|
908
|
-
|
|
909
|
-
if (flows) {
|
|
910
|
-
const rv = extent(flows, this.accessors.getFlowMagnitude);
|
|
911
|
-
return rv[0] !== undefined && rv[1] !== undefined ? rv : undefined;
|
|
912
|
-
} else {
|
|
913
|
-
return undefined;
|
|
914
|
-
}
|
|
957
|
+
if (state.settings.adaptiveScalesEnabled) {
|
|
958
|
+
return this._getAdaptiveFlowMagnitudeExtent(state, props);
|
|
915
959
|
} else {
|
|
916
|
-
return this.
|
|
960
|
+
return this._getFlowMagnitudeExtent(state, props);
|
|
917
961
|
}
|
|
918
962
|
};
|
|
919
963
|
|
|
@@ -933,19 +977,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
933
977
|
|
|
934
978
|
getFlowThicknessScale = createSelector(
|
|
935
979
|
this.getFlowMagnitudeExtent,
|
|
936
|
-
|
|
937
|
-
if (!magnitudeExtent) return undefined;
|
|
938
|
-
return scaleLinear()
|
|
939
|
-
.range([0.025, 0.5])
|
|
940
|
-
.domain([
|
|
941
|
-
0,
|
|
942
|
-
// should support diff mode too
|
|
943
|
-
Math.max.apply(
|
|
944
|
-
null,
|
|
945
|
-
magnitudeExtent.map((x: number | undefined) => Math.abs(x || 0)),
|
|
946
|
-
),
|
|
947
|
-
]);
|
|
948
|
-
},
|
|
980
|
+
getFlowThicknessScale,
|
|
949
981
|
);
|
|
950
982
|
|
|
951
983
|
getCircleSizeScale = createSelector(
|
|
@@ -1027,7 +1059,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1027
1059
|
},
|
|
1028
1060
|
);
|
|
1029
1061
|
|
|
1030
|
-
|
|
1062
|
+
getLocationsForFlowmapLayer: Selector<
|
|
1031
1063
|
L,
|
|
1032
1064
|
F,
|
|
1033
1065
|
Array<L | ClusterNode> | undefined
|
|
@@ -1057,11 +1089,11 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1057
1089
|
},
|
|
1058
1090
|
);
|
|
1059
1091
|
|
|
1060
|
-
|
|
1092
|
+
getLocationsForFlowmapLayerById: Selector<
|
|
1061
1093
|
L,
|
|
1062
1094
|
F,
|
|
1063
1095
|
Map<string, L | ClusterNode> | undefined
|
|
1064
|
-
> = createSelector(this.
|
|
1096
|
+
> = createSelector(this.getLocationsForFlowmapLayer, (locations) => {
|
|
1065
1097
|
if (!locations) return undefined;
|
|
1066
1098
|
return locations.reduce(
|
|
1067
1099
|
(m, d) => (m.set(this.accessors.getLocationId(d), d), m),
|
|
@@ -1070,10 +1102,10 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1070
1102
|
});
|
|
1071
1103
|
|
|
1072
1104
|
getLayersData: Selector<L, F, LayersData> = createSelector(
|
|
1073
|
-
this.
|
|
1074
|
-
this.
|
|
1075
|
-
this.
|
|
1076
|
-
this.
|
|
1105
|
+
this.getLocationsForFlowmapLayer,
|
|
1106
|
+
this.getFlowsForFlowmapLayer,
|
|
1107
|
+
this.getFlowmapColorsRGBA,
|
|
1108
|
+
this.getLocationsForFlowmapLayerById,
|
|
1077
1109
|
this.getLocationIdsInViewport,
|
|
1078
1110
|
this.getInCircleSizeGetter,
|
|
1079
1111
|
this.getOutCircleSizeGetter,
|
|
@@ -1082,7 +1114,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1082
1114
|
(
|
|
1083
1115
|
locations,
|
|
1084
1116
|
flows,
|
|
1085
|
-
|
|
1117
|
+
flowmapColors,
|
|
1086
1118
|
locationsById,
|
|
1087
1119
|
locationIdsInViewport,
|
|
1088
1120
|
getInCircleSize,
|
|
@@ -1093,7 +1125,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1093
1125
|
return this._prepareLayersData(
|
|
1094
1126
|
locations,
|
|
1095
1127
|
flows,
|
|
1096
|
-
|
|
1128
|
+
flowmapColors,
|
|
1097
1129
|
locationsById,
|
|
1098
1130
|
locationIdsInViewport,
|
|
1099
1131
|
getInCircleSize,
|
|
@@ -1104,11 +1136,11 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1104
1136
|
},
|
|
1105
1137
|
);
|
|
1106
1138
|
|
|
1107
|
-
prepareLayersData(state:
|
|
1108
|
-
const locations = this.
|
|
1109
|
-
const flows = this.
|
|
1110
|
-
const
|
|
1111
|
-
const locationsById = this.
|
|
1139
|
+
prepareLayersData(state: FlowmapState, props: FlowmapData<L, F>): LayersData {
|
|
1140
|
+
const locations = this.getLocationsForFlowmapLayer(state, props) || [];
|
|
1141
|
+
const flows = this.getFlowsForFlowmapLayer(state, props) || [];
|
|
1142
|
+
const flowmapColors = this.getFlowmapColorsRGBA(state, props);
|
|
1143
|
+
const locationsById = this.getLocationsForFlowmapLayerById(state, props);
|
|
1112
1144
|
const locationIdsInViewport = this.getLocationIdsInViewport(state, props);
|
|
1113
1145
|
const getInCircleSize = this.getInCircleSizeGetter(state, props);
|
|
1114
1146
|
const getOutCircleSize = this.getOutCircleSizeGetter(state, props);
|
|
@@ -1116,20 +1148,20 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1116
1148
|
return this._prepareLayersData(
|
|
1117
1149
|
locations,
|
|
1118
1150
|
flows,
|
|
1119
|
-
|
|
1151
|
+
flowmapColors,
|
|
1120
1152
|
locationsById,
|
|
1121
1153
|
locationIdsInViewport,
|
|
1122
1154
|
getInCircleSize,
|
|
1123
1155
|
getOutCircleSize,
|
|
1124
1156
|
flowThicknessScale,
|
|
1125
|
-
state.
|
|
1157
|
+
state.settings.animationEnabled,
|
|
1126
1158
|
);
|
|
1127
1159
|
}
|
|
1128
1160
|
|
|
1129
1161
|
_prepareLayersData(
|
|
1130
1162
|
locations: (L | ClusterNode)[] | undefined,
|
|
1131
1163
|
flows: (F | AggregateFlow)[] | undefined,
|
|
1132
|
-
|
|
1164
|
+
flowmapColors: DiffColorsRGBA | ColorsRGBA,
|
|
1133
1165
|
locationsById: Map<string, L | ClusterNode> | undefined,
|
|
1134
1166
|
locationIdsInViewport: Set<string> | undefined,
|
|
1135
1167
|
getInCircleSize: (locationId: string) => number,
|
|
@@ -1144,80 +1176,113 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1144
1176
|
getFlowDestId,
|
|
1145
1177
|
getFlowMagnitude,
|
|
1146
1178
|
getLocationId,
|
|
1147
|
-
|
|
1179
|
+
getLocationLon,
|
|
1180
|
+
getLocationLat,
|
|
1148
1181
|
} = this.accessors;
|
|
1149
1182
|
|
|
1150
|
-
const getCentroid = (id: string) => {
|
|
1151
|
-
const loc = locationsById?.get(id);
|
|
1152
|
-
return loc ? getLocationCentroid(loc) : [0, 0];
|
|
1153
|
-
};
|
|
1154
|
-
|
|
1155
1183
|
const flowMagnitudeExtent = extent(flows, (f) => getFlowMagnitude(f)) as [
|
|
1156
1184
|
number,
|
|
1157
1185
|
number,
|
|
1158
1186
|
];
|
|
1159
1187
|
const flowColorScale = getFlowColorScale(
|
|
1160
|
-
|
|
1188
|
+
flowmapColors,
|
|
1161
1189
|
flowMagnitudeExtent,
|
|
1162
1190
|
false,
|
|
1163
1191
|
);
|
|
1164
1192
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1193
|
+
// Using a generator here helps to avoid creating intermediary arrays
|
|
1194
|
+
const circlePositions = Float32Array.from(
|
|
1195
|
+
(function* () {
|
|
1196
|
+
for (const location of locations) {
|
|
1197
|
+
yield getLocationLon(location);
|
|
1198
|
+
yield getLocationLat(location);
|
|
1199
|
+
}
|
|
1200
|
+
})(),
|
|
1167
1201
|
);
|
|
1168
1202
|
|
|
1169
1203
|
// TODO: diff mode
|
|
1170
|
-
const circleColor = isDiffColorsRGBA(
|
|
1171
|
-
?
|
|
1172
|
-
:
|
|
1173
|
-
|
|
1174
|
-
const circleColors =
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
}),
|
|
1204
|
+
const circleColor = isDiffColorsRGBA(flowmapColors)
|
|
1205
|
+
? flowmapColors.positive.locationCircles.inner
|
|
1206
|
+
: flowmapColors.locationCircles.inner;
|
|
1207
|
+
|
|
1208
|
+
const circleColors = Uint8Array.from(
|
|
1209
|
+
(function* () {
|
|
1210
|
+
for (const location of locations) {
|
|
1211
|
+
yield* circleColor;
|
|
1212
|
+
}
|
|
1213
|
+
})(),
|
|
1180
1214
|
);
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1215
|
+
|
|
1216
|
+
const inCircleRadii = Float32Array.from(
|
|
1217
|
+
(function* () {
|
|
1218
|
+
for (const location of locations) {
|
|
1219
|
+
const id = getLocationId(location);
|
|
1220
|
+
yield locationIdsInViewport?.has(id) ? getInCircleSize(id) : 1.0;
|
|
1221
|
+
}
|
|
1222
|
+
})(),
|
|
1223
|
+
);
|
|
1224
|
+
const outCircleRadii = Float32Array.from(
|
|
1225
|
+
(function* () {
|
|
1226
|
+
for (const location of locations) {
|
|
1227
|
+
const id = getLocationId(location);
|
|
1228
|
+
yield locationIdsInViewport?.has(id) ? getOutCircleSize(id) : 1.0;
|
|
1229
|
+
}
|
|
1230
|
+
})(),
|
|
1186
1231
|
);
|
|
1187
1232
|
|
|
1188
|
-
const sourcePositions =
|
|
1189
|
-
|
|
1233
|
+
const sourcePositions = Float32Array.from(
|
|
1234
|
+
(function* () {
|
|
1235
|
+
for (const flow of flows) {
|
|
1236
|
+
const loc = locationsById?.get(getFlowOriginId(flow));
|
|
1237
|
+
yield loc ? getLocationLon(loc) : 0;
|
|
1238
|
+
yield loc ? getLocationLat(loc) : 0;
|
|
1239
|
+
}
|
|
1240
|
+
})(),
|
|
1190
1241
|
);
|
|
1191
|
-
const targetPositions =
|
|
1192
|
-
|
|
1242
|
+
const targetPositions = Float32Array.from(
|
|
1243
|
+
(function* () {
|
|
1244
|
+
for (const flow of flows) {
|
|
1245
|
+
const loc = locationsById?.get(getFlowDestId(flow));
|
|
1246
|
+
yield loc ? getLocationLon(loc) : 0;
|
|
1247
|
+
yield loc ? getLocationLat(loc) : 0;
|
|
1248
|
+
}
|
|
1249
|
+
})(),
|
|
1193
1250
|
);
|
|
1194
|
-
const thicknesses =
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1251
|
+
const thicknesses = Float32Array.from(
|
|
1252
|
+
(function* () {
|
|
1253
|
+
for (const flow of flows) {
|
|
1254
|
+
yield flowThicknessScale
|
|
1255
|
+
? flowThicknessScale(getFlowMagnitude(flow)) || 0
|
|
1256
|
+
: 0;
|
|
1257
|
+
}
|
|
1258
|
+
})(),
|
|
1198
1259
|
);
|
|
1199
|
-
const endpointOffsets =
|
|
1200
|
-
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
Math.max(getInCircleSize(originId), getOutCircleSize(originId))
|
|
1205
|
-
Math.max(getInCircleSize(destId), getOutCircleSize(destId))
|
|
1206
|
-
|
|
1207
|
-
}),
|
|
1260
|
+
const endpointOffsets = Float32Array.from(
|
|
1261
|
+
(function* () {
|
|
1262
|
+
for (const flow of flows) {
|
|
1263
|
+
const originId = getFlowOriginId(flow);
|
|
1264
|
+
const destId = getFlowDestId(flow);
|
|
1265
|
+
yield Math.max(getInCircleSize(originId), getOutCircleSize(originId));
|
|
1266
|
+
yield Math.max(getInCircleSize(destId), getOutCircleSize(destId));
|
|
1267
|
+
}
|
|
1268
|
+
})(),
|
|
1208
1269
|
);
|
|
1209
|
-
const flowLineColors =
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1270
|
+
const flowLineColors = Uint8Array.from(
|
|
1271
|
+
(function* () {
|
|
1272
|
+
for (const flow of flows) {
|
|
1273
|
+
yield* flowColorScale(getFlowMagnitude(flow));
|
|
1274
|
+
}
|
|
1275
|
+
})(),
|
|
1213
1276
|
);
|
|
1214
1277
|
|
|
1215
1278
|
const staggeringValues = animationEnabled
|
|
1216
|
-
?
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1279
|
+
? Float32Array.from(
|
|
1280
|
+
(function* () {
|
|
1281
|
+
for (const f of flows) {
|
|
1282
|
+
// @ts-ignore
|
|
1283
|
+
yield new alea(`${getFlowOriginId(f)}-${getFlowDestId(f)}`)();
|
|
1284
|
+
}
|
|
1285
|
+
})(),
|
|
1221
1286
|
)
|
|
1222
1287
|
: undefined;
|
|
1223
1288
|
|
|
@@ -1275,7 +1340,7 @@ export default class FlowMapSelectors<L, F> {
|
|
|
1275
1340
|
isFlowInSelection(
|
|
1276
1341
|
flow: F | AggregateFlow,
|
|
1277
1342
|
selectedLocationsSet: Set<string> | undefined,
|
|
1278
|
-
locationFilterMode
|
|
1343
|
+
locationFilterMode?: LocationFilterMode,
|
|
1279
1344
|
) {
|
|
1280
1345
|
const origin = this.accessors.getFlowOriginId(flow);
|
|
1281
1346
|
const dest = this.accessors.getFlowDestId(flow);
|
|
@@ -1368,10 +1433,9 @@ function aggregateFlows<F>(
|
|
|
1368
1433
|
flowAccessors: FlowAccessors<F>,
|
|
1369
1434
|
): AggregateFlow[] {
|
|
1370
1435
|
// Sum up flows with same origin, dest
|
|
1371
|
-
const byOriginDest =
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
.rollup((ff: F[]) => {
|
|
1436
|
+
const byOriginDest = rollup(
|
|
1437
|
+
flows,
|
|
1438
|
+
(ff: F[]) => {
|
|
1375
1439
|
const origin = flowAccessors.getFlowOriginId(ff[0]);
|
|
1376
1440
|
const dest = flowAccessors.getFlowDestId(ff[0]);
|
|
1377
1441
|
// const color = ff[0].color;
|
|
@@ -1390,11 +1454,14 @@ function aggregateFlows<F>(
|
|
|
1390
1454
|
};
|
|
1391
1455
|
// if (color) rv.color = color;
|
|
1392
1456
|
return rv;
|
|
1393
|
-
}
|
|
1394
|
-
.
|
|
1457
|
+
},
|
|
1458
|
+
flowAccessors.getFlowOriginId,
|
|
1459
|
+
flowAccessors.getFlowDestId,
|
|
1460
|
+
);
|
|
1461
|
+
|
|
1395
1462
|
const rv: AggregateFlow[] = [];
|
|
1396
|
-
for (const
|
|
1397
|
-
for (const
|
|
1463
|
+
for (const values of byOriginDest.values()) {
|
|
1464
|
+
for (const value of values.values()) {
|
|
1398
1465
|
rv.push(value);
|
|
1399
1466
|
}
|
|
1400
1467
|
}
|
|
@@ -1414,7 +1481,7 @@ export function getOuterCircleRadiusByIndex(
|
|
|
1414
1481
|
return Math.max(getInRadius.value[index], getOutRadius.value[index]);
|
|
1415
1482
|
}
|
|
1416
1483
|
|
|
1417
|
-
export function
|
|
1484
|
+
export function getLocationCoordsByIndex(
|
|
1418
1485
|
circleAttributes: FlowCirclesLayerAttributes,
|
|
1419
1486
|
index: number,
|
|
1420
1487
|
): [number, number] {
|