@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.
Files changed (97) hide show
  1. package/dist/FlowmapAggregateAccessors.d.ts +16 -0
  2. package/dist/FlowmapAggregateAccessors.d.ts.map +1 -0
  3. package/dist/FlowmapAggregateAccessors.js +46 -0
  4. package/dist/FlowmapSelectors.d.ts +188 -0
  5. package/dist/FlowmapSelectors.d.ts.map +1 -0
  6. package/dist/FlowmapSelectors.js +863 -0
  7. package/dist/FlowmapState.d.ts +27 -0
  8. package/dist/FlowmapState.d.ts.map +1 -0
  9. package/dist/FlowmapState.js +2 -0
  10. package/dist/cluster/cluster.d.ts.map +1 -1
  11. package/dist/cluster/cluster.js +8 -5
  12. package/dist/colors.d.ts +7 -7
  13. package/dist/colors.d.ts.map +1 -1
  14. package/dist/colors.js +55 -20
  15. package/dist/getViewStateForLocations.d.ts +18 -11
  16. package/dist/getViewStateForLocations.d.ts.map +1 -1
  17. package/dist/getViewStateForLocations.js +23 -20
  18. package/dist/index.d.ts +6 -6
  19. package/dist/index.js +6 -6
  20. package/dist/provider/FlowmapDataProvider.d.ts +16 -0
  21. package/dist/provider/FlowmapDataProvider.d.ts.map +1 -0
  22. package/dist/provider/FlowmapDataProvider.js +17 -0
  23. package/dist/provider/LocalFlowmapDataProvider.d.ts +20 -0
  24. package/dist/provider/LocalFlowmapDataProvider.d.ts.map +1 -0
  25. package/dist/provider/LocalFlowmapDataProvider.js +98 -0
  26. package/dist/types.d.ts +6 -4
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/types.js +1 -1
  29. package/dist/util.d.ts +0 -1
  30. package/dist/util.d.ts.map +1 -1
  31. package/dist/util.js +1 -4
  32. package/dist-es5/FlowmapAggregateAccessors.d.ts +16 -0
  33. package/dist-es5/FlowmapAggregateAccessors.d.ts.map +1 -0
  34. package/dist-es5/FlowmapAggregateAccessors.js +57 -0
  35. package/{dist/FlowMapSelectors.d.ts → dist-es5/FlowmapSelectors.d.ts} +55 -49
  36. package/dist-es5/FlowmapSelectors.d.ts.map +1 -0
  37. package/dist-es5/FlowmapSelectors.js +1507 -0
  38. package/{dist/FlowMapState.d.ts → dist-es5/FlowmapState.d.ts} +6 -3
  39. package/dist-es5/FlowmapState.d.ts.map +1 -0
  40. package/dist-es5/FlowmapState.js +3 -0
  41. package/dist-es5/cluster/ClusterIndex.d.ts +42 -0
  42. package/dist-es5/cluster/ClusterIndex.d.ts.map +1 -0
  43. package/dist-es5/cluster/ClusterIndex.js +297 -0
  44. package/dist-es5/cluster/cluster.d.ts +31 -0
  45. package/dist-es5/cluster/cluster.d.ts.map +1 -0
  46. package/dist-es5/cluster/cluster.js +266 -0
  47. package/dist-es5/colors.d.ts +103 -0
  48. package/dist-es5/colors.d.ts.map +1 -0
  49. package/dist-es5/colors.js +510 -0
  50. package/dist-es5/getViewStateForLocations.d.ts +23 -0
  51. package/dist-es5/getViewStateForLocations.d.ts.map +1 -0
  52. package/dist-es5/getViewStateForLocations.js +64 -0
  53. package/dist-es5/index.d.ts +11 -0
  54. package/dist-es5/index.d.ts.map +1 -0
  55. package/dist-es5/index.js +28 -0
  56. package/dist-es5/provider/FlowmapDataProvider.d.ts +16 -0
  57. package/dist-es5/provider/FlowmapDataProvider.d.ts.map +1 -0
  58. package/dist-es5/provider/FlowmapDataProvider.js +22 -0
  59. package/dist-es5/provider/LocalFlowmapDataProvider.d.ts +20 -0
  60. package/dist-es5/provider/LocalFlowmapDataProvider.d.ts.map +1 -0
  61. package/dist-es5/provider/LocalFlowmapDataProvider.js +154 -0
  62. package/dist-es5/time.d.ts +24 -0
  63. package/dist-es5/time.d.ts.map +1 -0
  64. package/dist-es5/time.js +168 -0
  65. package/dist-es5/types.d.ts +118 -0
  66. package/dist-es5/types.d.ts.map +1 -0
  67. package/dist-es5/types.js +29 -0
  68. package/dist-es5/util.d.ts +5 -0
  69. package/dist-es5/util.d.ts.map +1 -0
  70. package/dist-es5/util.js +14 -0
  71. package/package.json +7 -5
  72. package/src/{FlowMapAggregateAccessors.ts → FlowmapAggregateAccessors.ts} +14 -9
  73. package/src/{FlowMapSelectors.ts → FlowmapSelectors.ts} +213 -178
  74. package/src/{FlowMapState.ts → FlowmapState.ts} +5 -2
  75. package/src/cluster/cluster.ts +7 -4
  76. package/src/colors.ts +65 -21
  77. package/src/getViewStateForLocations.ts +41 -35
  78. package/src/index.ts +6 -6
  79. package/src/provider/{FlowMapDataProvider.ts → FlowmapDataProvider.ts} +14 -12
  80. package/src/provider/LocalFlowmapDataProvider.ts +122 -0
  81. package/src/types.ts +6 -4
  82. package/src/util.ts +0 -4
  83. package/tsconfig.es5.json +11 -0
  84. package/dist/FlowMapAggregateAccessors.d.ts +0 -15
  85. package/dist/FlowMapAggregateAccessors.d.ts.map +0 -1
  86. package/dist/FlowMapAggregateAccessors.js +0 -43
  87. package/dist/FlowMapSelectors.d.ts.map +0 -1
  88. package/dist/FlowMapSelectors.js +0 -834
  89. package/dist/FlowMapState.d.ts.map +0 -1
  90. package/dist/FlowMapState.js +0 -2
  91. package/dist/provider/FlowMapDataProvider.d.ts +0 -16
  92. package/dist/provider/FlowMapDataProvider.d.ts.map +0 -1
  93. package/dist/provider/FlowMapDataProvider.js +0 -17
  94. package/dist/provider/LocalFlowMapDataProvider.d.ts +0 -20
  95. package/dist/provider/LocalFlowMapDataProvider.d.ts.map +0 -1
  96. package/dist/provider/LocalFlowMapDataProvider.js +0 -87
  97. package/src/provider/LocalFlowMapDataProvider.ts +0 -105
@@ -98,7 +98,7 @@ export function clusterLocations<L>(
98
98
  getLocationWeight: LocationWeightGetter,
99
99
  options?: Partial<Options>,
100
100
  ): ClusterLevel[] {
101
- const {getLocationCentroid, getLocationId} = locationAccessors;
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 [x, y] = getLocationCentroid(locations[i]);
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
- centroid: getLocationCentroid(location),
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
- centroid: [xLng(x), yLat(y)] as [number, number],
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 './FlowMapState';
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 BLUES_PALE = [FLOW_MIN_COLOR, ColorScheme.primary];
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 getFlowMapColors(
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
- schemeKey: string | undefined,
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 = (schemeKey && COLOR_SCHEMES[schemeKey]) || DEFAULT_COLOR_SCHEME;
363
+ let scheme;
359
364
 
360
- if (darkMode) {
361
- scheme = scheme.slice().reverse();
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 alpha = amount(i);
394
- if (color == null || alpha == null) return '#000';
405
+ const a = amount(i);
406
+ if (color == null || a == null) return '#000';
395
407
  const col = hcl(color);
396
- col.l = darkMode
397
- ? col.l - col.l * alpha
398
- : col.l + (100 - col.l) * alpha;
399
- col.c = col.c - col.c * (alpha / 4);
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(interpolateRgbBasis(scheme))
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
- emptyOutline?: string;
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
- emptyOutline: RGBA;
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
- emptyOutline: mixColorsRGBA(innerColor, emptyColor, 0.4),
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 {FeatureCollection, GeometryCollection, GeometryObject} from 'geojson';
4
- import {ViewState} from './types';
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
- pad?: number;
15
- tileSize?: number;
16
- minZoom?: number;
17
- maxZoom?: number;
18
- },
19
- ): ViewState {
20
- const {pad = 0.05, tileSize = 512, minZoom = 0, maxZoom = 100} = opts || {};
21
- const [[x1, y1], [x2, y2]] = geoBounds(featureCollection as any);
22
- const bounds: BoundingBox = [
23
- x1 - pad * (x2 - x1),
24
- y1 - pad * (y2 - y1),
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
- longitude,
35
- latitude,
36
- zoom: Math.max(Math.min(maxZoom, zoom), minZoom),
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
- getLocationCentroid: (location: any) => [number, number],
55
+ getLocationCoords: (location: any) => [number, number],
45
56
  size: [number, number],
46
- opts?: {
47
- pad?: number;
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: getLocationCentroid(location),
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 './FlowMapState';
4
- export * from './FlowMapSelectors';
3
+ export * from './FlowmapState';
4
+ export * from './FlowmapSelectors';
5
5
  export * from './time';
6
6
  export * from './getViewStateForLocations';
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';
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 {FlowMapState} from '../FlowMapState';
2
+ import {FlowmapState} from '../FlowmapState';
3
3
  import {
4
4
  ClusterNode,
5
- FlowMapData,
6
- FlowMapDataAccessors,
5
+ FlowmapData,
6
+ FlowmapDataAccessors,
7
7
  LayersData,
8
8
  ViewportProps,
9
9
  } from '../types';
10
10
 
11
- export default interface FlowMapDataProvider<L, F> {
12
- setAccessors(accessors: FlowMapDataAccessors<L, F>): void;
11
+ export default interface FlowmapDataProvider<L, F> {
12
+ setAccessors(accessors: FlowmapDataAccessors<L, F>): void;
13
13
 
14
- setFlowMapState(flowMapState: FlowMapState): Promise<void>;
14
+ setFlowmapState(flowmapState: FlowmapState): Promise<void>;
15
15
 
16
16
  // clearData(): void;
17
17
 
18
- getViewportForLocations(dims: [number, number]): Promise<ViewportProps>;
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 isFlowMapData<L, F>(
41
+ export function isFlowmapData<L, F>(
40
42
  data: Record<string, any>,
41
- ): data is FlowMapData<L, F> {
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 isFlowMapDataProvider<L, F>(
53
+ export function isFlowmapDataProvider<L, F>(
52
54
  dataProvider: Record<string, any>,
53
- ): dataProvider is FlowMapDataProvider<L, F> {
55
+ ): dataProvider is FlowmapDataProvider<L, F> {
54
56
  return (
55
57
  dataProvider &&
56
- typeof dataProvider.setFlowMapState === 'function' &&
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 FlowMapData<L, F> = {
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
- getLocationCentroid: LocationAccessor<L, [number, number]>;
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 FlowMapDataAccessors<L, F> = LocationAccessors<L> &
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
- centroid: [number, number];
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;
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "target": "es5",
5
+ "module": "commonjs",
6
+ "outDir": "dist-es5",
7
+ "lib": [
8
+ "es2015", "dom"
9
+ ]
10
+ }
11
+ }
@@ -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==