@flowmap.gl/data 8.0.0-alpha.1 → 8.0.0-alpha.13
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 +188 -0
- package/dist/FlowmapSelectors.d.ts.map +1 -0
- package/dist/FlowmapSelectors.js +863 -0
- package/dist/FlowmapState.d.ts +27 -0
- package/dist/FlowmapState.d.ts.map +1 -0
- package/dist/FlowmapState.js +2 -0
- package/dist/cluster/cluster.d.ts.map +1 -1
- package/dist/cluster/cluster.js +8 -5
- 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 +23 -20
- package/dist/index.d.ts +6 -6
- package/dist/index.js +6 -6
- package/dist/provider/FlowmapDataProvider.d.ts +16 -0
- package/dist/provider/FlowmapDataProvider.d.ts.map +1 -0
- package/dist/provider/FlowmapDataProvider.js +17 -0
- package/dist/provider/LocalFlowmapDataProvider.d.ts +20 -0
- package/dist/provider/LocalFlowmapDataProvider.d.ts.map +1 -0
- package/dist/provider/LocalFlowmapDataProvider.js +98 -0
- package/dist/types.d.ts +6 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/util.d.ts +0 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -4
- package/dist-es5/FlowmapAggregateAccessors.d.ts +16 -0
- package/dist-es5/FlowmapAggregateAccessors.d.ts.map +1 -0
- package/dist-es5/FlowmapAggregateAccessors.js +57 -0
- package/{dist/FlowMapSelectors.d.ts → dist-es5/FlowmapSelectors.d.ts} +55 -49
- package/dist-es5/FlowmapSelectors.d.ts.map +1 -0
- package/dist-es5/FlowmapSelectors.js +1507 -0
- package/{dist/FlowMapState.d.ts → dist-es5/FlowmapState.d.ts} +6 -3
- package/dist-es5/FlowmapState.d.ts.map +1 -0
- package/dist-es5/FlowmapState.js +3 -0
- package/dist-es5/cluster/ClusterIndex.d.ts +42 -0
- package/dist-es5/cluster/ClusterIndex.d.ts.map +1 -0
- package/dist-es5/cluster/ClusterIndex.js +297 -0
- package/dist-es5/cluster/cluster.d.ts +31 -0
- package/dist-es5/cluster/cluster.d.ts.map +1 -0
- package/dist-es5/cluster/cluster.js +266 -0
- package/dist-es5/colors.d.ts +103 -0
- package/dist-es5/colors.d.ts.map +1 -0
- package/dist-es5/colors.js +510 -0
- package/dist-es5/getViewStateForLocations.d.ts +23 -0
- package/dist-es5/getViewStateForLocations.d.ts.map +1 -0
- package/dist-es5/getViewStateForLocations.js +64 -0
- package/dist-es5/index.d.ts +11 -0
- package/dist-es5/index.d.ts.map +1 -0
- package/dist-es5/index.js +28 -0
- package/dist-es5/provider/FlowmapDataProvider.d.ts +16 -0
- package/dist-es5/provider/FlowmapDataProvider.d.ts.map +1 -0
- package/dist-es5/provider/FlowmapDataProvider.js +22 -0
- package/dist-es5/provider/LocalFlowmapDataProvider.d.ts +20 -0
- package/dist-es5/provider/LocalFlowmapDataProvider.d.ts.map +1 -0
- package/dist-es5/provider/LocalFlowmapDataProvider.js +154 -0
- package/dist-es5/time.d.ts +24 -0
- package/dist-es5/time.d.ts.map +1 -0
- package/dist-es5/time.js +168 -0
- package/dist-es5/types.d.ts +118 -0
- package/dist-es5/types.d.ts.map +1 -0
- package/dist-es5/types.js +29 -0
- package/dist-es5/util.d.ts +5 -0
- package/dist-es5/util.d.ts.map +1 -0
- package/dist-es5/util.js +14 -0
- package/package.json +7 -5
- package/src/{FlowMapAggregateAccessors.ts → FlowmapAggregateAccessors.ts} +14 -9
- package/src/{FlowMapSelectors.ts → FlowmapSelectors.ts} +213 -178
- package/src/{FlowMapState.ts → FlowmapState.ts} +5 -2
- package/src/cluster/cluster.ts +7 -4
- package/src/colors.ts +65 -21
- package/src/getViewStateForLocations.ts +41 -35
- package/src/index.ts +6 -6
- package/src/provider/{FlowMapDataProvider.ts → FlowmapDataProvider.ts} +14 -12
- package/src/provider/LocalFlowmapDataProvider.ts +122 -0
- package/src/types.ts +6 -4
- package/src/util.ts +0 -4
- package/tsconfig.es5.json +11 -0
- 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
package/src/cluster/cluster.ts
CHANGED
|
@@ -98,7 +98,7 @@ export function clusterLocations<L>(
|
|
|
98
98
|
getLocationWeight: LocationWeightGetter,
|
|
99
99
|
options?: Partial<Options>,
|
|
100
100
|
): ClusterLevel[] {
|
|
101
|
-
const {
|
|
101
|
+
const {getLocationLon, getLocationLat, getLocationId} = locationAccessors;
|
|
102
102
|
const opts = {
|
|
103
103
|
...defaultOptions,
|
|
104
104
|
...options,
|
|
@@ -110,7 +110,8 @@ export function clusterLocations<L>(
|
|
|
110
110
|
// generate a cluster object for each point and index input points into a KD-tree
|
|
111
111
|
let clusters = new Array<Point>();
|
|
112
112
|
for (let i = 0; i < locations.length; i++) {
|
|
113
|
-
const
|
|
113
|
+
const x = getLocationLon(locations[i]);
|
|
114
|
+
const y = getLocationLat(locations[i]);
|
|
114
115
|
clusters.push({
|
|
115
116
|
x: lngX(x), // projected point coordinates
|
|
116
117
|
y: latY(y),
|
|
@@ -165,7 +166,8 @@ export function clusterLocations<L>(
|
|
|
165
166
|
nodes.push({
|
|
166
167
|
id: getLocationId(location),
|
|
167
168
|
zoom,
|
|
168
|
-
|
|
169
|
+
lat: getLocationLat(location),
|
|
170
|
+
lon: getLocationLon(location),
|
|
169
171
|
});
|
|
170
172
|
} else if (isClusterPoint(point)) {
|
|
171
173
|
const {id} = point;
|
|
@@ -177,7 +179,8 @@ export function clusterLocations<L>(
|
|
|
177
179
|
id: makeClusterId(id),
|
|
178
180
|
name: makeClusterName(id, numPoints),
|
|
179
181
|
zoom,
|
|
180
|
-
|
|
182
|
+
lat: yLat(y),
|
|
183
|
+
lon: xLng(x),
|
|
181
184
|
children,
|
|
182
185
|
} as Cluster);
|
|
183
186
|
}
|
package/src/colors.ts
CHANGED
|
@@ -26,9 +26,9 @@ import {
|
|
|
26
26
|
} from 'd3-scale-chromatic';
|
|
27
27
|
import {range} from 'd3-array';
|
|
28
28
|
import {scalePow, scaleSequential, scaleSequentialPow} from 'd3-scale';
|
|
29
|
-
import {interpolateRgbBasis} from 'd3-interpolate';
|
|
30
|
-
import {color as d3color, hcl} from 'd3-color';
|
|
31
|
-
import {SettingsState} from './
|
|
29
|
+
import {interpolateBasis, interpolateRgbBasis} from 'd3-interpolate';
|
|
30
|
+
import {color as d3color, hcl, rgb as colorRgb} from 'd3-color';
|
|
31
|
+
import {SettingsState} from './FlowmapState';
|
|
32
32
|
|
|
33
33
|
const DEFAULT_OUTLINE_COLOR = '#fff';
|
|
34
34
|
const DEFAULT_DIMMED_OPACITY = 0.4;
|
|
@@ -58,7 +58,10 @@ export function opacifyHex(hexCode: string, opacity: number): string {
|
|
|
58
58
|
return `rgba(${col.r}, ${col.g}, ${col.b}, ${opacity})`;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export function colorAsRgba(color: string): RGBA {
|
|
61
|
+
export function colorAsRgba(color: string | number[]): RGBA {
|
|
62
|
+
if (Array.isArray(color)) {
|
|
63
|
+
return color as RGBA;
|
|
64
|
+
}
|
|
62
65
|
const col = d3color(color);
|
|
63
66
|
if (!col) {
|
|
64
67
|
console.warn('Invalid color: ', color);
|
|
@@ -100,7 +103,7 @@ const getColorSteps = (interpolate: (x: number) => string) =>
|
|
|
100
103
|
.reverse();
|
|
101
104
|
|
|
102
105
|
const FLOW_MIN_COLOR = 'rgba(240,240,240,0.5)';
|
|
103
|
-
export const
|
|
106
|
+
export const GRAYISH = [FLOW_MIN_COLOR, ColorScheme.primary];
|
|
104
107
|
const schemeBluYl = [
|
|
105
108
|
'#f7feae',
|
|
106
109
|
'#b7e6a5',
|
|
@@ -134,7 +137,6 @@ export const schemeTeal = [
|
|
|
134
137
|
export const DEFAULT_COLOR_SCHEME = schemeTeal;
|
|
135
138
|
export const COLOR_SCHEMES: {[key: string]: string[]} = {
|
|
136
139
|
Blues: asScheme(schemeBlues),
|
|
137
|
-
BluesPale: BLUES_PALE,
|
|
138
140
|
BluGrn: [
|
|
139
141
|
'#c4e6c3',
|
|
140
142
|
'#96d2a4',
|
|
@@ -186,6 +188,7 @@ export const COLOR_SCHEMES: {[key: string]: string[]} = {
|
|
|
186
188
|
],
|
|
187
189
|
Emrld: schemeEmrld,
|
|
188
190
|
GnBu: asScheme(schemeGnBu),
|
|
191
|
+
Grayish: GRAYISH,
|
|
189
192
|
Greens: asScheme(schemeGreens),
|
|
190
193
|
Greys: asScheme(schemeGreys),
|
|
191
194
|
Inferno: getColorSteps(interpolateInferno),
|
|
@@ -330,7 +333,7 @@ const diffColors: DiffColors = {
|
|
|
330
333
|
outlineColor: 'rgb(230,233,237)',
|
|
331
334
|
};
|
|
332
335
|
|
|
333
|
-
export function
|
|
336
|
+
export function getFlowmapColors(
|
|
334
337
|
settingsState: SettingsState,
|
|
335
338
|
): Colors | DiffColors {
|
|
336
339
|
return getColors(
|
|
@@ -338,6 +341,7 @@ export function getFlowMapColors(
|
|
|
338
341
|
settingsState.colorScheme,
|
|
339
342
|
settingsState.darkMode,
|
|
340
343
|
settingsState.fadeEnabled,
|
|
344
|
+
settingsState.fadeOpacityEnabled,
|
|
341
345
|
settingsState.fadeAmount,
|
|
342
346
|
settingsState.animationEnabled,
|
|
343
347
|
);
|
|
@@ -345,9 +349,10 @@ export function getFlowMapColors(
|
|
|
345
349
|
|
|
346
350
|
export function getColors(
|
|
347
351
|
diffMode: boolean,
|
|
348
|
-
|
|
352
|
+
colorScheme: string | string[] | undefined,
|
|
349
353
|
darkMode: boolean,
|
|
350
354
|
fadeEnabled: boolean,
|
|
355
|
+
fadeOpacityEnabled: boolean,
|
|
351
356
|
fadeAmount: number,
|
|
352
357
|
animate: boolean,
|
|
353
358
|
): Colors | DiffColors {
|
|
@@ -355,11 +360,18 @@ export function getColors(
|
|
|
355
360
|
return diffColors;
|
|
356
361
|
}
|
|
357
362
|
|
|
358
|
-
let scheme
|
|
363
|
+
let scheme;
|
|
359
364
|
|
|
360
|
-
if (
|
|
361
|
-
scheme =
|
|
365
|
+
if (Array.isArray(colorScheme)) {
|
|
366
|
+
scheme = colorScheme;
|
|
367
|
+
} else {
|
|
368
|
+
scheme =
|
|
369
|
+
(colorScheme && COLOR_SCHEMES[colorScheme]) || DEFAULT_COLOR_SCHEME;
|
|
370
|
+
if (darkMode) {
|
|
371
|
+
scheme = scheme.slice().reverse();
|
|
372
|
+
}
|
|
362
373
|
}
|
|
374
|
+
|
|
363
375
|
// if (animate)
|
|
364
376
|
// if (fadeAmount > 0)
|
|
365
377
|
{
|
|
@@ -390,13 +402,14 @@ export function getColors(
|
|
|
390
402
|
scheme = indices.map(
|
|
391
403
|
(c, i) => {
|
|
392
404
|
const color = colorScale(i);
|
|
393
|
-
const
|
|
394
|
-
if (color == null ||
|
|
405
|
+
const a = amount(i);
|
|
406
|
+
if (color == null || a == null) return '#000';
|
|
395
407
|
const col = hcl(color);
|
|
396
|
-
col.l = darkMode
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
408
|
+
col.l = darkMode ? col.l - col.l * a : col.l + (100 - col.l) * a;
|
|
409
|
+
col.c = col.c - col.c * (a / 4);
|
|
410
|
+
if (fadeOpacityEnabled) {
|
|
411
|
+
col.opacity = col.opacity * (1.0 - a);
|
|
412
|
+
}
|
|
400
413
|
return col.toString();
|
|
401
414
|
},
|
|
402
415
|
// interpolateRgbBasis([colorScale(i), darkMode ? '#000' : '#fff'])(amount(i))
|
|
@@ -417,15 +430,46 @@ export function getColors(
|
|
|
417
430
|
};
|
|
418
431
|
}
|
|
419
432
|
|
|
433
|
+
function interpolateRgbaBasis(colors: string[]) {
|
|
434
|
+
const spline = interpolateBasis;
|
|
435
|
+
const n = colors.length;
|
|
436
|
+
let r: any = new Array(n),
|
|
437
|
+
g: any = new Array(n),
|
|
438
|
+
b: any = new Array(n),
|
|
439
|
+
opacity: any = new Array(n),
|
|
440
|
+
i,
|
|
441
|
+
color: any;
|
|
442
|
+
for (i = 0; i < n; ++i) {
|
|
443
|
+
color = colorRgb(colors[i]);
|
|
444
|
+
r[i] = color.r || 0;
|
|
445
|
+
g[i] = color.g || 0;
|
|
446
|
+
b[i] = color.b || 0;
|
|
447
|
+
opacity[i] = color.opacity || 0;
|
|
448
|
+
}
|
|
449
|
+
r = spline(r);
|
|
450
|
+
g = spline(g);
|
|
451
|
+
b = spline(b);
|
|
452
|
+
opacity = spline(opacity);
|
|
453
|
+
// color.opacity = 1;
|
|
454
|
+
return function (t: number) {
|
|
455
|
+
color.r = r(t);
|
|
456
|
+
color.g = g(t);
|
|
457
|
+
color.b = b(t);
|
|
458
|
+
color.opacity = opacity(t);
|
|
459
|
+
return color + '';
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
420
463
|
export function createFlowColorScale(
|
|
421
464
|
domain: [number, number],
|
|
422
465
|
scheme: string[],
|
|
423
466
|
animate: boolean | undefined,
|
|
424
467
|
): ColorScale {
|
|
425
|
-
const scale = scaleSequentialPow(
|
|
468
|
+
const scale = scaleSequentialPow(interpolateRgbaBasis(scheme))
|
|
426
469
|
// @ts-ignore
|
|
427
470
|
.exponent(animate ? 1 / 2 : 1 / 3)
|
|
428
471
|
.domain(domain);
|
|
472
|
+
|
|
429
473
|
return (value: number) => colorAsRgba(scale(value));
|
|
430
474
|
}
|
|
431
475
|
|
|
@@ -516,7 +560,7 @@ export interface LocationCircleColors {
|
|
|
516
560
|
incoming?: string;
|
|
517
561
|
highlighted?: string;
|
|
518
562
|
empty?: string;
|
|
519
|
-
|
|
563
|
+
outlineEmptyMix?: number;
|
|
520
564
|
}
|
|
521
565
|
|
|
522
566
|
export interface LocationAreaColors {
|
|
@@ -563,7 +607,7 @@ export interface LocationCircleColorsRGBA {
|
|
|
563
607
|
incoming: RGBA;
|
|
564
608
|
highlighted: RGBA;
|
|
565
609
|
empty: RGBA;
|
|
566
|
-
|
|
610
|
+
outlineEmptyMix: number;
|
|
567
611
|
}
|
|
568
612
|
|
|
569
613
|
export interface LocationAreaColorsRGBA {
|
|
@@ -648,7 +692,7 @@ function getFlowAndCircleColors(
|
|
|
648
692
|
flowColorHighlighted,
|
|
649
693
|
),
|
|
650
694
|
empty: emptyColor,
|
|
651
|
-
|
|
695
|
+
outlineEmptyMix: inputColors?.locationCircles?.outlineEmptyMix ?? 0.4,
|
|
652
696
|
},
|
|
653
697
|
};
|
|
654
698
|
}
|
|
@@ -1,39 +1,50 @@
|
|
|
1
|
-
import {BoundingBox, viewport} from '@mapbox/geo-viewport';
|
|
2
1
|
import {geoBounds} from 'd3-geo';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import {fitBounds} from '@math.gl/web-mercator';
|
|
3
|
+
import type {
|
|
4
|
+
FeatureCollection,
|
|
5
|
+
GeometryCollection,
|
|
6
|
+
GeometryObject,
|
|
7
|
+
} from 'geojson';
|
|
8
|
+
import type {ViewState} from './types';
|
|
5
9
|
|
|
6
10
|
export type LocationProperties = any;
|
|
7
11
|
|
|
12
|
+
export type GetViewStateOptions = {
|
|
13
|
+
pad?: number; // size ratio
|
|
14
|
+
padding?: {top: number; bottom: number; left: number; right: number};
|
|
15
|
+
tileSize?: number;
|
|
16
|
+
// minZoom?: number; // not supported by fitBounds
|
|
17
|
+
maxZoom?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
8
20
|
export function getViewStateForFeatures(
|
|
9
21
|
featureCollection:
|
|
10
22
|
| FeatureCollection<GeometryObject, LocationProperties>
|
|
11
23
|
| GeometryCollection,
|
|
12
24
|
size: [number, number],
|
|
13
|
-
opts?:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
x2 + pad * (x2 - x1),
|
|
26
|
-
y2 + pad * (y2 - y1),
|
|
27
|
-
];
|
|
28
|
-
const {
|
|
29
|
-
center: [longitude, latitude],
|
|
30
|
-
zoom,
|
|
31
|
-
} = viewport(bounds, size, undefined, undefined, tileSize, true);
|
|
32
|
-
|
|
25
|
+
opts?: GetViewStateOptions,
|
|
26
|
+
): ViewState & {width: number; height: number} {
|
|
27
|
+
const {pad = 0.05, maxZoom = 100} = opts || {};
|
|
28
|
+
const bounds = geoBounds(featureCollection as any);
|
|
29
|
+
const [[x1, y1], [x2, y2]] = bounds;
|
|
30
|
+
const paddedBounds: [[number, number], [number, number]] = pad
|
|
31
|
+
? [
|
|
32
|
+
[x1 - pad * (x2 - x1), y1 - pad * (y2 - y1)],
|
|
33
|
+
[x2 + pad * (x2 - x1), y2 + pad * (y2 - y1)],
|
|
34
|
+
]
|
|
35
|
+
: bounds;
|
|
36
|
+
const [width, height] = size;
|
|
33
37
|
return {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
...fitBounds({
|
|
39
|
+
width,
|
|
40
|
+
height,
|
|
41
|
+
bounds: paddedBounds,
|
|
42
|
+
padding: opts?.padding,
|
|
43
|
+
// minZoom,
|
|
44
|
+
maxZoom,
|
|
45
|
+
}),
|
|
46
|
+
width,
|
|
47
|
+
height,
|
|
37
48
|
bearing: 0,
|
|
38
49
|
pitch: 0,
|
|
39
50
|
};
|
|
@@ -41,21 +52,16 @@ export function getViewStateForFeatures(
|
|
|
41
52
|
|
|
42
53
|
export function getViewStateForLocations(
|
|
43
54
|
locations: any[],
|
|
44
|
-
|
|
55
|
+
getLocationCoords: (location: any) => [number, number],
|
|
45
56
|
size: [number, number],
|
|
46
|
-
opts?:
|
|
47
|
-
|
|
48
|
-
tileSize?: number;
|
|
49
|
-
minZoom?: number;
|
|
50
|
-
maxZoom?: number;
|
|
51
|
-
},
|
|
52
|
-
): ViewState {
|
|
57
|
+
opts?: GetViewStateOptions,
|
|
58
|
+
): ViewState & {width: number; height: number} {
|
|
53
59
|
return getViewStateForFeatures(
|
|
54
60
|
{
|
|
55
61
|
type: 'GeometryCollection',
|
|
56
62
|
geometries: locations.map((location) => ({
|
|
57
63
|
type: 'Point',
|
|
58
|
-
coordinates:
|
|
64
|
+
coordinates: getLocationCoords(location),
|
|
59
65
|
})),
|
|
60
66
|
} as any,
|
|
61
67
|
size,
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export * from './colors';
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
3
|
+
export * from './FlowmapState';
|
|
4
|
+
export * from './FlowmapSelectors';
|
|
5
5
|
export * from './time';
|
|
6
6
|
export * from './getViewStateForLocations';
|
|
7
|
-
export * from './provider/
|
|
8
|
-
export {default as
|
|
9
|
-
export type {default as
|
|
10
|
-
export {default as
|
|
7
|
+
export * from './provider/FlowmapDataProvider';
|
|
8
|
+
export {default as FlowmapAggregateAccessors} from './FlowmapAggregateAccessors';
|
|
9
|
+
export type {default as FlowmapDataProvider} from './provider/FlowmapDataProvider';
|
|
10
|
+
export {default as LocalFlowmapDataProvider} from './provider/LocalFlowmapDataProvider';
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import {AggregateFlow, Cluster, LocationAccessors, LocationTotals} from '..';
|
|
2
|
-
import {
|
|
2
|
+
import {FlowmapState} from '../FlowmapState';
|
|
3
3
|
import {
|
|
4
4
|
ClusterNode,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
FlowmapData,
|
|
6
|
+
FlowmapDataAccessors,
|
|
7
7
|
LayersData,
|
|
8
8
|
ViewportProps,
|
|
9
9
|
} from '../types';
|
|
10
10
|
|
|
11
|
-
export default interface
|
|
12
|
-
setAccessors(accessors:
|
|
11
|
+
export default interface FlowmapDataProvider<L, F> {
|
|
12
|
+
setAccessors(accessors: FlowmapDataAccessors<L, F>): void;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
setFlowmapState(flowmapState: FlowmapState): Promise<void>;
|
|
15
15
|
|
|
16
16
|
// clearData(): void;
|
|
17
17
|
|
|
18
|
-
getViewportForLocations(
|
|
18
|
+
getViewportForLocations(
|
|
19
|
+
dims: [number, number],
|
|
20
|
+
): Promise<ViewportProps | undefined>;
|
|
19
21
|
|
|
20
22
|
// getFlowTotals(): Promise<FlowTotals>;
|
|
21
23
|
|
|
@@ -36,9 +38,9 @@ export default interface FlowMapDataProvider<L, F> {
|
|
|
36
38
|
getLayersData(): Promise<LayersData | undefined>;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
export function
|
|
41
|
+
export function isFlowmapData<L, F>(
|
|
40
42
|
data: Record<string, any>,
|
|
41
|
-
): data is
|
|
43
|
+
): data is FlowmapData<L, F> {
|
|
42
44
|
return (
|
|
43
45
|
data &&
|
|
44
46
|
data.locations &&
|
|
@@ -48,12 +50,12 @@ export function isFlowMapData<L, F>(
|
|
|
48
50
|
);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
export function
|
|
53
|
+
export function isFlowmapDataProvider<L, F>(
|
|
52
54
|
dataProvider: Record<string, any>,
|
|
53
|
-
): dataProvider is
|
|
55
|
+
): dataProvider is FlowmapDataProvider<L, F> {
|
|
54
56
|
return (
|
|
55
57
|
dataProvider &&
|
|
56
|
-
typeof dataProvider.
|
|
58
|
+
typeof dataProvider.setFlowmapState === 'function' &&
|
|
57
59
|
typeof dataProvider.getViewportForLocations === 'function' &&
|
|
58
60
|
typeof dataProvider.getFlowByIndex === 'function' &&
|
|
59
61
|
typeof dataProvider.getLocationById === 'function' &&
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type FlowmapDataProvider from './FlowmapDataProvider';
|
|
2
|
+
import type {
|
|
3
|
+
Cluster,
|
|
4
|
+
ClusterNode,
|
|
5
|
+
FlowmapData,
|
|
6
|
+
FlowmapDataAccessors,
|
|
7
|
+
LayersData,
|
|
8
|
+
LocationTotals,
|
|
9
|
+
ViewportProps,
|
|
10
|
+
AggregateFlow,
|
|
11
|
+
} from '../types';
|
|
12
|
+
import {FlowmapState} from '../FlowmapState';
|
|
13
|
+
import FlowmapSelectors from '../FlowmapSelectors';
|
|
14
|
+
import {
|
|
15
|
+
GetViewStateOptions,
|
|
16
|
+
getViewStateForLocations,
|
|
17
|
+
} from '../getViewStateForLocations';
|
|
18
|
+
|
|
19
|
+
export default class LocalFlowmapDataProvider<L, F>
|
|
20
|
+
implements FlowmapDataProvider<L, F>
|
|
21
|
+
{
|
|
22
|
+
private selectors: FlowmapSelectors<L, F>;
|
|
23
|
+
private flowmapData: FlowmapData<L, F> | undefined;
|
|
24
|
+
private flowmapState: FlowmapState | undefined;
|
|
25
|
+
|
|
26
|
+
constructor(accessors: FlowmapDataAccessors<L, F>) {
|
|
27
|
+
// scope selectors to the concrete instance of FlowmapDataProvider
|
|
28
|
+
this.selectors = new FlowmapSelectors<L, F>(accessors);
|
|
29
|
+
this.flowmapData = undefined;
|
|
30
|
+
this.flowmapState = undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setAccessors(accessors: FlowmapDataAccessors<L, F>) {
|
|
34
|
+
this.selectors.setAccessors(accessors);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async setFlowmapData(flowmapData: FlowmapData<L, F>): Promise<void> {
|
|
38
|
+
this.flowmapData = flowmapData;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async setFlowmapState(flowmapState: FlowmapState): Promise<void> {
|
|
42
|
+
this.flowmapState = flowmapState;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getFlowByIndex(idx: number): Promise<F | AggregateFlow | undefined> {
|
|
46
|
+
if (!this.flowmapState || !this.flowmapData) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const flows = this.selectors.getFlowsForFlowmapLayer(
|
|
50
|
+
this.flowmapState,
|
|
51
|
+
this.flowmapData,
|
|
52
|
+
);
|
|
53
|
+
return flows?.[idx];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getLocationByIndex(idx: number): Promise<L | ClusterNode | undefined> {
|
|
57
|
+
if (!this.flowmapState || !this.flowmapData) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const locations = this.selectors.getLocationsForFlowmapLayer(
|
|
61
|
+
this.flowmapState,
|
|
62
|
+
this.flowmapData,
|
|
63
|
+
);
|
|
64
|
+
return locations?.[idx];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async getLayersData(): Promise<LayersData | undefined> {
|
|
68
|
+
if (!this.flowmapState || !this.flowmapData) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return this.selectors.getLayersData(this.flowmapState, this.flowmapData);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getLocationById(id: string): Promise<L | Cluster | undefined> {
|
|
75
|
+
if (!this.flowmapState || !this.flowmapData) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
const clusterIndex = this.selectors.getClusterIndex(
|
|
79
|
+
this.flowmapState,
|
|
80
|
+
this.flowmapData,
|
|
81
|
+
);
|
|
82
|
+
if (clusterIndex) {
|
|
83
|
+
const cluster = clusterIndex.getClusterById(id);
|
|
84
|
+
if (cluster) {
|
|
85
|
+
return cluster;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const locationsById = this.selectors.getLocationsById(
|
|
89
|
+
this.flowmapState,
|
|
90
|
+
this.flowmapData,
|
|
91
|
+
);
|
|
92
|
+
return locationsById?.get(id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async getTotalsForLocation(id: string): Promise<LocationTotals | undefined> {
|
|
96
|
+
if (!this.flowmapState || !this.flowmapData) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return this.selectors
|
|
100
|
+
.getLocationTotals(this.flowmapState, this.flowmapData)
|
|
101
|
+
?.get(id);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async getViewportForLocations(
|
|
105
|
+
dims: [number, number],
|
|
106
|
+
opts?: GetViewStateOptions,
|
|
107
|
+
): Promise<ViewportProps | undefined> {
|
|
108
|
+
if (!this.flowmapData?.locations) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
// @ts-ignore
|
|
112
|
+
return getViewStateForLocations(
|
|
113
|
+
this.flowmapData.locations,
|
|
114
|
+
(loc) => [
|
|
115
|
+
this.selectors.accessors.getLocationLon(loc),
|
|
116
|
+
this.selectors.accessors.getLocationLat(loc),
|
|
117
|
+
],
|
|
118
|
+
dims,
|
|
119
|
+
opts,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type FlowmapData<L, F> = {
|
|
2
2
|
locations: L[] | undefined;
|
|
3
3
|
flows: F[] | undefined;
|
|
4
4
|
};
|
|
@@ -26,14 +26,15 @@ export interface FlowAccessors<F> {
|
|
|
26
26
|
export interface LocationAccessors<L> {
|
|
27
27
|
getLocationId: LocationAccessor<L, string>;
|
|
28
28
|
getLocationName?: LocationAccessor<L, string>;
|
|
29
|
-
|
|
29
|
+
getLocationLat: LocationAccessor<L, number>;
|
|
30
|
+
getLocationLon: LocationAccessor<L, number>;
|
|
30
31
|
getLocationClusterName?: (locationIds: string[]) => string;
|
|
31
32
|
// getLocationTotalIn?: LocationAccessor<number>;
|
|
32
33
|
// getLocationTotalOut?: LocationAccessor<number>;
|
|
33
34
|
// getLocationTotalInternal?: LocationAccessor<number>;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
export type
|
|
37
|
+
export type FlowmapDataAccessors<L, F> = LocationAccessors<L> &
|
|
37
38
|
FlowAccessors<F>;
|
|
38
39
|
|
|
39
40
|
export interface LocationTotals {
|
|
@@ -75,7 +76,8 @@ export interface ViewportProps {
|
|
|
75
76
|
export interface ClusterNode {
|
|
76
77
|
id: string;
|
|
77
78
|
zoom: number;
|
|
78
|
-
|
|
79
|
+
lat: number;
|
|
80
|
+
lon: number;
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
export interface ClusterLevel {
|
package/src/util.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import {createSelectorCreator, defaultMemoize} from 'reselect';
|
|
2
2
|
|
|
3
|
-
export function flatMap<S, T>(xs: S[], f: (item: S) => T | T[]): T[] {
|
|
4
|
-
return xs.reduce((acc: T[], x: S) => acc.concat(f(x)), []);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
3
|
export const createDebugSelector = createSelectorCreator(defaultMemoize, {
|
|
8
4
|
equalityCheck: (previousVal: any, currentVal: any) => {
|
|
9
5
|
const rv = currentVal === previousVal;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { AggregateFlow, ClusterNode, FlowMapDataAccessors } from './types';
|
|
2
|
-
export default class FlowMapAggregateAccessors<L, F> {
|
|
3
|
-
private accessors;
|
|
4
|
-
constructor(accessors: FlowMapDataAccessors<L, F>);
|
|
5
|
-
setAccessors(accessors: FlowMapDataAccessors<L, F>): void;
|
|
6
|
-
getFlowMapDataAccessors(): FlowMapDataAccessors<L, F>;
|
|
7
|
-
getLocationId: (location: L | ClusterNode) => string;
|
|
8
|
-
getLocationName: (location: L | ClusterNode) => string;
|
|
9
|
-
getLocationCentroid: (location: L | ClusterNode) => [number, number];
|
|
10
|
-
getFlowOriginId: (f: F | AggregateFlow) => string;
|
|
11
|
-
getFlowDestId: (f: F | AggregateFlow) => string;
|
|
12
|
-
getFlowMagnitude: (f: F | AggregateFlow) => number;
|
|
13
|
-
getFlowTime: (f: F) => Date | undefined;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=FlowMapAggregateAccessors.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FlowMapAggregateAccessors.d.ts","sourceRoot":"","sources":["../src/FlowMapAggregateAccessors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,WAAW,EACX,oBAAoB,EAIrB,MAAM,SAAS,CAAC;AAEjB,MAAM,CAAC,OAAO,OAAO,yBAAyB,CAAC,CAAC,EAAE,CAAC;IACjD,OAAO,CAAC,SAAS,CAA6B;gBAClC,SAAS,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC;IAIjD,YAAY,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC;IAIlD,uBAAuB;IAIvB,aAAa,aAAc,CAAC,GAAG,WAAW,KAAG,MAAM,CAGN;IAE7C,eAAe,aAAc,CAAC,GAAG,WAAW,KAAG,MAAM,CAGJ;IAMjD,mBAAmB,aAAc,CAAC,GAAG,WAAW,KAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAGhB;IAEnD,eAAe,MAAO,CAAC,GAAG,aAAa,YAErC;IAEF,aAAa,MAAO,CAAC,GAAG,aAAa,YAEnC;IAEF,gBAAgB,MAAO,CAAC,GAAG,aAAa,YAEtC;IAGF,WAAW,MAAO,CAAC,sBAGjB;CACH"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { isAggregateFlow, isCluster, isLocationClusterNode, } from './types';
|
|
2
|
-
export default class FlowMapAggregateAccessors {
|
|
3
|
-
constructor(accessors) {
|
|
4
|
-
this.getLocationId = (location) => isLocationClusterNode(location)
|
|
5
|
-
? location.id
|
|
6
|
-
: this.accessors.getLocationId(location);
|
|
7
|
-
this.getLocationName = (location) => {
|
|
8
|
-
var _a;
|
|
9
|
-
return (_a = (isLocationClusterNode(location) && isCluster(location)
|
|
10
|
-
? location.name
|
|
11
|
-
: undefined)) !== null && _a !== void 0 ? _a : this.getLocationId(location);
|
|
12
|
-
};
|
|
13
|
-
// ? location.name // TODO getLocationName for locations and clusters
|
|
14
|
-
// : this.accessors.getLocationName
|
|
15
|
-
// ? this.accessors.getLocationName(location)
|
|
16
|
-
// : this.getLocationId(location);
|
|
17
|
-
this.getLocationCentroid = (location) => isLocationClusterNode(location)
|
|
18
|
-
? location.centroid
|
|
19
|
-
: this.accessors.getLocationCentroid(location);
|
|
20
|
-
this.getFlowOriginId = (f) => {
|
|
21
|
-
return isAggregateFlow(f) ? f.origin : this.accessors.getFlowOriginId(f);
|
|
22
|
-
};
|
|
23
|
-
this.getFlowDestId = (f) => {
|
|
24
|
-
return isAggregateFlow(f) ? f.dest : this.accessors.getFlowDestId(f);
|
|
25
|
-
};
|
|
26
|
-
this.getFlowMagnitude = (f) => {
|
|
27
|
-
return isAggregateFlow(f) ? f.count : this.accessors.getFlowMagnitude(f);
|
|
28
|
-
};
|
|
29
|
-
// Note: Aggregate flows have no time
|
|
30
|
-
this.getFlowTime = (f) => {
|
|
31
|
-
const { getFlowTime } = this.accessors;
|
|
32
|
-
return getFlowTime ? getFlowTime(f) : undefined;
|
|
33
|
-
};
|
|
34
|
-
this.accessors = accessors;
|
|
35
|
-
}
|
|
36
|
-
setAccessors(accessors) {
|
|
37
|
-
this.accessors = accessors;
|
|
38
|
-
}
|
|
39
|
-
getFlowMapDataAccessors() {
|
|
40
|
-
return this.accessors;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmxvd01hcEFnZ3JlZ2F0ZUFjY2Vzc29ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9GbG93TWFwQWdncmVnYXRlQWNjZXNzb3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFJTCxlQUFlLEVBQ2YsU0FBUyxFQUNULHFCQUFxQixHQUN0QixNQUFNLFNBQVMsQ0FBQztBQUVqQixNQUFNLENBQUMsT0FBTyxPQUFPLHlCQUF5QjtJQUU1QyxZQUFZLFNBQXFDO1FBWWpELGtCQUFhLEdBQUcsQ0FBQyxRQUF5QixFQUFVLEVBQUUsQ0FDcEQscUJBQXFCLENBQUMsUUFBUSxDQUFDO1lBQzdCLENBQUMsQ0FBQyxRQUFRLENBQUMsRUFBRTtZQUNiLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUU3QyxvQkFBZSxHQUFHLENBQUMsUUFBeUIsRUFBVSxFQUFFOztZQUN0RCxPQUFBLE1BQUEsQ0FBQyxxQkFBcUIsQ0FBQyxRQUFRLENBQUMsSUFBSSxTQUFTLENBQUMsUUFBUSxDQUFDO2dCQUNyRCxDQUFDLENBQUMsUUFBUSxDQUFDLElBQUk7Z0JBQ2YsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxtQ0FBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1NBQUEsQ0FBQztRQUNqRCxxRUFBcUU7UUFDckUsbUNBQW1DO1FBQ25DLDZDQUE2QztRQUM3QyxrQ0FBa0M7UUFFbEMsd0JBQW1CLEdBQUcsQ0FBQyxRQUF5QixFQUFvQixFQUFFLENBQ3BFLHFCQUFxQixDQUFDLFFBQVEsQ0FBQztZQUM3QixDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVE7WUFDbkIsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsbUJBQW1CLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFbkQsb0JBQWUsR0FBRyxDQUFDLENBQW9CLEVBQUUsRUFBRTtZQUN6QyxPQUFPLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDM0UsQ0FBQyxDQUFDO1FBRUYsa0JBQWEsR0FBRyxDQUFDLENBQW9CLEVBQUUsRUFBRTtZQUN2QyxPQUFPLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkUsQ0FBQyxDQUFDO1FBRUYscUJBQWdCLEdBQUcsQ0FBQyxDQUFvQixFQUFFLEVBQUU7WUFDMUMsT0FBTyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDM0UsQ0FBQyxDQUFDO1FBRUYscUNBQXFDO1FBQ3JDLGdCQUFXLEdBQUcsQ0FBQyxDQUFJLEVBQUUsRUFBRTtZQUNyQixNQUFNLEVBQUMsV0FBVyxFQUFDLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztZQUNyQyxPQUFPLFdBQVcsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7UUFDbEQsQ0FBQyxDQUFDO1FBOUNBLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDO0lBQzdCLENBQUM7SUFFRCxZQUFZLENBQUMsU0FBcUM7UUFDaEQsSUFBSSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7SUFDN0IsQ0FBQztJQUVELHVCQUF1QjtRQUNyQixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDeEIsQ0FBQztDQXNDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFnZ3JlZ2F0ZUZsb3csXG4gIENsdXN0ZXJOb2RlLFxuICBGbG93TWFwRGF0YUFjY2Vzc29ycyxcbiAgaXNBZ2dyZWdhdGVGbG93LFxuICBpc0NsdXN0ZXIsXG4gIGlzTG9jYXRpb25DbHVzdGVyTm9kZSxcbn0gZnJvbSAnLi90eXBlcyc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEZsb3dNYXBBZ2dyZWdhdGVBY2Nlc3NvcnM8TCwgRj4ge1xuICBwcml2YXRlIGFjY2Vzc29yczogRmxvd01hcERhdGFBY2Nlc3NvcnM8TCwgRj47XG4gIGNvbnN0cnVjdG9yKGFjY2Vzc29yczogRmxvd01hcERhdGFBY2Nlc3NvcnM8TCwgRj4pIHtcbiAgICB0aGlzLmFjY2Vzc29ycyA9IGFjY2Vzc29ycztcbiAgfVxuXG4gIHNldEFjY2Vzc29ycyhhY2Nlc3NvcnM6IEZsb3dNYXBEYXRhQWNjZXNzb3JzPEwsIEY+KSB7XG4gICAgdGhpcy5hY2Nlc3NvcnMgPSBhY2Nlc3NvcnM7XG4gIH1cblxuICBnZXRGbG93TWFwRGF0YUFjY2Vzc29ycygpIHtcbiAgICByZXR1cm4gdGhpcy5hY2Nlc3NvcnM7XG4gIH1cblxuICBnZXRMb2NhdGlvbklkID0gKGxvY2F0aW9uOiBMIHwgQ2x1c3Rlck5vZGUpOiBzdHJpbmcgPT5cbiAgICBpc0xvY2F0aW9uQ2x1c3Rlck5vZGUobG9jYXRpb24pXG4gICAgICA/IGxvY2F0aW9uLmlkXG4gICAgICA6IHRoaXMuYWNjZXNzb3JzLmdldExvY2F0aW9uSWQobG9jYXRpb24pO1xuXG4gIGdldExvY2F0aW9uTmFtZSA9IChsb2NhdGlvbjogTCB8IENsdXN0ZXJOb2RlKTogc3RyaW5nID0+XG4gICAgKGlzTG9jYXRpb25DbHVzdGVyTm9kZShsb2NhdGlvbikgJiYgaXNDbHVzdGVyKGxvY2F0aW9uKVxuICAgICAgPyBsb2NhdGlvbi5uYW1lXG4gICAgICA6IHVuZGVmaW5lZCkgPz8gdGhpcy5nZXRMb2NhdGlvbklkKGxvY2F0aW9uKTtcbiAgLy8gPyBsb2NhdGlvbi5uYW1lIC8vIFRPRE8gZ2V0TG9jYXRpb25OYW1lIGZvciBsb2NhdGlvbnMgYW5kIGNsdXN0ZXJzXG4gIC8vIDogdGhpcy5hY2Nlc3NvcnMuZ2V0TG9jYXRpb25OYW1lXG4gIC8vID8gdGhpcy5hY2Nlc3NvcnMuZ2V0TG9jYXRpb25OYW1lKGxvY2F0aW9uKVxuICAvLyA6IHRoaXMuZ2V0TG9jYXRpb25JZChsb2NhdGlvbik7XG5cbiAgZ2V0TG9jYXRpb25DZW50cm9pZCA9IChsb2NhdGlvbjogTCB8IENsdXN0ZXJOb2RlKTogW251bWJlciwgbnVtYmVyXSA9PlxuICAgIGlzTG9jYXRpb25DbHVzdGVyTm9kZShsb2NhdGlvbilcbiAgICAgID8gbG9jYXRpb24uY2VudHJvaWRcbiAgICAgIDogdGhpcy5hY2Nlc3NvcnMuZ2V0TG9jYXRpb25DZW50cm9pZChsb2NhdGlvbik7XG5cbiAgZ2V0Rmxvd09yaWdpbklkID0gKGY6IEYgfCBBZ2dyZWdhdGVGbG93KSA9PiB7XG4gICAgcmV0dXJuIGlzQWdncmVnYXRlRmxvdyhmKSA/IGYub3JpZ2luIDogdGhpcy5hY2Nlc3NvcnMuZ2V0Rmxvd09yaWdpbklkKGYpO1xuICB9O1xuXG4gIGdldEZsb3dEZXN0SWQgPSAoZjogRiB8IEFnZ3JlZ2F0ZUZsb3cpID0+IHtcbiAgICByZXR1cm4gaXNBZ2dyZWdhdGVGbG93KGYpID8gZi5kZXN0IDogdGhpcy5hY2Nlc3NvcnMuZ2V0Rmxvd0Rlc3RJZChmKTtcbiAgfTtcblxuICBnZXRGbG93TWFnbml0dWRlID0gKGY6IEYgfCBBZ2dyZWdhdGVGbG93KSA9PiB7XG4gICAgcmV0dXJuIGlzQWdncmVnYXRlRmxvdyhmKSA/IGYuY291bnQgOiB0aGlzLmFjY2Vzc29ycy5nZXRGbG93TWFnbml0dWRlKGYpO1xuICB9O1xuXG4gIC8vIE5vdGU6IEFnZ3JlZ2F0ZSBmbG93cyBoYXZlIG5vIHRpbWVcbiAgZ2V0Rmxvd1RpbWUgPSAoZjogRikgPT4ge1xuICAgIGNvbnN0IHtnZXRGbG93VGltZX0gPSB0aGlzLmFjY2Vzc29ycztcbiAgICByZXR1cm4gZ2V0Rmxvd1RpbWUgPyBnZXRGbG93VGltZShmKSA6IHVuZGVmaW5lZDtcbiAgfTtcbn1cbiJdfQ==
|