@carto/api-client 0.5.6 → 0.5.7-alpha-optional-spatial-filter.0

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.
@@ -0,0 +1,90 @@
1
+ /** Flags 'empty' values in a Uint32Array index. */
2
+ const EMPTY_U32 = 2 ** 32 - 1;
3
+
4
+ /**
5
+ * Custom Set-like interface optimized for BigUint64 cell IDs. Unlike Set,
6
+ * limited in most JavaScript runtimes to ~16M entries, this implementation
7
+ * can support up to `n = 2^32 - 1` (4 billion) entries, with lookups in
8
+ * amortized O(1) time.
9
+ */
10
+ export class CellSet {
11
+ /** List of cells stored by the set. Stored by reference, without copying. */
12
+ private cells: bigint[];
13
+
14
+ /** DataView representing a single cell ID. Pre-allocated to reduce memory during queries. */
15
+ private cellView = new DataView(new ArrayBuffer(8));
16
+
17
+ /** Hash table, mapping a hash index (computed) to an index in the 'cells' array. */
18
+ private hashTable: Uint32Array;
19
+
20
+ constructor(cells: bigint[]) {
21
+ this.cells = cells;
22
+
23
+ // Pre-allocate hash table for queries.
24
+ this.hashTable = new Uint32Array(hashBuckets(cells.length)).fill(EMPTY_U32);
25
+ for (let cellIndex = 0; cellIndex < cells.length; cellIndex++) {
26
+ this.hashTable[this.hashLookup(cells[cellIndex])] = cellIndex;
27
+ }
28
+ }
29
+
30
+ has(cell: bigint): boolean {
31
+ const hashIndex = this.hashLookup(cell);
32
+ return this.hashTable[hashIndex] !== EMPTY_U32;
33
+ }
34
+
35
+ private hashLookup(cell: bigint): number {
36
+ // Hash implementation operates on 32-bit chunks, so write the cell ID
37
+ // into a pre-allocated DataView for easier iteration.
38
+ this.cellView.setBigUint64(0, cell);
39
+ const hashval = hash(this.cellView);
40
+ const hashmod = this.hashTable.length - 1;
41
+ let bucket = hashval & hashmod;
42
+
43
+ // Find the first bucket in the hash table where either (a) no cell
44
+ // is yet stored, or (b) the stored cell and the query cell are equal.
45
+ for (let probe = 0; probe <= hashmod; probe++) {
46
+ const cellIndex = this.hashTable[bucket];
47
+
48
+ if (cellIndex === EMPTY_U32 || cell === this.cells[cellIndex]) {
49
+ return bucket;
50
+ }
51
+
52
+ bucket = (bucket + probe + 1) & hashmod; // Hash collision; quadratic probing.
53
+ }
54
+
55
+ throw new Error('Hash table full.'); // Unreachable.
56
+ }
57
+ }
58
+
59
+ /**
60
+ * MurmurHash2
61
+ *
62
+ * References:
63
+ * - https://github.com/mikolalysenko/murmurhash-js/blob/f19136e9f9c17f8cddc216ca3d44ec7c5c502f60/murmurhash2_gc.js#L14
64
+ * - https://github.com/zeux/meshoptimizer/blob/e47e1be6d3d9513153188216455bdbed40a206ef/src/indexgenerator.cpp#L12
65
+ */
66
+ function hash(view: DataView, h = 0): number {
67
+ const m = 0x5bd1e995;
68
+ const r = 24;
69
+
70
+ for (let i = 0, il = view.byteLength / 4; i < il; i++) {
71
+ let k = view.getUint32(i * 4);
72
+
73
+ k = Math.imul(k, m) >>> 0;
74
+ k = (k ^ (k >> r)) >>> 0;
75
+ k = Math.imul(k, m) >>> 0;
76
+
77
+ h = Math.imul(h, m) >>> 0;
78
+ h = (h ^ k) >>> 0;
79
+ }
80
+
81
+ return h;
82
+ }
83
+
84
+ function hashBuckets(initialCount: number) {
85
+ let buckets = 1;
86
+ while (buckets < initialCount + initialCount / 4) {
87
+ buckets *= 2;
88
+ }
89
+ return buckets;
90
+ }
@@ -74,14 +74,13 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
74
74
  this._features.length = 0;
75
75
  }
76
76
 
77
- protected _extractTileFeatures(spatialFilter: SpatialFilter) {
77
+ protected _extractTileFeatures(spatialFilter?: SpatialFilter) {
78
78
  // When spatial filter has not changed, don't redo extraction. If tiles or
79
79
  // tile extract options change, features will have been cleared already.
80
80
  const prevInputs = this._tileFeatureExtractPreviousInputs;
81
81
  if (
82
82
  this._features.length &&
83
- prevInputs.spatialFilter &&
84
- booleanEqual(prevInputs.spatialFilter, spatialFilter)
83
+ spatialFilterEquals(prevInputs.spatialFilter, spatialFilter)
85
84
  ) {
86
85
  return;
87
86
  }
@@ -383,7 +382,6 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
383
382
  filters?: Record<string, Filter>,
384
383
  filterOwner?: string
385
384
  ): FeatureData[] {
386
- assert(spatialFilter, 'spatialFilter required for tilesets');
387
385
  this._extractTileFeatures(spatialFilter);
388
386
  return applyFilters(
389
387
  this._features,
@@ -422,3 +420,9 @@ function normalizeColumns(columns: string | string[]): string[] {
422
420
  ? [columns]
423
421
  : [];
424
422
  }
423
+
424
+ function spatialFilterEquals(a?: SpatialFilter, b?: SpatialFilter) {
425
+ if (a === b) return true;
426
+ if (!a || !b) return false;
427
+ return booleanEqual(a, b);
428
+ }