@flowmap.gl/data 8.0.0-alpha.20 → 8.0.0-alpha.21

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.
@@ -151,9 +151,8 @@ export default class FlowmapSelectors<L, F> {
151
151
  props: FlowmapData<L, F>,
152
152
  ) => state.settings.animationEnabled;
153
153
 
154
- getInvalidLocationIds: Selector<L, F, string[] | undefined> = createSelector(
155
- this.getLocationsFromProps,
156
- (locations) => {
154
+ getInvalidLocationIds: Selector<L, F, (string | number)[] | undefined> =
155
+ createSelector(this.getLocationsFromProps, (locations) => {
157
156
  if (!locations) return undefined;
158
157
  const invalid = [];
159
158
  for (const location of locations) {
@@ -165,8 +164,7 @@ export default class FlowmapSelectors<L, F> {
165
164
  }
166
165
  }
167
166
  return invalid.length > 0 ? invalid : undefined;
168
- },
169
- );
167
+ });
170
168
 
171
169
  getLocations: Selector<L, F, Iterable<L> | undefined> = createSelector(
172
170
  this.getLocationsFromProps,
@@ -186,17 +184,15 @@ export default class FlowmapSelectors<L, F> {
186
184
  },
187
185
  );
188
186
 
189
- getLocationIds: Selector<L, F, Set<string> | undefined> = createSelector(
190
- this.getLocations,
191
- (locations) => {
187
+ getLocationIds: Selector<L, F, Set<string | number> | undefined> =
188
+ createSelector(this.getLocations, (locations) => {
192
189
  if (!locations) return undefined;
193
- const ids = new Set<string>();
190
+ const ids = new Set<string | number>();
194
191
  for (const id of locations) {
195
192
  ids.add(this.accessors.getLocationId(id));
196
193
  }
197
194
  return ids;
198
- },
199
- );
195
+ });
200
196
 
201
197
  getSelectedLocationsSet: Selector<L, F, Set<string> | undefined> =
202
198
  createSelector(this.getSelectedLocations, (ids) =>
@@ -317,17 +313,15 @@ export default class FlowmapSelectors<L, F> {
317
313
  },
318
314
  );
319
315
 
320
- getLocationsById: Selector<L, F, Map<string, L> | undefined> = createSelector(
321
- this.getLocationsHavingFlows,
322
- (locations) => {
316
+ getLocationsById: Selector<L, F, Map<string | number, L> | undefined> =
317
+ createSelector(this.getLocationsHavingFlows, (locations) => {
323
318
  if (!locations) return undefined;
324
- const locationsById = new Map<string, L>();
319
+ const locationsById = new Map<string | number, L>();
325
320
  for (const location of locations) {
326
321
  locationsById.set(this.accessors.getLocationId(location), location);
327
322
  }
328
323
  return locationsById;
329
- },
330
- );
324
+ });
331
325
 
332
326
  getLocationWeightGetter: Selector<L, F, LocationWeightGetter | undefined> =
333
327
  createSelector(this.getSortedFlowsForKnownLocations, (flows) => {
@@ -371,7 +365,7 @@ export default class FlowmapSelectors<L, F> {
371
365
  this.accessors.getFlowmapDataAccessors();
372
366
 
373
367
  // Adding meaningful names
374
- const getName = (id: string) => {
368
+ const getName = (id: string | number) => {
375
369
  const loc = locationsById.get(id);
376
370
  if (loc) {
377
371
  return getLocationName
@@ -555,27 +549,28 @@ export default class FlowmapSelectors<L, F> {
555
549
  },
556
550
  );
557
551
 
558
- getUnknownLocations: Selector<L, F, Set<string> | undefined> = createSelector(
559
- this.getLocationIds,
560
- this.getFlowsFromProps,
561
- this.getSortedFlowsForKnownLocations,
562
- (ids, flows, flowsForKnownLocations) => {
563
- if (!ids || !flows) return undefined;
564
- if (
565
- flowsForKnownLocations
566
- // && flows.length === flowsForKnownLocations.length
567
- )
568
- return undefined;
569
- const missing = new Set<string>();
570
- for (const flow of flows) {
571
- if (!ids.has(this.accessors.getFlowOriginId(flow)))
572
- missing.add(this.accessors.getFlowOriginId(flow));
573
- if (!ids.has(this.accessors.getFlowDestId(flow)))
574
- missing.add(this.accessors.getFlowDestId(flow));
575
- }
576
- return missing;
577
- },
578
- );
552
+ getUnknownLocations: Selector<L, F, Set<string | number> | undefined> =
553
+ createSelector(
554
+ this.getLocationIds,
555
+ this.getFlowsFromProps,
556
+ this.getSortedFlowsForKnownLocations,
557
+ (ids, flows, flowsForKnownLocations) => {
558
+ if (!ids || !flows) return undefined;
559
+ if (
560
+ flowsForKnownLocations
561
+ // && flows.length === flowsForKnownLocations.length
562
+ )
563
+ return undefined;
564
+ const missing = new Set<string | number>();
565
+ for (const flow of flows) {
566
+ if (!ids.has(this.accessors.getFlowOriginId(flow)))
567
+ missing.add(this.accessors.getFlowOriginId(flow));
568
+ if (!ids.has(this.accessors.getFlowDestId(flow)))
569
+ missing.add(this.accessors.getFlowDestId(flow));
570
+ }
571
+ return missing;
572
+ },
573
+ );
579
574
 
580
575
  getSortedAggregatedFilteredFlows: Selector<
581
576
  L,
@@ -713,47 +708,50 @@ export default class FlowmapSelectors<L, F> {
713
708
  },
714
709
  );
715
710
 
716
- getLocationTotals: Selector<L, F, Map<string, LocationTotals> | undefined> =
717
- createSelector(
718
- this.getLocationsForZoom,
719
- this.getSortedAggregatedFilteredFlows,
720
- this.getSelectedLocationsSet,
721
- this.getLocationFilterMode,
722
- (locations, flows, selectedLocationsSet, locationFilterMode) => {
723
- if (!flows) return undefined;
724
- const totals = new Map<string, LocationTotals>();
725
- const add = (
726
- id: string,
727
- d: Partial<LocationTotals>,
728
- ): LocationTotals => {
729
- const rv = totals.get(id) ?? {
730
- incomingCount: 0,
731
- outgoingCount: 0,
732
- internalCount: 0,
733
- };
734
- if (d.incomingCount != null) rv.incomingCount += d.incomingCount;
735
- if (d.outgoingCount != null) rv.outgoingCount += d.outgoingCount;
736
- if (d.internalCount != null) rv.internalCount += d.internalCount;
737
- return rv;
711
+ getLocationTotals: Selector<
712
+ L,
713
+ F,
714
+ Map<string | number, LocationTotals> | undefined
715
+ > = createSelector(
716
+ this.getLocationsForZoom,
717
+ this.getSortedAggregatedFilteredFlows,
718
+ this.getSelectedLocationsSet,
719
+ this.getLocationFilterMode,
720
+ (locations, flows, selectedLocationsSet, locationFilterMode) => {
721
+ if (!flows) return undefined;
722
+ const totals = new Map<string | number, LocationTotals>();
723
+ const add = (
724
+ id: string | number,
725
+ d: Partial<LocationTotals>,
726
+ ): LocationTotals => {
727
+ const rv = totals.get(id) ?? {
728
+ incomingCount: 0,
729
+ outgoingCount: 0,
730
+ internalCount: 0,
738
731
  };
739
- for (const f of flows) {
740
- if (
741
- this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode)
742
- ) {
743
- const originId = this.accessors.getFlowOriginId(f);
744
- const destId = this.accessors.getFlowDestId(f);
745
- const count = this.accessors.getFlowMagnitude(f);
746
- if (originId === destId) {
747
- totals.set(originId, add(originId, {internalCount: count}));
748
- } else {
749
- totals.set(originId, add(originId, {outgoingCount: count}));
750
- totals.set(destId, add(destId, {incomingCount: count}));
751
- }
732
+ if (d.incomingCount != null) rv.incomingCount += d.incomingCount;
733
+ if (d.outgoingCount != null) rv.outgoingCount += d.outgoingCount;
734
+ if (d.internalCount != null) rv.internalCount += d.internalCount;
735
+ return rv;
736
+ };
737
+ for (const f of flows) {
738
+ if (
739
+ this.isFlowInSelection(f, selectedLocationsSet, locationFilterMode)
740
+ ) {
741
+ const originId = this.accessors.getFlowOriginId(f);
742
+ const destId = this.accessors.getFlowDestId(f);
743
+ const count = this.accessors.getFlowMagnitude(f);
744
+ if (originId === destId) {
745
+ totals.set(originId, add(originId, {internalCount: count}));
746
+ } else {
747
+ totals.set(originId, add(originId, {outgoingCount: count}));
748
+ totals.set(destId, add(destId, {incomingCount: count}));
752
749
  }
753
750
  }
754
- return totals;
755
- },
756
- );
751
+ }
752
+ return totals;
753
+ },
754
+ );
757
755
 
758
756
  getLocationsTree: Selector<L, F, KDBushTree> = createSelector(
759
757
  this.getLocationsForZoom,
@@ -787,7 +785,7 @@ export default class FlowmapSelectors<L, F> {
787
785
  },
788
786
  );
789
787
 
790
- getLocationIdsInViewport: Selector<L, F, Set<string> | undefined> =
788
+ getLocationIdsInViewport: Selector<L, F, Set<string | number> | undefined> =
791
789
  createSelectorCreator(
792
790
  defaultMemoize,
793
791
  // @ts-ignore
@@ -1008,7 +1006,7 @@ export default class FlowmapSelectors<L, F> {
1008
1006
  this.getCircleSizeScale,
1009
1007
  this.getLocationTotals,
1010
1008
  (circleSizeScale, locationTotals) => {
1011
- return (locationId: string) => {
1009
+ return (locationId: string | number) => {
1012
1010
  const total = locationTotals?.get(locationId);
1013
1011
  if (total && circleSizeScale) {
1014
1012
  return (
@@ -1026,7 +1024,7 @@ export default class FlowmapSelectors<L, F> {
1026
1024
  this.getCircleSizeScale,
1027
1025
  this.getLocationTotals,
1028
1026
  (circleSizeScale, locationTotals) => {
1029
- return (locationId: string) => {
1027
+ return (locationId: string | number) => {
1030
1028
  const total = locationTotals?.get(locationId);
1031
1029
  if (total && circleSizeScale) {
1032
1030
  return (
@@ -1162,10 +1160,10 @@ export default class FlowmapSelectors<L, F> {
1162
1160
  locations: (L | ClusterNode)[] | undefined,
1163
1161
  flows: (F | AggregateFlow)[] | undefined,
1164
1162
  flowmapColors: DiffColorsRGBA | ColorsRGBA,
1165
- locationsById: Map<string, L | ClusterNode> | undefined,
1166
- locationIdsInViewport: Set<string> | undefined,
1167
- getInCircleSize: (locationId: string) => number,
1168
- getOutCircleSize: (locationId: string) => number,
1163
+ locationsById: Map<string | number, L | ClusterNode> | undefined,
1164
+ locationIdsInViewport: Set<string | number> | undefined,
1165
+ getInCircleSize: (locationId: string | number) => number,
1166
+ getOutCircleSize: (locationId: string | number) => number,
1169
1167
  flowThicknessScale: ScaleLinear<number, number, never> | undefined,
1170
1168
  animationEnabled: boolean,
1171
1169
  ): LayersData {
@@ -1339,7 +1337,7 @@ export default class FlowmapSelectors<L, F> {
1339
1337
 
1340
1338
  isFlowInSelection(
1341
1339
  flow: F | AggregateFlow,
1342
- selectedLocationsSet: Set<string> | undefined,
1340
+ selectedLocationsSet: Set<string | number> | undefined,
1343
1341
  locationFilterMode?: LocationFilterMode,
1344
1342
  ) {
1345
1343
  const origin = this.accessors.getFlowOriginId(flow);
@@ -1386,8 +1384,8 @@ export default class FlowmapSelectors<L, F> {
1386
1384
  }
1387
1385
 
1388
1386
  function calcLocationTotalsExtent(
1389
- locationTotals: Map<string, LocationTotals> | undefined,
1390
- locationIdsInViewport: Set<string> | undefined,
1387
+ locationTotals: Map<string | number, LocationTotals> | undefined,
1388
+ locationIdsInViewport: Set<string | number> | undefined,
1391
1389
  ) {
1392
1390
  if (!locationTotals) return undefined;
1393
1391
  let rv: [number, number] | undefined = undefined;
@@ -27,14 +27,14 @@ import {
27
27
  } from './../types';
28
28
  import {ascending, bisectLeft, extent} from 'd3-array';
29
29
 
30
- export type LocationWeightGetter = (id: string) => number;
30
+ export type LocationWeightGetter = (id: string | number) => number;
31
31
 
32
32
  /**
33
33
  * A data structure representing the cluster levels for efficient flow aggregation.
34
34
  */
35
35
  export interface ClusterIndex<F> {
36
36
  availableZoomLevels: number[];
37
- getClusterById: (clusterId: string) => Cluster | undefined;
37
+ getClusterById: (clusterId: string | number) => Cluster | undefined;
38
38
  /**
39
39
  * List the nodes on the given zoom level.
40
40
  */
@@ -50,7 +50,10 @@ export interface ClusterIndex<F> {
50
50
  /**
51
51
  * Find the cluster the given location is residing in on the specified zoom level.
52
52
  */
53
- findClusterFor: (locationId: string, zoom: number) => string | undefined;
53
+ findClusterFor: (
54
+ locationId: string | number,
55
+ zoom: number,
56
+ ) => string | number | undefined;
54
57
  /**
55
58
  * Aggregate flows for the specified zoom level.
56
59
  */
@@ -69,8 +72,8 @@ export interface ClusterIndex<F> {
69
72
  */
70
73
  export function buildIndex<F>(clusterLevels: ClusterLevels): ClusterIndex<F> {
71
74
  const nodesByZoom = new Map<number, ClusterNode[]>();
72
- const clustersById = new Map<string, Cluster>();
73
- const minZoomByLocationId = new Map<string, number>();
75
+ const clustersById = new Map<string | number, Cluster>();
76
+ const minZoomByLocationId = new Map<string | number, number>();
74
77
  for (const {zoom, nodes} of clusterLevels) {
75
78
  nodesByZoom.set(zoom, nodes);
76
79
  for (const node of nodes) {
@@ -91,7 +94,10 @@ export function buildIndex<F>(clusterLevels: ClusterLevels): ClusterIndex<F> {
91
94
  throw new Error('Could not determine minZoom or maxZoom');
92
95
  }
93
96
 
94
- const leavesToClustersByZoom = new Map<number, Map<string, Cluster>>();
97
+ const leavesToClustersByZoom = new Map<
98
+ number,
99
+ Map<string | number, Cluster>
100
+ >();
95
101
 
96
102
  for (const cluster of clustersById.values()) {
97
103
  const {zoom} = cluster;
@@ -118,7 +124,7 @@ export function buildIndex<F>(clusterLevels: ClusterLevels): ClusterIndex<F> {
118
124
 
119
125
  const expandCluster = (cluster: Cluster, targetZoom: number = maxZoom) => {
120
126
  const ids: string[] = [];
121
- const visit = (c: Cluster, expandedIds: string[]) => {
127
+ const visit = (c: Cluster, expandedIds: (string | number)[]) => {
122
128
  if (targetZoom > c.zoom) {
123
129
  for (const childId of c.children) {
124
130
  const child = clustersById.get(childId);
@@ -136,7 +142,7 @@ export function buildIndex<F>(clusterLevels: ClusterLevels): ClusterIndex<F> {
136
142
  return ids;
137
143
  };
138
144
 
139
- function findClusterFor(locationId: string, zoom: number) {
145
+ function findClusterFor(locationId: string | number, zoom: number) {
140
146
  const leavesToClusters = leavesToClustersByZoom.get(zoom);
141
147
  if (!leavesToClusters) {
142
148
  return undefined;
@@ -179,7 +185,8 @@ export function buildIndex<F>(clusterLevels: ClusterLevels): ClusterIndex<F> {
179
185
  }
180
186
  const result: (F | AggregateFlow)[] = [];
181
187
  const aggFlowsByKey = new Map<string, AggregateFlow>();
182
- const makeKey = (origin: string, dest: string) => `${origin}:${dest}`;
188
+ const makeKey = (origin: string | number, dest: string | number) =>
189
+ `${origin}:${dest}`;
183
190
  const {
184
191
  flowCountsMapReduce = {
185
192
  map: getFlowMagnitude,
@@ -223,8 +230,8 @@ export function makeLocationWeightGetter<F>(
223
230
  {getFlowOriginId, getFlowDestId, getFlowMagnitude}: FlowAccessors<F>,
224
231
  ): LocationWeightGetter {
225
232
  const locationTotals = {
226
- incoming: new Map<string, number>(),
227
- outgoing: new Map<string, number>(),
233
+ incoming: new Map<string | number, number>(),
234
+ outgoing: new Map<string | number, number>(),
228
235
  };
229
236
  for (const flow of flows) {
230
237
  const origin = getFlowOriginId(flow);
@@ -239,7 +246,7 @@ export function makeLocationWeightGetter<F>(
239
246
  (locationTotals.outgoing.get(origin) || 0) + count,
240
247
  );
241
248
  }
242
- return (id: string) =>
249
+ return (id: string | number) =>
243
250
  Math.max(
244
251
  Math.abs(locationTotals.incoming.get(id) || 0),
245
252
  Math.abs(locationTotals.outgoing.get(id) || 0),
@@ -172,11 +172,11 @@ export function clusterLocations<L>(
172
172
  const clusterLevels = new Array<ClusterLevel>();
173
173
  prevZoom = NaN;
174
174
  for (let zoom = maxAvailZoom; zoom >= minAvailZoom; zoom--) {
175
- let childrenByParent: Map<number, string[]> | undefined;
175
+ let childrenByParent: Map<number, (string | number)[]> | undefined;
176
176
  const tree = trees[zoom];
177
177
  if (!tree) continue;
178
178
  if (zoom < maxAvailZoom) {
179
- childrenByParent = rollup<Point<L>, string[], number>(
179
+ childrenByParent = rollup<Point<L>, (string | number)[], number>(
180
180
  trees[prevZoom].points,
181
181
  (points: any[]) =>
182
182
  points.map((p: any) =>
@@ -23,11 +23,13 @@ export default interface FlowmapDataProvider<L, F> {
23
23
 
24
24
  getFlowByIndex(index: number): Promise<F | AggregateFlow | undefined>;
25
25
 
26
- getLocationById(id: string): Promise<L | Cluster | undefined>;
26
+ getLocationById(id: string | number): Promise<L | Cluster | undefined>;
27
27
 
28
28
  getLocationByIndex(idx: number): Promise<L | ClusterNode | undefined>;
29
29
 
30
- getTotalsForLocation(id: string): Promise<LocationTotals | undefined>;
30
+ getTotalsForLocation(
31
+ id: string | number,
32
+ ): Promise<LocationTotals | undefined>;
31
33
 
32
34
  // getLocationsInBbox(
33
35
  // bbox: [number, number, number, number],
@@ -72,7 +72,7 @@ export default class LocalFlowmapDataProvider<L, F>
72
72
  return this.selectors.getLayersData(this.flowmapState, this.flowmapData);
73
73
  }
74
74
 
75
- async getLocationById(id: string): Promise<L | Cluster | undefined> {
75
+ async getLocationById(id: string | number): Promise<L | Cluster | undefined> {
76
76
  if (!this.flowmapState || !this.flowmapData) {
77
77
  return undefined;
78
78
  }
@@ -93,7 +93,9 @@ export default class LocalFlowmapDataProvider<L, F>
93
93
  return locationsById?.get(id);
94
94
  }
95
95
 
96
- async getTotalsForLocation(id: string): Promise<LocationTotals | undefined> {
96
+ async getTotalsForLocation(
97
+ id: string | number,
98
+ ): Promise<LocationTotals | undefined> {
97
99
  if (!this.flowmapState || !this.flowmapData) {
98
100
  return undefined;
99
101
  }
package/src/types.ts CHANGED
@@ -17,19 +17,19 @@ export type FlowAccessor<F, T> = (flow: F) => T; // objectInfo?: AccessorObjectI
17
17
  export type LocationAccessor<L, T> = (location: L) => T;
18
18
 
19
19
  export interface FlowAccessors<F> {
20
- getFlowOriginId: FlowAccessor<F, string>;
21
- getFlowDestId: FlowAccessor<F, string>;
20
+ getFlowOriginId: FlowAccessor<F, string | number>;
21
+ getFlowDestId: FlowAccessor<F, string | number>;
22
22
  getFlowMagnitude: FlowAccessor<F, number>;
23
23
  getFlowTime?: FlowAccessor<F, Date>; // TODO: use number instead of Date
24
24
  // getFlowColor?: FlowAccessor<string | undefined>;
25
25
  }
26
26
 
27
27
  export interface LocationAccessors<L> {
28
- getLocationId: LocationAccessor<L, string>;
29
- getLocationName?: LocationAccessor<L, string>;
28
+ getLocationId: LocationAccessor<L, string | number>;
29
+ getLocationName?: LocationAccessor<L, string | number>;
30
30
  getLocationLat: LocationAccessor<L, number>;
31
31
  getLocationLon: LocationAccessor<L, number>;
32
- getLocationClusterName?: (locationIds: string[]) => string;
32
+ getLocationClusterName?: (locationIds: (string | number)[]) => string;
33
33
  // getLocationTotalIn?: LocationAccessor<number>;
34
34
  // getLocationTotalOut?: LocationAccessor<number>;
35
35
  // getLocationTotalInternal?: LocationAccessor<number>;
@@ -75,7 +75,7 @@ export interface ViewportProps {
75
75
  }
76
76
 
77
77
  export interface ClusterNode {
78
- id: string;
78
+ id: string | number;
79
79
  zoom: number;
80
80
  lat: number;
81
81
  lon: number;
@@ -105,8 +105,8 @@ export function isLocationClusterNode<L>(l: L | ClusterNode): l is ClusterNode {
105
105
  }
106
106
 
107
107
  export interface AggregateFlow {
108
- origin: string;
109
- dest: string;
108
+ origin: string | number;
109
+ dest: string | number;
110
110
  count: number;
111
111
  aggregate: true;
112
112
  }