@carto/api-client 0.5.6-alpha.bundle.4 → 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.
- package/CHANGELOG.md +7 -0
- package/build/api-client.cjs +488 -361
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.d.cts +35 -6
- package/build/api-client.d.ts +35 -6
- package/build/api-client.js +451 -325
- package/build/api-client.js.map +1 -1
- package/build/worker-compat.js +4227 -2968
- package/build/worker-compat.js.map +1 -1
- package/build/worker.js +408 -340
- package/build/worker.js.map +1 -1
- package/package.json +2 -2
- package/src/constants.ts +12 -0
- package/src/fetch-map/layer-map.ts +2 -1
- package/src/filters/tileFeatures.ts +1 -1
- package/src/filters/tileFeaturesGeometries.ts +28 -55
- package/src/filters/tileFeaturesRaster.ts +16 -12
- package/src/filters/tileFeaturesSpatialIndex.ts +37 -52
- package/src/filters/tileIntersection.ts +158 -0
- package/src/index.ts +1 -0
- package/src/types.ts +1 -1
- package/src/utils/CellSet.ts +90 -0
- package/src/widget-sources/widget-remote-source.ts +5 -5
- package/src/widget-sources/widget-tileset-source-impl.ts +15 -10
|
@@ -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
|
+
}
|
|
@@ -21,7 +21,7 @@ import {assert, normalizeObjectKeys} from '../utils.js';
|
|
|
21
21
|
import {DEFAULT_TILE_RESOLUTION} from '../constants-internal.js';
|
|
22
22
|
import {WidgetSource, type WidgetSourceProps} from './widget-source.js';
|
|
23
23
|
import type {Filters} from '../types.js';
|
|
24
|
-
import {ApiVersion} from '../constants.js';
|
|
24
|
+
import {AggregationTypes, ApiVersion} from '../constants.js';
|
|
25
25
|
import {getApplicableFilters} from '../filters.js';
|
|
26
26
|
|
|
27
27
|
export type WidgetRemoteSourceProps = WidgetSourceProps;
|
|
@@ -76,7 +76,7 @@ export abstract class WidgetRemoteSource<
|
|
|
76
76
|
} = options;
|
|
77
77
|
const {column, operation, operationColumn, operationExp} = params;
|
|
78
78
|
|
|
79
|
-
if (operation ===
|
|
79
|
+
if (operation === AggregationTypes.Custom) {
|
|
80
80
|
assert(operationExp, 'operationExp is required for custom operation');
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -148,7 +148,7 @@ export abstract class WidgetRemoteSource<
|
|
|
148
148
|
|
|
149
149
|
type FormulaModelResponse = {rows: {value: number}[]};
|
|
150
150
|
|
|
151
|
-
if (operation ===
|
|
151
|
+
if (operation === AggregationTypes.Custom) {
|
|
152
152
|
assert(operationExp, 'operationExp is required for custom operation');
|
|
153
153
|
}
|
|
154
154
|
|
|
@@ -161,7 +161,7 @@ export abstract class WidgetRemoteSource<
|
|
|
161
161
|
},
|
|
162
162
|
params: {
|
|
163
163
|
column: column ?? '*',
|
|
164
|
-
operation: operation ??
|
|
164
|
+
operation: operation ?? AggregationTypes.Count,
|
|
165
165
|
operationExp,
|
|
166
166
|
},
|
|
167
167
|
opts: {signal, headers: this.props.headers},
|
|
@@ -331,7 +331,7 @@ export abstract class WidgetRemoteSource<
|
|
|
331
331
|
splitByCategoryValues,
|
|
332
332
|
} = params;
|
|
333
333
|
|
|
334
|
-
if (operation ===
|
|
334
|
+
if (operation === AggregationTypes.Custom) {
|
|
335
335
|
assert(operationExp, 'operationExp is required for custom operation');
|
|
336
336
|
}
|
|
337
337
|
|
|
@@ -39,6 +39,7 @@ import {WidgetSource} from './widget-source.js';
|
|
|
39
39
|
import {booleanEqual} from '@turf/boolean-equal';
|
|
40
40
|
import type {WidgetTilesetSourceProps} from './widget-tileset-source.js';
|
|
41
41
|
import {getApplicableFilters} from '../filters.js';
|
|
42
|
+
import {AggregationTypes} from '../constants.js';
|
|
42
43
|
|
|
43
44
|
// TODO(cleanup): Parameter defaults in source functions and widget API calls are
|
|
44
45
|
// currently duplicated and possibly inconsistent. Consider consolidating and
|
|
@@ -73,14 +74,13 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
|
|
|
73
74
|
this._features.length = 0;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
protected _extractTileFeatures(spatialFilter
|
|
77
|
+
protected _extractTileFeatures(spatialFilter?: SpatialFilter) {
|
|
77
78
|
// When spatial filter has not changed, don't redo extraction. If tiles or
|
|
78
79
|
// tile extract options change, features will have been cleared already.
|
|
79
80
|
const prevInputs = this._tileFeatureExtractPreviousInputs;
|
|
80
81
|
if (
|
|
81
82
|
this._features.length &&
|
|
82
|
-
prevInputs.spatialFilter
|
|
83
|
-
booleanEqual(prevInputs.spatialFilter, spatialFilter)
|
|
83
|
+
spatialFilterEquals(prevInputs.spatialFilter, spatialFilter)
|
|
84
84
|
) {
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
@@ -120,7 +120,7 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
|
|
|
120
120
|
|
|
121
121
|
async getFormula({
|
|
122
122
|
column = '*',
|
|
123
|
-
operation =
|
|
123
|
+
operation = AggregationTypes.Count,
|
|
124
124
|
joinOperation,
|
|
125
125
|
filters,
|
|
126
126
|
filterOwner,
|
|
@@ -132,16 +132,16 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
|
|
|
132
132
|
filterOwner
|
|
133
133
|
);
|
|
134
134
|
|
|
135
|
-
if (filteredFeatures.length === 0 && operation !==
|
|
135
|
+
if (filteredFeatures.length === 0 && operation !== AggregationTypes.Count) {
|
|
136
136
|
return {value: null};
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
if (operation ===
|
|
139
|
+
if (operation === AggregationTypes.Custom) {
|
|
140
140
|
throw new Error('Custom aggregation not supported for tilesets');
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// Column is required except when operation is 'count'.
|
|
144
|
-
if ((column && column !== '*') || operation !==
|
|
144
|
+
if ((column && column !== '*') || operation !== AggregationTypes.Count) {
|
|
145
145
|
assertColumn(this._features, column);
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -152,7 +152,7 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
override async getHistogram({
|
|
155
|
-
operation =
|
|
155
|
+
operation = AggregationTypes.Count,
|
|
156
156
|
ticks,
|
|
157
157
|
column,
|
|
158
158
|
joinOperation,
|
|
@@ -183,7 +183,7 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
|
|
|
183
183
|
|
|
184
184
|
override async getCategories({
|
|
185
185
|
column,
|
|
186
|
-
operation =
|
|
186
|
+
operation = AggregationTypes.Count,
|
|
187
187
|
operationColumn,
|
|
188
188
|
joinOperation,
|
|
189
189
|
filters,
|
|
@@ -382,7 +382,6 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
|
|
|
382
382
|
filters?: Record<string, Filter>,
|
|
383
383
|
filterOwner?: string
|
|
384
384
|
): FeatureData[] {
|
|
385
|
-
assert(spatialFilter, 'spatialFilter required for tilesets');
|
|
386
385
|
this._extractTileFeatures(spatialFilter);
|
|
387
386
|
return applyFilters(
|
|
388
387
|
this._features,
|
|
@@ -421,3 +420,9 @@ function normalizeColumns(columns: string | string[]): string[] {
|
|
|
421
420
|
? [columns]
|
|
422
421
|
: [];
|
|
423
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
|
+
}
|