@carto/api-client 0.5.0-alpha.7 → 0.5.0-alpha.9
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 +58 -45
- package/build/api-client.d.cts +61 -54
- package/build/api-client.d.ts +61 -54
- package/build/api-client.js +2091 -104
- package/build/worker.js +1929 -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 +194 -330
- package/src/workers/widget-tileset-worker.ts +9 -8
- 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) &&
|
|
70
|
+
TSUP_FORMAT !== 'cjs' &&
|
|
71
|
+
typeof Worker !== 'undefined';
|
|
72
|
+
|
|
73
|
+
if (!this._workerEnabled) {
|
|
74
|
+
this._localImpl = new WidgetTilesetSourceImpl(this.props);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
destroy() {
|
|
79
|
+
this._localImpl?.destroy();
|
|
80
|
+
this._localImpl = null;
|
|
81
|
+
|
|
82
|
+
this._workerImpl?.terminate();
|
|
83
|
+
this._workerImpl = null;
|
|
84
|
+
|
|
85
|
+
super.destroy();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/////////////////////////////////////////////////////////////////////////////
|
|
89
|
+
// WEB WORKER MANAGEMENT
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns an initialized Worker, to be reused for the lifecycle of this
|
|
93
|
+
* source instance.
|
|
94
|
+
*/
|
|
95
|
+
protected _getWorker(): Worker {
|
|
96
|
+
if (this._workerImpl) {
|
|
97
|
+
return this._workerImpl;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this._workerImpl = new Worker(
|
|
101
|
+
new URL('@carto/api-client/worker', import.meta.url),
|
|
102
|
+
{
|
|
103
|
+
type: 'module',
|
|
104
|
+
name: 'cartowidgettileset',
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
this._workerImpl.postMessage({
|
|
109
|
+
method: Method.INIT,
|
|
110
|
+
params: [this.props],
|
|
111
|
+
} as WorkerRequest);
|
|
112
|
+
|
|
113
|
+
return this._workerImpl;
|
|
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
|
+
if (!this._workerEnabled) {
|
|
123
|
+
// @ts-expect-error No type-checking dynamic method name.
|
|
124
|
+
return this._localImpl[method](...params);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const worker = this._getWorker();
|
|
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,36 @@ export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps>
|
|
|
88
193
|
* before computing statistics on the tiles.
|
|
89
194
|
*/
|
|
90
195
|
loadTiles(tiles: unknown[]) {
|
|
91
|
-
this.
|
|
92
|
-
|
|
196
|
+
if (!this._workerEnabled) {
|
|
197
|
+
return this._localImpl!.loadTiles(tiles);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const worker = this._getWorker();
|
|
201
|
+
|
|
202
|
+
tiles = (tiles as Tile[]).map(({id, bbox, data}) => ({
|
|
203
|
+
id,
|
|
204
|
+
bbox,
|
|
205
|
+
data,
|
|
206
|
+
}));
|
|
207
|
+
|
|
208
|
+
worker.postMessage({
|
|
209
|
+
method: Method.LOAD_TILES,
|
|
210
|
+
params: [tiles],
|
|
211
|
+
} as WorkerRequest);
|
|
93
212
|
}
|
|
94
213
|
|
|
95
214
|
/** Configures options used to extract features from tiles. */
|
|
96
215
|
setTileFeatureExtractOptions(options: TileFeatureExtractOptions) {
|
|
97
|
-
this.
|
|
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;
|
|
216
|
+
if (!this._workerEnabled) {
|
|
217
|
+
return this._localImpl?.setTileFeatureExtractOptions(options);
|
|
111
218
|
}
|
|
112
219
|
|
|
113
|
-
|
|
114
|
-
tiles: this._tiles,
|
|
115
|
-
tileFormat: this.props.tileFormat,
|
|
116
|
-
...this._tileFeatureExtractOptions,
|
|
220
|
+
const worker = this._getWorker();
|
|
117
221
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
222
|
+
worker.postMessage({
|
|
223
|
+
type: Method.SET_TILE_FEATURE_EXTRACT_OPTIONS,
|
|
224
|
+
params: [options],
|
|
121
225
|
});
|
|
122
|
-
|
|
123
|
-
prevInputs.spatialFilter = spatialFilter;
|
|
124
226
|
}
|
|
125
227
|
|
|
126
228
|
/**
|
|
@@ -135,310 +237,72 @@ export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps>
|
|
|
135
237
|
geojson: FeatureCollection;
|
|
136
238
|
spatialFilter: SpatialFilter;
|
|
137
239
|
}) {
|
|
138
|
-
this.
|
|
139
|
-
geojson,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
240
|
+
if (!this._workerEnabled) {
|
|
241
|
+
return this._localImpl!.loadGeoJSON({geojson, spatialFilter});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const worker = this._getWorker();
|
|
245
|
+
|
|
246
|
+
worker.postMessage({
|
|
247
|
+
method: Method.LOAD_GEOJSON,
|
|
248
|
+
params: [{geojson, spatialFilter}],
|
|
249
|
+
} as WorkerRequest);
|
|
144
250
|
}
|
|
145
251
|
|
|
252
|
+
/////////////////////////////////////////////////////////////////////////////
|
|
253
|
+
// WIDGETS API
|
|
254
|
+
|
|
255
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
146
256
|
override async getFeatures(): Promise<FeaturesResponse> {
|
|
147
257
|
throw new Error('getFeatures not supported for tilesets');
|
|
148
258
|
}
|
|
149
259
|
|
|
150
260
|
async getFormula({
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
joinOperation,
|
|
154
|
-
filters,
|
|
155
|
-
filterOwner,
|
|
156
|
-
spatialFilter,
|
|
261
|
+
signal,
|
|
262
|
+
...options
|
|
157
263
|
}: 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
|
-
};
|
|
264
|
+
return this._executeWorkerMethod(Method.GET_FORMULA, [options], signal);
|
|
181
265
|
}
|
|
182
266
|
|
|
183
267
|
override async getHistogram({
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
column,
|
|
187
|
-
joinOperation,
|
|
188
|
-
filters,
|
|
189
|
-
filterOwner,
|
|
190
|
-
spatialFilter,
|
|
268
|
+
signal,
|
|
269
|
+
...options
|
|
191
270
|
}: 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
|
-
});
|
|
271
|
+
return this._executeWorkerMethod(Method.GET_HISTOGRAM, [options], signal);
|
|
211
272
|
}
|
|
212
273
|
|
|
213
274
|
override async getCategories({
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
operationColumn,
|
|
217
|
-
joinOperation,
|
|
218
|
-
filters,
|
|
219
|
-
filterOwner,
|
|
220
|
-
spatialFilter,
|
|
275
|
+
signal,
|
|
276
|
+
...options
|
|
221
277
|
}: 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 || [];
|
|
278
|
+
return this._executeWorkerMethod(Method.GET_CATEGORIES, [options], signal);
|
|
243
279
|
}
|
|
244
280
|
|
|
245
281
|
override async getScatter({
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
xAxisJoinOperation,
|
|
249
|
-
yAxisJoinOperation,
|
|
250
|
-
filters,
|
|
251
|
-
filterOwner,
|
|
252
|
-
spatialFilter,
|
|
282
|
+
signal,
|
|
283
|
+
...options
|
|
253
284
|
}: 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
|
-
});
|
|
285
|
+
return this._executeWorkerMethod(Method.GET_SCATTER, [options], signal);
|
|
273
286
|
}
|
|
274
287
|
|
|
275
288
|
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,
|
|
289
|
+
signal,
|
|
290
|
+
...options
|
|
287
291
|
}: 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;
|
|
292
|
+
return this._executeWorkerMethod(Method.GET_TABLE, [options], signal);
|
|
334
293
|
}
|
|
335
294
|
|
|
336
295
|
override async getTimeSeries({
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
operation,
|
|
340
|
-
operationColumn,
|
|
341
|
-
joinOperation,
|
|
342
|
-
filters,
|
|
343
|
-
filterOwner,
|
|
344
|
-
spatialFilter,
|
|
296
|
+
signal,
|
|
297
|
+
...options
|
|
345
298
|
}: 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};
|
|
299
|
+
return this._executeWorkerMethod(Method.GET_TIME_SERIES, [options], signal);
|
|
369
300
|
}
|
|
370
301
|
|
|
371
302
|
override async getRange({
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
filterOwner,
|
|
375
|
-
spatialFilter,
|
|
303
|
+
signal,
|
|
304
|
+
...options
|
|
376
305
|
}: 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
|
-
};
|
|
395
|
-
}
|
|
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
|
-
);
|
|
306
|
+
return this._executeWorkerMethod(Method.GET_RANGE, [options], signal);
|
|
435
307
|
}
|
|
436
308
|
}
|
|
437
|
-
|
|
438
|
-
function normalizeColumns(columns: string | string[]): string[] {
|
|
439
|
-
return Array.isArray(columns)
|
|
440
|
-
? columns
|
|
441
|
-
: typeof columns === 'string'
|
|
442
|
-
? [columns]
|
|
443
|
-
: [];
|
|
444
|
-
}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type WidgetTilesetSourceProps,
|
|
4
|
-
} from '../widget-sources/widget-tileset-source.js';
|
|
1
|
+
import {WidgetTilesetSourceImpl} from '../widget-sources/widget-tileset-source-impl.js';
|
|
2
|
+
import {type WidgetTilesetSourceProps} from '../widget-sources/widget-tileset-source.js';
|
|
5
3
|
import {Method} from './constants.js';
|
|
6
4
|
import type {WorkerRequest, WorkerResponse} from './types.js';
|
|
7
5
|
|
|
8
6
|
/*
|
|
9
7
|
* Web Worker, compiled as a separate `@carto/api-client/worker` entrypoint.
|
|
10
8
|
*
|
|
11
|
-
* Workers are scoped to the lifecycle of a single
|
|
12
|
-
*
|
|
9
|
+
* Workers are scoped to the lifecycle of a single WidgetTilesetSource instance,
|
|
10
|
+
* representing and executing calculations on a single datasource.
|
|
13
11
|
*/
|
|
14
12
|
|
|
15
|
-
let source:
|
|
13
|
+
let source: WidgetTilesetSourceImpl;
|
|
16
14
|
|
|
17
15
|
addEventListener('message', (e) => {
|
|
18
16
|
const {method, params, requestId} = e.data as WorkerRequest;
|
|
19
17
|
|
|
20
18
|
if (method === Method.INIT) {
|
|
21
|
-
source = new
|
|
19
|
+
source = new WidgetTilesetSourceImpl({
|
|
20
|
+
...(params[0] as WidgetTilesetSourceProps),
|
|
21
|
+
widgetSourceWorker: false,
|
|
22
|
+
});
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
25
|
|