@carto/api-client 0.5.0-alpha.7 → 0.5.0-alpha.8
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/build/api-client.cjs +66 -52
- package/build/api-client.d.cts +61 -54
- package/build/api-client.d.ts +61 -54
- package/build/api-client.js +2101 -113
- package/build/worker.js +2119 -4
- package/package.json +5 -1
- package/src/sources/h3-tileset-source.ts +1 -8
- package/src/sources/quadbin-tileset-source.ts +1 -8
- package/src/sources/vector-tileset-source.ts +1 -8
- package/src/widget-sources/index.ts +0 -1
- package/src/widget-sources/widget-source.ts +11 -0
- package/src/widget-sources/widget-tileset-source-impl.ts +417 -0
- package/src/widget-sources/widget-tileset-source.ts +192 -331
- package/src/workers/widget-tileset-worker.ts +6 -3
- package/build/chunk-LEI5PI5X.js +0 -2063
- package/src/widget-sources/widget-tileset-worker-source.ts +0 -240
- package/src/workers/utils.ts +0 -33
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/require-await */
|
|
2
|
-
import {TilesetSourceOptions} from '../sources/index.js';
|
|
3
1
|
import {
|
|
4
2
|
CategoryRequestOptions,
|
|
5
3
|
CategoryResponse,
|
|
@@ -17,33 +15,15 @@ import {
|
|
|
17
15
|
TimeSeriesRequestOptions,
|
|
18
16
|
TimeSeriesResponse,
|
|
19
17
|
} from './types.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {Filter, SpatialFilter, Tile} from '../types.js';
|
|
23
|
-
import {
|
|
24
|
-
TileFeatureExtractOptions,
|
|
25
|
-
applyFilters,
|
|
26
|
-
geojsonFeatures,
|
|
27
|
-
tileFeatures,
|
|
28
|
-
} from '../filters/index.js';
|
|
29
|
-
import {
|
|
30
|
-
aggregationFunctions,
|
|
31
|
-
applySorting,
|
|
32
|
-
groupValuesByColumn,
|
|
33
|
-
groupValuesByDateColumn,
|
|
34
|
-
histogram,
|
|
35
|
-
scatterPlot,
|
|
36
|
-
} from '../operations/index.js';
|
|
37
|
-
import {FeatureData} from '../types-internal.js';
|
|
18
|
+
import {SpatialFilter, Tile} from '../types.js';
|
|
19
|
+
import {TileFeatureExtractOptions} from '../filters/index.js';
|
|
38
20
|
import {FeatureCollection} from 'geojson';
|
|
39
|
-
import {SpatialDataType} from '../sources/types.js';
|
|
40
21
|
import {WidgetSource, WidgetSourceProps} from './widget-source.js';
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// https://github.com/CartoDB/carto-api-client/issues/39
|
|
22
|
+
import {Method} from '../workers/constants.js';
|
|
23
|
+
import {WorkerRequest, WorkerResponse} from '../workers/types.js';
|
|
24
|
+
import {SpatialDataType, TilesetSourceOptions} from '../sources/types.js';
|
|
25
|
+
import {TileFormat} from '../constants.js';
|
|
26
|
+
import {WidgetTilesetSourceImpl} from './widget-tileset-source-impl.js';
|
|
47
27
|
|
|
48
28
|
export type WidgetTilesetSourceProps = WidgetSourceProps &
|
|
49
29
|
Omit<TilesetSourceOptions, 'filters'> & {
|
|
@@ -76,11 +56,136 @@ export type WidgetTilesetSourceResult = {widgetSource: WidgetTilesetSource};
|
|
|
76
56
|
* ```
|
|
77
57
|
*/
|
|
78
58
|
export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps> {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
59
|
+
protected _localImpl: WidgetTilesetSourceImpl | null = null;
|
|
60
|
+
|
|
61
|
+
protected _workerImpl: Worker | null = null;
|
|
62
|
+
protected _workerEnabled: boolean;
|
|
63
|
+
protected _workerNextRequestId = 1;
|
|
64
|
+
|
|
65
|
+
constructor(props: WidgetTilesetSourceProps) {
|
|
66
|
+
super(props);
|
|
67
|
+
|
|
68
|
+
this._workerEnabled =
|
|
69
|
+
(props.widgetSourceWorker ?? true) && TSUP_FORMAT !== 'cjs';
|
|
70
|
+
this._localImpl = this._workerEnabled
|
|
71
|
+
? null
|
|
72
|
+
: new WidgetTilesetSourceImpl(this.props);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
destroy() {
|
|
76
|
+
this._localImpl?.destroy();
|
|
77
|
+
this._localImpl = null;
|
|
78
|
+
|
|
79
|
+
this._workerImpl?.terminate();
|
|
80
|
+
this._workerImpl = null;
|
|
81
|
+
|
|
82
|
+
super.destroy();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/////////////////////////////////////////////////////////////////////////////
|
|
86
|
+
// WEB WORKER MANAGEMENT
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Returns an initialized Worker, to be reused for the lifecycle of this
|
|
90
|
+
* source instance.
|
|
91
|
+
*/
|
|
92
|
+
protected _getWorker(): Worker | null {
|
|
93
|
+
if (this._workerImpl || this._localImpl) {
|
|
94
|
+
return this._workerImpl;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
this._workerImpl = new Worker(new URL('worker.js', import.meta.url), {
|
|
99
|
+
type: 'module',
|
|
100
|
+
name: 'cartowidgettileset',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
this._workerImpl.postMessage({
|
|
104
|
+
method: Method.INIT,
|
|
105
|
+
params: [this.props],
|
|
106
|
+
} as WorkerRequest);
|
|
107
|
+
|
|
108
|
+
return this._workerImpl;
|
|
109
|
+
} catch {
|
|
110
|
+
this._workerEnabled = false;
|
|
111
|
+
this._localImpl = new WidgetTilesetSourceImpl(this.props);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Executes a given method on the worker. */
|
|
117
|
+
protected _executeWorkerMethod<T>(
|
|
118
|
+
method: Method,
|
|
119
|
+
params: unknown[],
|
|
120
|
+
signal?: AbortSignal
|
|
121
|
+
): Promise<T> {
|
|
122
|
+
const worker = this._getWorker();
|
|
123
|
+
if (!worker) {
|
|
124
|
+
// @ts-expect-error No type-checking dynamic method name.
|
|
125
|
+
return this._localImpl[method](...params);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const requestId = this._workerNextRequestId++;
|
|
129
|
+
|
|
130
|
+
// TODO: ViewState may contain non-serializable data, which we do not need.
|
|
131
|
+
// Remove this sanitization after sc-469614 is fixed.
|
|
132
|
+
const options = params[0] as any;
|
|
133
|
+
if (options?.spatialIndexReferenceViewState) {
|
|
134
|
+
const {zoom, latitude, longitude} =
|
|
135
|
+
options.spatialIndexReferenceViewState;
|
|
136
|
+
options.spatialIndexReferenceViewState = {zoom, latitude, longitude};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let resolve: ((value: T) => void) | null = null;
|
|
140
|
+
let reject: ((reason: any) => void) | null = null;
|
|
141
|
+
|
|
142
|
+
// If worker sends message to main process, check whether it's a response
|
|
143
|
+
// to this request, and whether the request can been aborted. Then resolve
|
|
144
|
+
// or reject the Promise.
|
|
145
|
+
function onMessage(e: MessageEvent) {
|
|
146
|
+
const response = e.data as WorkerResponse;
|
|
147
|
+
if (response.requestId !== requestId) return;
|
|
148
|
+
|
|
149
|
+
if (signal?.aborted) {
|
|
150
|
+
reject!(new Error(signal.reason));
|
|
151
|
+
} else if (response.ok) {
|
|
152
|
+
resolve!(response.result as T);
|
|
153
|
+
} else {
|
|
154
|
+
reject!(new Error(response.error));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// If request is aborted by user, immediately reject the Promise.
|
|
159
|
+
function onAbort() {
|
|
160
|
+
reject!(new Error(signal!.reason));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
worker.addEventListener('message', onMessage);
|
|
164
|
+
signal?.addEventListener('abort', onAbort);
|
|
165
|
+
|
|
166
|
+
// Send the task to the worker, creating a Promise to resolve/reject later.
|
|
167
|
+
const promise = new Promise<T>((_resolve, _reject) => {
|
|
168
|
+
resolve = _resolve;
|
|
169
|
+
reject = _reject;
|
|
170
|
+
|
|
171
|
+
worker.postMessage({
|
|
172
|
+
requestId,
|
|
173
|
+
method,
|
|
174
|
+
params,
|
|
175
|
+
} as WorkerRequest);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Whether the task completes, fails, or aborts: clean up afterward.
|
|
179
|
+
void promise.finally(() => {
|
|
180
|
+
worker.removeEventListener('message', onMessage);
|
|
181
|
+
signal?.removeEventListener('abort', onAbort);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return promise;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/////////////////////////////////////////////////////////////////////////////
|
|
188
|
+
// DATA LOADING
|
|
84
189
|
|
|
85
190
|
/**
|
|
86
191
|
* Loads features as a list of tiles (typically provided by deck.gl).
|
|
@@ -88,39 +193,34 @@ export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps>
|
|
|
88
193
|
* before computing statistics on the tiles.
|
|
89
194
|
*/
|
|
90
195
|
loadTiles(tiles: unknown[]) {
|
|
91
|
-
|
|
92
|
-
|
|
196
|
+
const worker = this._getWorker();
|
|
197
|
+
if (!worker) {
|
|
198
|
+
return this._localImpl!.loadTiles(tiles);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
tiles = (tiles as Tile[]).map(({id, bbox, data}) => ({
|
|
202
|
+
id,
|
|
203
|
+
bbox,
|
|
204
|
+
data,
|
|
205
|
+
}));
|
|
206
|
+
|
|
207
|
+
worker.postMessage({
|
|
208
|
+
method: Method.LOAD_TILES,
|
|
209
|
+
params: [tiles],
|
|
210
|
+
} as WorkerRequest);
|
|
93
211
|
}
|
|
94
212
|
|
|
95
213
|
/** Configures options used to extract features from tiles. */
|
|
96
214
|
setTileFeatureExtractOptions(options: TileFeatureExtractOptions) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
protected _extractTileFeatures(spatialFilter: SpatialFilter) {
|
|
102
|
-
// When spatial filter has not changed, don't redo extraction. If tiles or
|
|
103
|
-
// tile extract options change, features will have been cleared already.
|
|
104
|
-
const prevInputs = this._tileFeatureExtractPreviousInputs;
|
|
105
|
-
if (
|
|
106
|
-
this._features.length &&
|
|
107
|
-
prevInputs.spatialFilter &&
|
|
108
|
-
booleanEqual(prevInputs.spatialFilter, spatialFilter)
|
|
109
|
-
) {
|
|
110
|
-
return;
|
|
215
|
+
const worker = this._getWorker();
|
|
216
|
+
if (!worker) {
|
|
217
|
+
return this._localImpl?.setTileFeatureExtractOptions(options);
|
|
111
218
|
}
|
|
112
219
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
...this._tileFeatureExtractOptions,
|
|
117
|
-
|
|
118
|
-
spatialFilter,
|
|
119
|
-
spatialDataColumn: this.props.spatialDataColumn,
|
|
120
|
-
spatialDataType: this.props.spatialDataType,
|
|
220
|
+
worker.postMessage({
|
|
221
|
+
type: Method.SET_TILE_FEATURE_EXTRACT_OPTIONS,
|
|
222
|
+
params: [options],
|
|
121
223
|
});
|
|
122
|
-
|
|
123
|
-
prevInputs.spatialFilter = spatialFilter;
|
|
124
224
|
}
|
|
125
225
|
|
|
126
226
|
/**
|
|
@@ -135,310 +235,71 @@ export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps>
|
|
|
135
235
|
geojson: FeatureCollection;
|
|
136
236
|
spatialFilter: SpatialFilter;
|
|
137
237
|
}) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
spatialFilter
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
238
|
+
const worker = this._getWorker();
|
|
239
|
+
if (!worker) {
|
|
240
|
+
return this._localImpl!.loadGeoJSON({geojson, spatialFilter});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
worker.postMessage({
|
|
244
|
+
method: Method.LOAD_GEOJSON,
|
|
245
|
+
params: [{geojson, spatialFilter}],
|
|
246
|
+
} as WorkerRequest);
|
|
144
247
|
}
|
|
145
248
|
|
|
249
|
+
/////////////////////////////////////////////////////////////////////////////
|
|
250
|
+
// WIDGETS API
|
|
251
|
+
|
|
252
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
146
253
|
override async getFeatures(): Promise<FeaturesResponse> {
|
|
147
254
|
throw new Error('getFeatures not supported for tilesets');
|
|
148
255
|
}
|
|
149
256
|
|
|
150
257
|
async getFormula({
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
joinOperation,
|
|
154
|
-
filters,
|
|
155
|
-
filterOwner,
|
|
156
|
-
spatialFilter,
|
|
258
|
+
signal,
|
|
259
|
+
...options
|
|
157
260
|
}: FormulaRequestOptions): Promise<FormulaResponse> {
|
|
158
|
-
|
|
159
|
-
throw new Error('Custom aggregation not supported for tilesets');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Column is required except when operation is 'count'.
|
|
163
|
-
if ((column && column !== '*') || operation !== 'count') {
|
|
164
|
-
assertColumn(this._features, column);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const filteredFeatures = this._getFilteredFeatures(
|
|
168
|
-
spatialFilter,
|
|
169
|
-
filters,
|
|
170
|
-
filterOwner
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
if (filteredFeatures.length === 0 && operation !== 'count') {
|
|
174
|
-
return {value: null};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const targetOperation = aggregationFunctions[operation];
|
|
178
|
-
return {
|
|
179
|
-
value: targetOperation(filteredFeatures, column, joinOperation),
|
|
180
|
-
};
|
|
261
|
+
return this._executeWorkerMethod(Method.GET_FORMULA, [options], signal);
|
|
181
262
|
}
|
|
182
263
|
|
|
183
264
|
override async getHistogram({
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
column,
|
|
187
|
-
joinOperation,
|
|
188
|
-
filters,
|
|
189
|
-
filterOwner,
|
|
190
|
-
spatialFilter,
|
|
265
|
+
signal,
|
|
266
|
+
...options
|
|
191
267
|
}: HistogramRequestOptions): Promise<HistogramResponse> {
|
|
192
|
-
|
|
193
|
-
spatialFilter,
|
|
194
|
-
filters,
|
|
195
|
-
filterOwner
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
if (!this._features.length) {
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
assertColumn(this._features, column);
|
|
203
|
-
|
|
204
|
-
return histogram({
|
|
205
|
-
data: filteredFeatures,
|
|
206
|
-
valuesColumns: normalizeColumns(column),
|
|
207
|
-
joinOperation,
|
|
208
|
-
ticks,
|
|
209
|
-
operation,
|
|
210
|
-
});
|
|
268
|
+
return this._executeWorkerMethod(Method.GET_HISTOGRAM, [options], signal);
|
|
211
269
|
}
|
|
212
270
|
|
|
213
271
|
override async getCategories({
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
operationColumn,
|
|
217
|
-
joinOperation,
|
|
218
|
-
filters,
|
|
219
|
-
filterOwner,
|
|
220
|
-
spatialFilter,
|
|
272
|
+
signal,
|
|
273
|
+
...options
|
|
221
274
|
}: CategoryRequestOptions): Promise<CategoryResponse> {
|
|
222
|
-
|
|
223
|
-
spatialFilter,
|
|
224
|
-
filters,
|
|
225
|
-
filterOwner
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
if (!filteredFeatures.length) {
|
|
229
|
-
return [];
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
assertColumn(this._features, column, operationColumn as string);
|
|
233
|
-
|
|
234
|
-
const groups = groupValuesByColumn({
|
|
235
|
-
data: filteredFeatures,
|
|
236
|
-
valuesColumns: normalizeColumns(operationColumn || column),
|
|
237
|
-
joinOperation,
|
|
238
|
-
keysColumn: column,
|
|
239
|
-
operation,
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
return groups || [];
|
|
275
|
+
return this._executeWorkerMethod(Method.GET_CATEGORIES, [options], signal);
|
|
243
276
|
}
|
|
244
277
|
|
|
245
278
|
override async getScatter({
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
xAxisJoinOperation,
|
|
249
|
-
yAxisJoinOperation,
|
|
250
|
-
filters,
|
|
251
|
-
filterOwner,
|
|
252
|
-
spatialFilter,
|
|
279
|
+
signal,
|
|
280
|
+
...options
|
|
253
281
|
}: ScatterRequestOptions): Promise<ScatterResponse> {
|
|
254
|
-
|
|
255
|
-
spatialFilter,
|
|
256
|
-
filters,
|
|
257
|
-
filterOwner
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
if (!filteredFeatures.length) {
|
|
261
|
-
return [];
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
assertColumn(this._features, xAxisColumn, yAxisColumn);
|
|
265
|
-
|
|
266
|
-
return scatterPlot({
|
|
267
|
-
data: filteredFeatures,
|
|
268
|
-
xAxisColumns: normalizeColumns(xAxisColumn),
|
|
269
|
-
xAxisJoinOperation,
|
|
270
|
-
yAxisColumns: normalizeColumns(yAxisColumn),
|
|
271
|
-
yAxisJoinOperation,
|
|
272
|
-
});
|
|
282
|
+
return this._executeWorkerMethod(Method.GET_SCATTER, [options], signal);
|
|
273
283
|
}
|
|
274
284
|
|
|
275
285
|
override async getTable({
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
searchFilterText,
|
|
279
|
-
sortBy,
|
|
280
|
-
sortDirection,
|
|
281
|
-
sortByColumnType,
|
|
282
|
-
offset = 0,
|
|
283
|
-
limit = 10,
|
|
284
|
-
filters,
|
|
285
|
-
filterOwner,
|
|
286
|
-
spatialFilter,
|
|
286
|
+
signal,
|
|
287
|
+
...options
|
|
287
288
|
}: TableRequestOptions): Promise<TableResponse> {
|
|
288
|
-
|
|
289
|
-
let filteredFeatures = this._getFilteredFeatures(
|
|
290
|
-
spatialFilter,
|
|
291
|
-
filters,
|
|
292
|
-
filterOwner
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
if (!filteredFeatures.length) {
|
|
296
|
-
return {rows: [], totalCount: 0};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Search.
|
|
300
|
-
if (searchFilterColumn && searchFilterText) {
|
|
301
|
-
filteredFeatures = filteredFeatures.filter(
|
|
302
|
-
(row) =>
|
|
303
|
-
row[searchFilterColumn] &&
|
|
304
|
-
String(row[searchFilterColumn] as unknown)
|
|
305
|
-
.toLowerCase()
|
|
306
|
-
.includes(String(searchFilterText).toLowerCase())
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Sort.
|
|
311
|
-
let rows = applySorting(filteredFeatures, {
|
|
312
|
-
sortBy,
|
|
313
|
-
sortByDirection: sortDirection,
|
|
314
|
-
sortByColumnType,
|
|
315
|
-
});
|
|
316
|
-
const totalCount = rows.length;
|
|
317
|
-
|
|
318
|
-
// Offset and limit.
|
|
319
|
-
rows = rows.slice(
|
|
320
|
-
Math.min(offset, totalCount),
|
|
321
|
-
Math.min(offset + limit, totalCount)
|
|
322
|
-
);
|
|
323
|
-
|
|
324
|
-
// Select columns.
|
|
325
|
-
rows = rows.map((srcRow: FeatureData) => {
|
|
326
|
-
const dstRow: FeatureData = {};
|
|
327
|
-
for (const column of columns) {
|
|
328
|
-
dstRow[column] = srcRow[column];
|
|
329
|
-
}
|
|
330
|
-
return dstRow;
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
return {rows, totalCount} as TableResponse;
|
|
289
|
+
return this._executeWorkerMethod(Method.GET_TABLE, [options], signal);
|
|
334
290
|
}
|
|
335
291
|
|
|
336
292
|
override async getTimeSeries({
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
operation,
|
|
340
|
-
operationColumn,
|
|
341
|
-
joinOperation,
|
|
342
|
-
filters,
|
|
343
|
-
filterOwner,
|
|
344
|
-
spatialFilter,
|
|
293
|
+
signal,
|
|
294
|
+
...options
|
|
345
295
|
}: TimeSeriesRequestOptions): Promise<TimeSeriesResponse> {
|
|
346
|
-
|
|
347
|
-
spatialFilter,
|
|
348
|
-
filters,
|
|
349
|
-
filterOwner
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
if (!filteredFeatures.length) {
|
|
353
|
-
return {rows: []};
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
assertColumn(this._features, column, operationColumn as string);
|
|
357
|
-
|
|
358
|
-
const rows =
|
|
359
|
-
groupValuesByDateColumn({
|
|
360
|
-
data: filteredFeatures,
|
|
361
|
-
valuesColumns: normalizeColumns(operationColumn || column),
|
|
362
|
-
keysColumn: column,
|
|
363
|
-
groupType: stepSize,
|
|
364
|
-
operation,
|
|
365
|
-
joinOperation,
|
|
366
|
-
}) || [];
|
|
367
|
-
|
|
368
|
-
return {rows};
|
|
296
|
+
return this._executeWorkerMethod(Method.GET_TIME_SERIES, [options], signal);
|
|
369
297
|
}
|
|
370
298
|
|
|
371
299
|
override async getRange({
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
filterOwner,
|
|
375
|
-
spatialFilter,
|
|
300
|
+
signal,
|
|
301
|
+
...options
|
|
376
302
|
}: RangeRequestOptions): Promise<RangeResponse> {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const filteredFeatures = this._getFilteredFeatures(
|
|
380
|
-
spatialFilter,
|
|
381
|
-
filters,
|
|
382
|
-
filterOwner
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
if (!this._features.length) {
|
|
386
|
-
// TODO: Is this the only nullable response in the Widgets API? If so,
|
|
387
|
-
// can we do something more consistent?
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
min: aggregationFunctions.min(filteredFeatures, column),
|
|
393
|
-
max: aggregationFunctions.max(filteredFeatures, column),
|
|
394
|
-
};
|
|
303
|
+
return this._executeWorkerMethod(Method.GET_RANGE, [options], signal);
|
|
395
304
|
}
|
|
396
|
-
|
|
397
|
-
/****************************************************************************
|
|
398
|
-
* INTERNAL
|
|
399
|
-
*/
|
|
400
|
-
|
|
401
|
-
private _getFilteredFeatures(
|
|
402
|
-
spatialFilter?: SpatialFilter,
|
|
403
|
-
filters?: Record<string, Filter>,
|
|
404
|
-
filterOwner?: string
|
|
405
|
-
): FeatureData[] {
|
|
406
|
-
assert(spatialFilter, 'spatialFilter required for tilesets');
|
|
407
|
-
this._extractTileFeatures(spatialFilter);
|
|
408
|
-
return applyFilters(
|
|
409
|
-
this._features,
|
|
410
|
-
getApplicableFilters(filterOwner, filters || this.props.filters),
|
|
411
|
-
this.props.filtersLogicalOperator || 'and'
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function assertColumn(
|
|
417
|
-
features: FeatureData[],
|
|
418
|
-
...columnArgs: string[] | string[][]
|
|
419
|
-
) {
|
|
420
|
-
// TODO(cleanup): Can drop support for multiple column shapes here?
|
|
421
|
-
|
|
422
|
-
// Due to the multiple column shape, we normalise it as an array with normalizeColumns
|
|
423
|
-
const columns = Array.from(new Set(columnArgs.map(normalizeColumns).flat()));
|
|
424
|
-
|
|
425
|
-
const featureKeys = Object.keys(features[0]);
|
|
426
|
-
|
|
427
|
-
const invalidColumns = columns.filter(
|
|
428
|
-
(column) => !featureKeys.includes(column)
|
|
429
|
-
);
|
|
430
|
-
|
|
431
|
-
if (invalidColumns.length) {
|
|
432
|
-
throw new InvalidColumnError(
|
|
433
|
-
`Missing column(s): ${invalidColumns.join(', ')}`
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function normalizeColumns(columns: string | string[]): string[] {
|
|
439
|
-
return Array.isArray(columns)
|
|
440
|
-
? columns
|
|
441
|
-
: typeof columns === 'string'
|
|
442
|
-
? [columns]
|
|
443
|
-
: [];
|
|
444
305
|
}
|
|
@@ -8,8 +8,8 @@ import type {WorkerRequest, WorkerResponse} from './types.js';
|
|
|
8
8
|
/*
|
|
9
9
|
* Web Worker, compiled as a separate `@carto/api-client/worker` entrypoint.
|
|
10
10
|
*
|
|
11
|
-
* Workers are scoped to the lifecycle of a single
|
|
12
|
-
*
|
|
11
|
+
* Workers are scoped to the lifecycle of a single WidgetTilesetSource instance,
|
|
12
|
+
* representing and executing calculations on a single datasource.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
let source: WidgetTilesetSource;
|
|
@@ -18,7 +18,10 @@ addEventListener('message', (e) => {
|
|
|
18
18
|
const {method, params, requestId} = e.data as WorkerRequest;
|
|
19
19
|
|
|
20
20
|
if (method === Method.INIT) {
|
|
21
|
-
source = new WidgetTilesetSource(
|
|
21
|
+
source = new WidgetTilesetSource({
|
|
22
|
+
...(params[0] as WidgetTilesetSourceProps),
|
|
23
|
+
widgetSourceWorker: false,
|
|
24
|
+
});
|
|
22
25
|
return;
|
|
23
26
|
}
|
|
24
27
|
|