@flowmap.gl/data 8.0.0-alpha.25 → 8.0.0-alpha.27

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 (55) hide show
  1. package/.turbo/turbo-build.log +3 -0
  2. package/.turbo/turbo-dev.log +0 -0
  3. package/LICENSE +2 -2
  4. package/dist/FlowmapAggregateAccessors.d.ts +1 -1
  5. package/dist/FlowmapAggregateAccessors.d.ts.map +1 -1
  6. package/dist/FlowmapAggregateAccessors.js +6 -1
  7. package/dist/FlowmapSelectors.d.ts +12 -84
  8. package/dist/FlowmapSelectors.d.ts.map +1 -1
  9. package/dist/FlowmapSelectors.js +68 -56
  10. package/dist/FlowmapState.d.ts +1 -0
  11. package/dist/FlowmapState.d.ts.map +1 -1
  12. package/dist/FlowmapState.js +6 -1
  13. package/dist/cluster/ClusterIndex.d.ts +1 -1
  14. package/dist/cluster/ClusterIndex.d.ts.map +1 -1
  15. package/dist/cluster/ClusterIndex.js +5 -17
  16. package/dist/cluster/cluster.d.ts +20 -1
  17. package/dist/cluster/cluster.d.ts.map +1 -1
  18. package/dist/cluster/cluster.js +69 -64
  19. package/dist/colors.d.ts +2 -2
  20. package/dist/colors.d.ts.map +1 -1
  21. package/dist/colors.js +17 -6
  22. package/dist/getViewStateForLocations.d.ts +2 -2
  23. package/dist/getViewStateForLocations.d.ts.map +1 -1
  24. package/dist/getViewStateForLocations.js +18 -8
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -1
  27. package/dist/provider/FlowmapDataProvider.d.ts.map +1 -1
  28. package/dist/provider/FlowmapDataProvider.js +6 -1
  29. package/dist/provider/LocalFlowmapDataProvider.d.ts +1 -1
  30. package/dist/provider/LocalFlowmapDataProvider.d.ts.map +1 -1
  31. package/dist/provider/LocalFlowmapDataProvider.js +60 -81
  32. package/dist/selector-functions.d.ts.map +1 -1
  33. package/dist/selector-functions.js +11 -2
  34. package/dist/time.d.ts.map +1 -1
  35. package/dist/time.js +6 -1
  36. package/dist/types.d.ts +6 -6
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/types.js +6 -1
  39. package/dist/util.d.ts.map +1 -1
  40. package/dist/util.js +6 -1
  41. package/package.json +17 -16
  42. package/src/FlowmapAggregateAccessors.ts +10 -1
  43. package/src/FlowmapSelectors.ts +21 -24
  44. package/src/FlowmapState.ts +7 -0
  45. package/src/cluster/ClusterIndex.ts +3 -15
  46. package/src/cluster/cluster.ts +100 -66
  47. package/src/colors.ts +6 -0
  48. package/src/getViewStateForLocations.ts +6 -0
  49. package/src/index.ts +6 -0
  50. package/src/provider/FlowmapDataProvider.ts +6 -0
  51. package/src/provider/LocalFlowmapDataProvider.ts +10 -2
  52. package/src/selector-functions.ts +6 -0
  53. package/src/time.ts +6 -0
  54. package/src/types.ts +6 -0
  55. package/src/util.ts +6 -0
@@ -1,19 +1,7 @@
1
1
  /*
2
- * Copyright 2022 FlowmapBlue
3
- * Copyright 2018-2020 Teralytics, modified by FlowmapBlue
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- *
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
17
5
  */
18
6
 
19
7
  import {ascending, descending, extent, min, rollup} from 'd3-array';
@@ -83,7 +71,10 @@ export type Selector<L, F, T> = ParametricSelector<
83
71
  T
84
72
  >;
85
73
 
86
- export default class FlowmapSelectors<L, F> {
74
+ export default class FlowmapSelectors<
75
+ L extends Record<string, any>,
76
+ F extends Record<string, any>,
77
+ > {
87
78
  accessors: FlowmapAggregateAccessors<L, F>;
88
79
 
89
80
  constructor(accessors: FlowmapDataAccessors<L, F>) {
@@ -737,14 +728,20 @@ export default class FlowmapSelectors<L, F> {
737
728
  if (!locations) {
738
729
  return undefined;
739
730
  }
740
- return new KDBush(
741
- // @ts-ignore
742
- locations,
743
- (location: L | ClusterNode) =>
744
- lngX(this.accessors.getLocationLon(location)),
745
- (location: L | ClusterNode) =>
746
- latY(this.accessors.getLocationLat(location)),
747
- );
731
+ const nodes = Array.isArray(locations)
732
+ ? locations
733
+ : Array.from(locations);
734
+ const bush = new KDBush(nodes.length, 64, Float32Array);
735
+ for (let i = 0; i < nodes.length; i++) {
736
+ const node = nodes[i];
737
+ bush.add(
738
+ lngX(this.accessors.getLocationLon(node)),
739
+ latY(this.accessors.getLocationLat(node)),
740
+ );
741
+ }
742
+ bush.finish();
743
+ bush.points = nodes;
744
+ return bush;
748
745
  },
749
746
  );
750
747
 
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import {LocationFilterMode, ViewportProps} from './types';
2
8
 
3
9
  export interface FilterState {
@@ -10,6 +16,7 @@ export interface SettingsState {
10
16
  animationEnabled: boolean;
11
17
  fadeEnabled: boolean;
12
18
  fadeOpacityEnabled: boolean;
19
+ locationsEnabled: boolean;
13
20
  locationTotalsEnabled: boolean;
14
21
  locationLabelsEnabled: boolean;
15
22
  adaptiveScalesEnabled: boolean;
@@ -1,19 +1,7 @@
1
1
  /*
2
- * Copyright 2022 FlowmapBlue
3
- * Copyright 2018-2020 Teralytics, modified by FlowmapBlue
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- *
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
17
5
  */
18
6
 
19
7
  import {
@@ -1,46 +1,34 @@
1
1
  /*
2
- * Copyright 2022 FlowmapBlue
3
- * Copyright 2018-2020 Teralytics, modified by FlowmapBlue
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- *
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
17
5
  */
18
6
 
19
- /**
20
- * The code in this file is a based on https://github.com/mapbox/supercluster
21
- */
22
-
23
- // ISC License
24
- //
25
- // Copyright (c) 2016, Mapbox
26
- //
27
- // Permission to use, copy, modify, and/or distribute this software for any purpose
28
- // with or without fee is hereby granted, provided that the above copyright notice
29
- // and this permission notice appear in all copies.
30
- //
31
- // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
32
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
33
- // FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
34
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
35
- // OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
36
- // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
37
- // THIS SOFTWARE.
38
-
39
7
  import {min, rollup} from 'd3-array';
40
8
  import KDBush from 'kdbush';
41
9
  import {LocationWeightGetter} from './ClusterIndex';
42
10
  import {Cluster, ClusterLevel, ClusterNode, LocationAccessors} from '../types';
43
11
 
12
+ /**
13
+ * The code in this file is a based on https://github.com/mapbox/supercluster
14
+ *
15
+ * ISC License
16
+ *
17
+ * Copyright (c) 2016, Mapbox
18
+ *
19
+ * Permission to use, copy, modify, and/or distribute this software for any purpose
20
+ * with or without fee is hereby granted, provided that the above copyright notice
21
+ * and this permission notice appear in all copies.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
24
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
25
+ * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
26
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
27
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
28
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
29
+ * THIS SOFTWARE.
30
+ */
31
+
44
32
  export interface Options {
45
33
  minZoom: number; // min zoom to generate clusters on
46
34
  maxZoom: number; // max zoom level to cluster the points on
@@ -126,9 +114,19 @@ export function clusterLocations<L>(
126
114
  locationsCount++;
127
115
  }
128
116
 
117
+ const makeBush = (points: Point<L>[]) => {
118
+ const bush = new KDBush(points.length, nodeSize, Float32Array);
119
+ for (let i = 0; i < points.length; i++) {
120
+ bush.add(points[i].x, points[i].y);
121
+ }
122
+ bush.finish();
123
+ bush.points = points;
124
+ return bush;
125
+ };
126
+
129
127
  // cluster points on max zoom, then cluster the results on previous zoom, etc.;
130
128
  // results in a cluster hierarchy across zoom levels
131
- trees[maxZoom + 1] = new KDBush(clusters, getX, getY, nodeSize, Float32Array);
129
+ trees[maxZoom + 1] = makeBush(clusters);
132
130
  let prevZoom = maxZoom + 1;
133
131
 
134
132
  for (let z = maxZoom; z >= minZoom; z--) {
@@ -144,7 +142,7 @@ export function clusterLocations<L>(
144
142
  } else {
145
143
  prevZoom = z;
146
144
  clusters = _clusters;
147
- trees[z] = new KDBush(clusters, getX, getY, nodeSize, Float32Array);
145
+ trees[z] = makeBush(clusters);
148
146
  }
149
147
  }
150
148
 
@@ -152,21 +150,34 @@ export function clusterLocations<L>(
152
150
  return [];
153
151
  }
154
152
 
155
- const numbersOfClusters = trees.map((d) => d?.points.length);
153
+ const numbersOfClusters: number[] = trees.map((d) => d?.points.length);
156
154
  const minClusters = min(numbersOfClusters.filter((d) => d > 0));
157
- const maxClusters = getMaxNumberOfClusters(locations, locationAccessors);
158
-
159
- let maxAvailZoom = numbersOfClusters.indexOf(maxClusters);
160
- if (maxClusters < locationsCount) {
161
- maxAvailZoom++;
162
- if (maxAvailZoom < maxZoom + 1) {
163
- trees[maxAvailZoom] = trees[maxZoom + 1];
164
- trees[maxZoom + 1] = undefined;
155
+
156
+ let maxAvailZoom =
157
+ findIndexOfMax(numbersOfClusters) ?? numbersOfClusters.length - 1;
158
+
159
+ const numUniqueLocations = countUniqueLocations(locations, locationAccessors);
160
+ if (numUniqueLocations < locationsCount) {
161
+ // Duplicate locations would be clustered together at any zoom level which can lead to having too many zooms.
162
+ // To avoid that, we need to find the max zoom level that has less or equal clusters than unique locations
163
+ // and drop all zoom levels beyond that (except the unclustered level).
164
+ const maxClustersZoom = findLastIndex(
165
+ numbersOfClusters,
166
+ (d) => d <= numUniqueLocations,
167
+ );
168
+ if (maxClustersZoom >= 0) {
169
+ // Now, move the unclustered points to the next zoom level to avoid having a gap
170
+ if (maxClustersZoom < maxAvailZoom) {
171
+ trees[maxClustersZoom + 1] = trees[maxAvailZoom];
172
+ trees.splice(maxClustersZoom + 2); // Remove all zoom levels beyond maxClustersZoom
173
+ }
174
+ maxAvailZoom = maxClustersZoom + 1;
165
175
  }
166
176
  }
177
+
167
178
  const minAvailZoom = Math.min(
168
179
  maxAvailZoom,
169
- numbersOfClusters.lastIndexOf(minClusters),
180
+ minClusters ? numbersOfClusters.lastIndexOf(minClusters) : maxAvailZoom,
170
181
  );
171
182
 
172
183
  const clusterLevels = new Array<ClusterLevel>();
@@ -175,8 +186,8 @@ export function clusterLocations<L>(
175
186
  let childrenByParent: Map<number, (string | number)[]> | undefined;
176
187
  const tree = trees[zoom];
177
188
  if (!tree) continue;
178
- if (zoom < maxAvailZoom) {
179
- childrenByParent = rollup<Point<L>, (string | number)[], number>(
189
+ if (trees[prevZoom] && zoom < maxAvailZoom) {
190
+ childrenByParent = rollup(
180
191
  trees[prevZoom].points,
181
192
  (points: any[]) =>
182
193
  points.map((p: any) =>
@@ -200,16 +211,19 @@ export function clusterLocations<L>(
200
211
  const {id} = point;
201
212
  const children = childrenByParent && childrenByParent.get(id);
202
213
  if (!children) {
203
- throw new Error(`Cluster ${id} doesn't have children`);
214
+ // Might happen if there are multiple locations with same coordinates
215
+ console.warn(`Omitting cluster with no children, point:`, point);
216
+ continue;
204
217
  }
205
- nodes.push({
218
+ const cluster = {
206
219
  id: makeClusterId(id),
207
220
  name: makeClusterName(id, numPoints),
208
221
  zoom,
209
222
  lat: yLat(y),
210
223
  lon: xLng(x),
211
- children,
212
- } as Cluster);
224
+ children: children ?? [],
225
+ } as Cluster;
226
+ nodes.push(cluster);
213
227
  }
214
228
  }
215
229
  clusterLevels.push({
@@ -329,32 +343,52 @@ function getY<L>(p: Point<L>) {
329
343
  return p.y;
330
344
  }
331
345
 
332
- /**
333
- * Finds groups of locations which share the same positions.
334
- * They will always be clustered together at any zoom level
335
- * which can lead to having too many zooms.
336
- */
337
- function getMaxNumberOfClusters<L>(
346
+ function countUniqueLocations<L>(
338
347
  locations: Iterable<L>,
339
348
  locationAccessors: LocationAccessors<L>,
340
349
  ) {
341
350
  const {getLocationLon, getLocationLat} = locationAccessors;
342
351
  const countByLatLon = new Map<string, number>();
343
- let numLocations = 0;
352
+ let uniqueCnt = 0;
344
353
  for (const loc of locations) {
345
354
  const lon = getLocationLon(loc);
346
355
  const lat = getLocationLat(loc);
347
356
  const key = `${lon},${lat}`;
348
357
  const prev = countByLatLon.get(key);
358
+ if (!prev) {
359
+ uniqueCnt++;
360
+ }
349
361
  countByLatLon.set(key, prev ? prev + 1 : 1);
350
- numLocations++;
351
362
  }
363
+ return uniqueCnt;
364
+ }
365
+
366
+ function findIndexOfMax(arr: (number | undefined)[]): number | undefined {
367
+ let max = -Infinity;
368
+ let maxIndex: number | undefined = undefined;
369
+
370
+ for (let i = 0; i < arr.length; i++) {
371
+ const value = arr[i];
372
+
373
+ if (typeof value === 'number') {
374
+ if (value > max) {
375
+ max = value;
376
+ maxIndex = i;
377
+ }
378
+ }
379
+ }
380
+
381
+ return maxIndex;
382
+ }
352
383
 
353
- let numSame = 0;
354
- for (const [key, count] of countByLatLon) {
355
- if (count > 1) {
356
- numSame++;
384
+ function findLastIndex<T>(
385
+ arr: T[],
386
+ predicate: (value: T, index: number, array: T[]) => boolean,
387
+ ): number {
388
+ for (let i = arr.length - 1; i >= 0; i--) {
389
+ if (predicate(arr[i], i, arr)) {
390
+ return i;
357
391
  }
358
392
  }
359
- return numLocations - numSame;
393
+ return -1;
360
394
  }
package/src/colors.ts CHANGED
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import {
2
8
  interpolateCool,
3
9
  interpolateInferno,
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import {geoBounds} from 'd3-geo';
2
8
  import {fitBounds} from '@math.gl/web-mercator';
3
9
  import type {
package/src/index.ts CHANGED
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  export * from './types';
2
8
  export * from './colors';
3
9
  export * from './FlowmapState';
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import {AggregateFlow, Cluster, LocationAccessors, LocationTotals} from '..';
2
8
  import {FlowmapState} from '../FlowmapState';
3
9
  import {
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import type FlowmapDataProvider from './FlowmapDataProvider';
2
8
  import type {
3
9
  Cluster,
@@ -17,8 +23,10 @@ import {
17
23
  } from '../getViewStateForLocations';
18
24
  import {ClusterIndex} from '../cluster/ClusterIndex';
19
25
 
20
- export default class LocalFlowmapDataProvider<L, F>
21
- implements FlowmapDataProvider<L, F>
26
+ export default class LocalFlowmapDataProvider<
27
+ L extends Record<string, any>,
28
+ F extends Record<string, any>,
29
+ > implements FlowmapDataProvider<L, F>
22
30
  {
23
31
  private selectors: FlowmapSelectors<L, F>;
24
32
  private flowmapData: FlowmapData<L, F> | undefined;
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import {WebMercatorViewport} from '@math.gl/web-mercator';
2
8
  import {
3
9
  ClusterLevel,
package/src/time.ts CHANGED
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import {timeFormat, timeParse} from 'd3-time-format';
2
8
  import {
3
9
  timeDay,
package/src/types.ts CHANGED
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  export type FlowmapData<L, F> = {
2
8
  locations: Iterable<L> | undefined;
3
9
  flows: Iterable<F> | undefined;
package/src/util.ts CHANGED
@@ -1,3 +1,9 @@
1
+ /*
2
+ * Copyright (c) Flowmap.gl contributors
3
+ * Copyright (c) 2018-2020 Teralytics
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  import {createSelectorCreator, defaultMemoize} from 'reselect';
2
8
 
3
9
  export const createDebugSelector = createSelectorCreator(defaultMemoize, {