@flowmap.gl/data 8.0.0-alpha.2 → 8.0.0-alpha.22

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 (70) hide show
  1. package/dist/FlowmapAggregateAccessors.d.ts +16 -0
  2. package/dist/FlowmapAggregateAccessors.d.ts.map +1 -0
  3. package/dist/FlowmapAggregateAccessors.js +48 -0
  4. package/dist/FlowmapSelectors.d.ts +213 -0
  5. package/dist/FlowmapSelectors.d.ts.map +1 -0
  6. package/dist/FlowmapSelectors.js +861 -0
  7. package/dist/{FlowMapState.d.ts → FlowmapState.d.ts} +12 -8
  8. package/dist/FlowmapState.d.ts.map +1 -0
  9. package/dist/FlowmapState.js +2 -0
  10. package/dist/cluster/ClusterIndex.d.ts +3 -3
  11. package/dist/cluster/ClusterIndex.d.ts.map +1 -1
  12. package/dist/cluster/ClusterIndex.js +1 -1
  13. package/dist/cluster/cluster.d.ts +6 -5
  14. package/dist/cluster/cluster.d.ts.map +1 -1
  15. package/dist/cluster/cluster.js +76 -20
  16. package/dist/colors.d.ts +7 -7
  17. package/dist/colors.d.ts.map +1 -1
  18. package/dist/colors.js +55 -20
  19. package/dist/getViewStateForLocations.d.ts +18 -11
  20. package/dist/getViewStateForLocations.d.ts.map +1 -1
  21. package/dist/getViewStateForLocations.js +37 -23
  22. package/dist/index.d.ts +9 -6
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -6
  25. package/dist/provider/FlowmapDataProvider.d.ts +21 -0
  26. package/dist/provider/FlowmapDataProvider.d.ts.map +1 -0
  27. package/dist/provider/FlowmapDataProvider.js +17 -0
  28. package/dist/provider/LocalFlowmapDataProvider.d.ts +25 -0
  29. package/dist/provider/LocalFlowmapDataProvider.d.ts.map +1 -0
  30. package/dist/provider/LocalFlowmapDataProvider.js +111 -0
  31. package/dist/selector-functions.d.ts +10 -0
  32. package/dist/selector-functions.d.ts.map +1 -0
  33. package/dist/selector-functions.js +56 -0
  34. package/dist/types.d.ts +20 -16
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/types.js +4 -4
  37. package/dist/util.d.ts +0 -1
  38. package/dist/util.d.ts.map +1 -1
  39. package/dist/util.js +1 -4
  40. package/package.json +10 -12
  41. package/src/FlowmapAggregateAccessors.ts +67 -0
  42. package/src/{FlowMapSelectors.ts → FlowmapSelectors.ts} +453 -398
  43. package/src/{FlowMapState.ts → FlowmapState.ts} +11 -7
  44. package/src/cluster/ClusterIndex.ts +19 -12
  45. package/src/cluster/cluster.ts +96 -35
  46. package/src/colors.ts +70 -28
  47. package/src/getViewStateForLocations.ts +56 -40
  48. package/src/index.ts +9 -6
  49. package/src/provider/FlowmapDataProvider.ts +75 -0
  50. package/src/provider/LocalFlowmapDataProvider.ts +143 -0
  51. package/src/selector-functions.ts +87 -0
  52. package/src/types.ts +23 -19
  53. package/src/util.ts +0 -4
  54. package/dist/FlowMapAggregateAccessors.d.ts +0 -15
  55. package/dist/FlowMapAggregateAccessors.d.ts.map +0 -1
  56. package/dist/FlowMapAggregateAccessors.js +0 -43
  57. package/dist/FlowMapSelectors.d.ts +0 -182
  58. package/dist/FlowMapSelectors.d.ts.map +0 -1
  59. package/dist/FlowMapSelectors.js +0 -834
  60. package/dist/FlowMapState.d.ts.map +0 -1
  61. package/dist/FlowMapState.js +0 -2
  62. package/dist/provider/FlowMapDataProvider.d.ts +0 -16
  63. package/dist/provider/FlowMapDataProvider.d.ts.map +0 -1
  64. package/dist/provider/FlowMapDataProvider.js +0 -17
  65. package/dist/provider/LocalFlowMapDataProvider.d.ts +0 -20
  66. package/dist/provider/LocalFlowMapDataProvider.d.ts.map +0 -1
  67. package/dist/provider/LocalFlowMapDataProvider.js +0 -87
  68. package/src/FlowMapAggregateAccessors.ts +0 -60
  69. package/src/provider/FlowMapDataProvider.ts +0 -63
  70. package/src/provider/LocalFlowMapDataProvider.ts +0 -105
@@ -1,62 +1,78 @@
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
  };
40
51
  }
41
52
 
42
- export function getViewStateForLocations(
43
- locations: any[],
44
- getLocationCentroid: (location: any) => [number, number],
53
+ export function getViewStateForLocations<L>(
54
+ locations: Iterable<L>,
55
+ getLocationCoords: (location: L) => [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} {
59
+ const asGeometry = (location: L) => ({
60
+ type: 'Point',
61
+ coordinates: getLocationCoords(location),
62
+ });
63
+ let geometries;
64
+ if (Array.isArray(locations)) {
65
+ geometries = locations.map(asGeometry);
66
+ } else {
67
+ geometries = [];
68
+ for (const location of locations) {
69
+ geometries.push(asGeometry(location));
70
+ }
71
+ }
53
72
  return getViewStateForFeatures(
54
73
  {
55
74
  type: 'GeometryCollection',
56
- geometries: locations.map((location) => ({
57
- type: 'Point',
58
- coordinates: getLocationCentroid(location),
59
- })),
75
+ geometries,
60
76
  } as any,
61
77
  size,
62
78
  opts,
package/src/index.ts CHANGED
@@ -1,10 +1,13 @@
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
+ export * from './selector-functions';
5
6
  export * from './time';
6
7
  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';
8
+ export * from './provider/FlowmapDataProvider';
9
+ export * from './cluster/cluster';
10
+ export * from './cluster/ClusterIndex';
11
+ export {default as FlowmapAggregateAccessors} from './FlowmapAggregateAccessors';
12
+ export type {default as FlowmapDataProvider} from './provider/FlowmapDataProvider';
13
+ export {default as LocalFlowmapDataProvider} from './provider/LocalFlowmapDataProvider';
@@ -0,0 +1,75 @@
1
+ import {AggregateFlow, Cluster, LocationAccessors, LocationTotals} from '..';
2
+ import {FlowmapState} from '../FlowmapState';
3
+ import {
4
+ ClusterNode,
5
+ FlowmapData,
6
+ FlowmapDataAccessors,
7
+ LayersData,
8
+ ViewportProps,
9
+ } from '../types';
10
+
11
+ export default interface FlowmapDataProvider<L, F> {
12
+ setAccessors(accessors: FlowmapDataAccessors<L, F>): void;
13
+
14
+ setFlowmapState(flowmapState: FlowmapState): Promise<void>;
15
+
16
+ // clearData(): void;
17
+
18
+ getViewportForLocations(
19
+ dims: [number, number],
20
+ ): Promise<ViewportProps | undefined>;
21
+
22
+ // getFlowTotals(): Promise<FlowTotals>;
23
+
24
+ getFlowByIndex(index: number): Promise<F | AggregateFlow | undefined>;
25
+
26
+ getLocationById(id: string | number): Promise<L | Cluster | undefined>;
27
+
28
+ getLocationByIndex(idx: number): Promise<L | ClusterNode | undefined>;
29
+
30
+ getTotalsForLocation(
31
+ id: string | number,
32
+ ): Promise<LocationTotals | undefined>;
33
+
34
+ // getLocationsInBbox(
35
+ // bbox: [number, number, number, number],
36
+ // ): Promise<Array<FlowLocation | ClusterNode> | undefined>;
37
+
38
+ // getLocationsForSearchBox(): Promise<(FlowLocation | ClusterNode)[] | undefined>;
39
+
40
+ getLayersData(): Promise<LayersData | undefined>;
41
+
42
+ /**
43
+ * This is to give the data provider control over when/how often layersData
44
+ * is updated which leads to the flowmap being redrawn.
45
+ */
46
+ updateLayersData(
47
+ setLayersData: (layersData: LayersData | undefined) => void,
48
+ changeFlags: Record<string, boolean>,
49
+ ): Promise<void>;
50
+ }
51
+
52
+ export function isFlowmapData<L, F>(
53
+ data: Record<string, any>,
54
+ ): data is FlowmapData<L, F> {
55
+ return (
56
+ data && data.locations && data.flows
57
+ // TODO: test that they are iterable
58
+ // Array.isArray(data.locations) &&
59
+ // Array.isArray(data.flows)
60
+ );
61
+ }
62
+
63
+ export function isFlowmapDataProvider<L, F>(
64
+ dataProvider: Record<string, any>,
65
+ ): dataProvider is FlowmapDataProvider<L, F> {
66
+ return (
67
+ dataProvider &&
68
+ typeof dataProvider.setFlowmapState === 'function' &&
69
+ typeof dataProvider.getViewportForLocations === 'function' &&
70
+ typeof dataProvider.getFlowByIndex === 'function' &&
71
+ typeof dataProvider.getLocationById === 'function' &&
72
+ typeof dataProvider.getLocationByIndex === 'function' &&
73
+ typeof dataProvider.getLayersData === 'function'
74
+ );
75
+ }
@@ -0,0 +1,143 @@
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
+ setFlowmapData(flowmapData: FlowmapData<L, F>): void {
38
+ this.flowmapData = flowmapData;
39
+ }
40
+
41
+ getSelectors(): FlowmapSelectors<L, F> {
42
+ return this.selectors;
43
+ }
44
+
45
+ getFlowmapData(): FlowmapData<L, F> | undefined {
46
+ return this.flowmapData;
47
+ }
48
+
49
+ async setFlowmapState(flowmapState: FlowmapState): Promise<void> {
50
+ this.flowmapState = flowmapState;
51
+ }
52
+
53
+ getFlowmapState(): FlowmapState | undefined {
54
+ return this.flowmapState;
55
+ }
56
+
57
+ async getFlowByIndex(idx: number): Promise<F | AggregateFlow | undefined> {
58
+ if (!this.flowmapState || !this.flowmapData) {
59
+ return undefined;
60
+ }
61
+ const flows = this.selectors.getFlowsForFlowmapLayer(
62
+ this.flowmapState,
63
+ this.flowmapData,
64
+ );
65
+ return flows?.[idx];
66
+ }
67
+
68
+ // TODO: this is unreliable, should replace by unqiue ID
69
+ async getLocationByIndex(idx: number): Promise<L | ClusterNode | undefined> {
70
+ if (!this.flowmapState || !this.flowmapData) {
71
+ return undefined;
72
+ }
73
+ const locations = this.selectors.getLocationsForFlowmapLayer(
74
+ this.flowmapState,
75
+ this.flowmapData,
76
+ );
77
+ return locations?.[idx];
78
+ }
79
+
80
+ async getLayersData(): Promise<LayersData | undefined> {
81
+ if (!this.flowmapState || !this.flowmapData) {
82
+ return undefined;
83
+ }
84
+ return this.selectors.getLayersData(this.flowmapState, this.flowmapData);
85
+ }
86
+
87
+ async getLocationById(id: string | number): Promise<L | Cluster | undefined> {
88
+ if (!this.flowmapState || !this.flowmapData) {
89
+ return undefined;
90
+ }
91
+ const clusterIndex = this.selectors.getClusterIndex(
92
+ this.flowmapState,
93
+ this.flowmapData,
94
+ );
95
+ if (clusterIndex) {
96
+ const cluster = clusterIndex.getClusterById(id);
97
+ if (cluster) {
98
+ return cluster;
99
+ }
100
+ }
101
+ const locationsById = this.selectors.getLocationsById(
102
+ this.flowmapState,
103
+ this.flowmapData,
104
+ );
105
+ return locationsById?.get(id);
106
+ }
107
+
108
+ async getTotalsForLocation(
109
+ id: string | number,
110
+ ): Promise<LocationTotals | undefined> {
111
+ if (!this.flowmapState || !this.flowmapData) {
112
+ return undefined;
113
+ }
114
+ return this.selectors
115
+ .getLocationTotals(this.flowmapState, this.flowmapData)
116
+ ?.get(id);
117
+ }
118
+
119
+ async getViewportForLocations(
120
+ dims: [number, number],
121
+ opts?: GetViewStateOptions,
122
+ ): Promise<ViewportProps | undefined> {
123
+ if (!this.flowmapData?.locations) {
124
+ return undefined;
125
+ }
126
+ // @ts-ignore
127
+ return getViewStateForLocations(
128
+ this.flowmapData.locations,
129
+ (loc) => [
130
+ this.selectors.accessors.getLocationLon(loc),
131
+ this.selectors.accessors.getLocationLat(loc),
132
+ ],
133
+ dims,
134
+ opts,
135
+ );
136
+ }
137
+
138
+ async updateLayersData(
139
+ setLayersData: (layersData: LayersData | undefined) => void,
140
+ ) {
141
+ setLayersData(await this.getLayersData());
142
+ }
143
+ }
@@ -0,0 +1,87 @@
1
+ import {WebMercatorViewport} from '@math.gl/web-mercator';
2
+ import {
3
+ ClusterLevel,
4
+ isCluster,
5
+ LocationAccessors,
6
+ ViewportProps,
7
+ } from './types';
8
+ import {scaleLinear} from 'd3-scale';
9
+ import {ClusterIndex, LocationWeightGetter} from './cluster/ClusterIndex';
10
+ import {descending} from 'd3-array';
11
+
12
+ // TODO: use re-reselect
13
+
14
+ export const getViewportBoundingBox = (
15
+ viewport: ViewportProps,
16
+ maxLocationCircleSize = 0,
17
+ ): [number, number, number, number] => {
18
+ const pad = maxLocationCircleSize;
19
+ const bounds = new WebMercatorViewport({
20
+ ...viewport,
21
+ width: viewport.width + pad * 2,
22
+ height: viewport.height + pad * 2,
23
+ }).getBounds();
24
+ return [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]];
25
+ };
26
+
27
+ export const getFlowThicknessScale = (
28
+ magnitudeExtent: [number, number] | undefined,
29
+ ) => {
30
+ if (!magnitudeExtent) return undefined;
31
+ return scaleLinear()
32
+ .range([0.025, 0.5])
33
+ .domain([
34
+ 0,
35
+ // should support diff mode too
36
+ Math.max.apply(
37
+ null,
38
+ magnitudeExtent.map((x: number | undefined) => Math.abs(x || 0)),
39
+ ),
40
+ ]);
41
+ };
42
+
43
+ /**
44
+ * Adding meaningful cluster names.
45
+ * NOTE: this will mutate the nodes in clusterIndex
46
+ */
47
+ export function addClusterNames<L, F>(
48
+ clusterIndex: ClusterIndex<F>,
49
+ clusterLevels: ClusterLevel[],
50
+ locationsById: Map<string | number, L>,
51
+ locationAccessors: LocationAccessors<L>,
52
+ getLocationWeight: LocationWeightGetter,
53
+ ): void {
54
+ const {getLocationId, getLocationName, getLocationClusterName} =
55
+ locationAccessors;
56
+ const getName = (id: string | number) => {
57
+ const loc = locationsById.get(id);
58
+ if (loc) {
59
+ return getLocationName ? getLocationName(loc) : getLocationId(loc) || id;
60
+ }
61
+ return `"${id}"`;
62
+ };
63
+ for (const level of clusterLevels) {
64
+ for (const node of level.nodes) {
65
+ // Here mutating the nodes (adding names)
66
+ if (isCluster(node)) {
67
+ const leaves = clusterIndex.expandCluster(node);
68
+
69
+ leaves.sort((a, b) =>
70
+ descending(getLocationWeight(a), getLocationWeight(b)),
71
+ );
72
+
73
+ if (getLocationClusterName) {
74
+ node.name = getLocationClusterName(leaves);
75
+ } else {
76
+ const topId = leaves[0];
77
+ const otherId = leaves.length === 2 ? leaves[1] : undefined;
78
+ node.name = `"${getName(topId)}" and ${
79
+ otherId ? `"${getName(otherId)}"` : `${leaves.length - 1} others`
80
+ }`;
81
+ }
82
+ } else {
83
+ (node as any).name = getName(node.id);
84
+ }
85
+ }
86
+ }
87
+ }
package/src/types.ts CHANGED
@@ -1,6 +1,7 @@
1
- export type FlowMapData<L, F> = {
2
- locations: L[] | undefined;
3
- flows: F[] | undefined;
1
+ export type FlowmapData<L, F> = {
2
+ locations: Iterable<L> | undefined;
3
+ flows: Iterable<F> | undefined;
4
+ clusterLevels?: ClusterLevels;
4
5
  };
5
6
 
6
7
  export interface ViewState {
@@ -16,24 +17,25 @@ export type FlowAccessor<F, T> = (flow: F) => T; // objectInfo?: AccessorObjectI
16
17
  export type LocationAccessor<L, T> = (location: L) => T;
17
18
 
18
19
  export interface FlowAccessors<F> {
19
- getFlowOriginId: FlowAccessor<F, string>;
20
- getFlowDestId: FlowAccessor<F, string>;
20
+ getFlowOriginId: FlowAccessor<F, string | number>;
21
+ getFlowDestId: FlowAccessor<F, string | number>;
21
22
  getFlowMagnitude: FlowAccessor<F, number>;
22
23
  getFlowTime?: FlowAccessor<F, Date>; // TODO: use number instead of Date
23
24
  // getFlowColor?: FlowAccessor<string | undefined>;
24
25
  }
25
26
 
26
27
  export interface LocationAccessors<L> {
27
- getLocationId: LocationAccessor<L, string>;
28
+ getLocationId: LocationAccessor<L, string | number>;
28
29
  getLocationName?: LocationAccessor<L, string>;
29
- getLocationCentroid: LocationAccessor<L, [number, number]>;
30
- getLocationClusterName?: (locationIds: string[]) => string;
30
+ getLocationLat: LocationAccessor<L, number>;
31
+ getLocationLon: LocationAccessor<L, number>;
32
+ getLocationClusterName?: (locationIds: (string | number)[]) => string;
31
33
  // getLocationTotalIn?: LocationAccessor<number>;
32
34
  // getLocationTotalOut?: LocationAccessor<number>;
33
35
  // getLocationTotalInternal?: LocationAccessor<number>;
34
36
  }
35
37
 
36
- export type FlowMapDataAccessors<L, F> = LocationAccessors<L> &
38
+ export type FlowmapDataAccessors<L, F> = LocationAccessors<L> &
37
39
  FlowAccessors<F>;
38
40
 
39
41
  export interface LocationTotals {
@@ -58,9 +60,9 @@ export interface ViewportProps {
58
60
  height: number;
59
61
  latitude: number;
60
62
  longitude: number;
61
- zoom: number;
62
- bearing: number;
63
- pitch: number;
63
+ zoom?: number;
64
+ bearing?: number;
65
+ pitch?: number;
64
66
  altitude?: number;
65
67
  maxZoom?: number;
66
68
  minZoom?: number;
@@ -73,9 +75,10 @@ export interface ViewportProps {
73
75
  }
74
76
 
75
77
  export interface ClusterNode {
76
- id: string;
78
+ id: string | number;
77
79
  zoom: number;
78
- centroid: [number, number];
80
+ lat: number;
81
+ lon: number;
79
82
  }
80
83
 
81
84
  export interface ClusterLevel {
@@ -102,8 +105,8 @@ export function isLocationClusterNode<L>(l: L | ClusterNode): l is ClusterNode {
102
105
  }
103
106
 
104
107
  export interface AggregateFlow {
105
- origin: string;
106
- dest: string;
108
+ origin: string | number;
109
+ dest: string | number;
107
110
  count: number;
108
111
  aggregate: true;
109
112
  }
@@ -113,9 +116,9 @@ export function isAggregateFlow(
113
116
  ): flow is AggregateFlow {
114
117
  return (
115
118
  flow &&
116
- flow.origin !== undefined &&
117
- flow.dest !== undefined &&
118
- flow.count !== undefined &&
119
+ // flow.origin !== undefined &&
120
+ // flow.dest !== undefined &&
121
+ // flow.count !== undefined &&
119
122
  (flow.aggregate ? true : false)
120
123
  );
121
124
  }
@@ -157,6 +160,7 @@ export interface FlowLinesLayerAttributes {
157
160
  export interface LayersData {
158
161
  circleAttributes: FlowCirclesLayerAttributes;
159
162
  lineAttributes: FlowLinesLayerAttributes;
163
+ locationLabels?: string[];
160
164
  }
161
165
 
162
166
  export type LayersDataAttrValues<T> = {value: T; size: number};
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==