@carto/api-client 0.5.0-alpha.3 → 0.5.0-alpha.5

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 (131) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/build/api-client.cjs +3332 -3100
  3. package/build/api-client.d.cts +1373 -0
  4. package/build/api-client.d.ts +1373 -0
  5. package/build/api-client.js +1712 -0
  6. package/build/chunk-V3E7BKVR.js +2063 -0
  7. package/build/worker.d.ts +2 -0
  8. package/build/worker.js +25 -0
  9. package/package.json +51 -45
  10. package/src/api/carto-api-error.ts +1 -1
  11. package/src/api/query.ts +5 -5
  12. package/src/api/request-with-parameters.ts +6 -6
  13. package/src/client.ts +3 -3
  14. package/src/constants-internal.ts +5 -11
  15. package/src/constants.ts +6 -6
  16. package/src/deck/get-data-filter-extension-props.ts +146 -0
  17. package/src/deck/index.ts +1 -0
  18. package/src/filters/Filter.ts +18 -8
  19. package/src/filters/FilterTypes.ts +2 -2
  20. package/src/filters/geosjonFeatures.ts +2 -2
  21. package/src/filters/tileFeatures.ts +19 -41
  22. package/src/filters.ts +4 -4
  23. package/src/geo.ts +12 -14
  24. package/src/global.d.ts +3 -8
  25. package/src/index.ts +1 -0
  26. package/src/models/common.ts +9 -7
  27. package/src/models/model.ts +3 -4
  28. package/src/operations/aggregation.ts +5 -5
  29. package/src/operations/applySorting.ts +4 -4
  30. package/src/operations/groupBy.ts +4 -4
  31. package/src/operations/groupByDate.ts +1 -1
  32. package/src/operations/histogram.ts +4 -4
  33. package/src/operations/scatterPlot.ts +4 -4
  34. package/src/sources/base-source.ts +8 -8
  35. package/src/sources/boundary-query-source.ts +2 -2
  36. package/src/sources/boundary-table-source.ts +2 -2
  37. package/src/sources/h3-query-source.ts +7 -5
  38. package/src/sources/h3-table-source.ts +7 -5
  39. package/src/sources/h3-tileset-source.ts +12 -5
  40. package/src/sources/index.ts +26 -26
  41. package/src/sources/quadbin-query-source.ts +7 -5
  42. package/src/sources/quadbin-table-source.ts +7 -5
  43. package/src/sources/quadbin-tileset-source.ts +12 -5
  44. package/src/sources/raster-source.ts +7 -16
  45. package/src/sources/types.ts +10 -4
  46. package/src/sources/vector-query-source.ts +2 -3
  47. package/src/sources/vector-table-source.ts +2 -3
  48. package/src/sources/vector-tileset-source.ts +13 -6
  49. package/src/spatial-index.ts +4 -4
  50. package/src/types-internal.ts +5 -5
  51. package/src/types.ts +15 -21
  52. package/src/utils/makeIntervalComplete.ts +1 -1
  53. package/src/utils.ts +3 -3
  54. package/src/widget-sources/index.ts +1 -1
  55. package/src/widget-sources/types.ts +5 -3
  56. package/src/widget-sources/widget-query-source.ts +6 -2
  57. package/src/widget-sources/widget-remote-source.ts +66 -18
  58. package/src/widget-sources/widget-source.ts +0 -28
  59. package/src/widget-sources/widget-table-source.ts +6 -2
  60. package/src/widget-sources/widget-tileset-source.ts +130 -90
  61. package/src/widget-sources/widget-tileset-worker-source.ts +238 -0
  62. package/src/workers/constants.ts +13 -0
  63. package/src/workers/types.ts +20 -0
  64. package/src/workers/utils.ts +33 -0
  65. package/src/workers/widget-tileset-worker.ts +36 -0
  66. package/build/api/carto-api-error.d.ts +0 -26
  67. package/build/api/endpoints.d.ts +0 -24
  68. package/build/api/index.d.ts +0 -5
  69. package/build/api/query.d.ts +0 -3
  70. package/build/api/request-with-parameters.d.ts +0 -10
  71. package/build/api-client.cjs.map +0 -1
  72. package/build/api-client.modern.js +0 -3304
  73. package/build/api-client.modern.js.map +0 -1
  74. package/build/client.d.ts +0 -14
  75. package/build/constants-internal.d.ts +0 -26
  76. package/build/constants.d.ts +0 -53
  77. package/build/filters/Filter.d.ts +0 -13
  78. package/build/filters/FilterTypes.d.ts +0 -3
  79. package/build/filters/geosjonFeatures.d.ts +0 -8
  80. package/build/filters/index.d.ts +0 -6
  81. package/build/filters/tileFeatures.d.ts +0 -21
  82. package/build/filters/tileFeaturesGeometries.d.ts +0 -13
  83. package/build/filters/tileFeaturesRaster.d.ts +0 -16
  84. package/build/filters/tileFeaturesSpatialIndex.d.ts +0 -10
  85. package/build/filters.d.ts +0 -39
  86. package/build/geo.d.ts +0 -19
  87. package/build/index.d.ts +0 -15
  88. package/build/models/common.d.ts +0 -27
  89. package/build/models/index.d.ts +0 -3
  90. package/build/models/model.d.ts +0 -37
  91. package/build/operations/aggregation.d.ts +0 -8
  92. package/build/operations/applySorting.d.ts +0 -20
  93. package/build/operations/groupBy.d.ts +0 -15
  94. package/build/operations/groupByDate.d.ts +0 -11
  95. package/build/operations/histogram.d.ts +0 -13
  96. package/build/operations/index.d.ts +0 -6
  97. package/build/operations/scatterPlot.d.ts +0 -14
  98. package/build/sources/base-source.d.ts +0 -4
  99. package/build/sources/boundary-query-source.d.ts +0 -10
  100. package/build/sources/boundary-table-source.d.ts +0 -8
  101. package/build/sources/h3-query-source.d.ts +0 -5
  102. package/build/sources/h3-table-source.d.ts +0 -5
  103. package/build/sources/h3-tileset-source.d.ts +0 -5
  104. package/build/sources/index.d.ts +0 -26
  105. package/build/sources/quadbin-query-source.d.ts +0 -5
  106. package/build/sources/quadbin-table-source.d.ts +0 -5
  107. package/build/sources/quadbin-tileset-source.d.ts +0 -5
  108. package/build/sources/raster-source.d.ts +0 -5
  109. package/build/sources/types.d.ts +0 -366
  110. package/build/sources/vector-query-source.d.ts +0 -5
  111. package/build/sources/vector-table-source.d.ts +0 -5
  112. package/build/sources/vector-tileset-source.d.ts +0 -5
  113. package/build/spatial-index.d.ts +0 -14
  114. package/build/types-internal.d.ts +0 -56
  115. package/build/types.d.ts +0 -148
  116. package/build/utils/dateUtils.d.ts +0 -10
  117. package/build/utils/getTileFormat.d.ts +0 -3
  118. package/build/utils/makeIntervalComplete.d.ts +0 -2
  119. package/build/utils/transformTileCoordsToWGS84.d.ts +0 -8
  120. package/build/utils/transformToTileCoords.d.ts +0 -9
  121. package/build/utils.d.ts +0 -32
  122. package/build/widget-sources/index.d.ts +0 -7
  123. package/build/widget-sources/types.d.ts +0 -158
  124. package/build/widget-sources/widget-query-source.d.ts +0 -33
  125. package/build/widget-sources/widget-raster-source.d.ts +0 -11
  126. package/build/widget-sources/widget-remote-source.d.ts +0 -18
  127. package/build/widget-sources/widget-source.d.ts +0 -74
  128. package/build/widget-sources/widget-table-source.d.ts +0 -33
  129. package/build/widget-sources/widget-tileset-source.d.ts +0 -77
  130. package/src/filters/tileFeaturesRaster.ts +0 -111
  131. package/src/widget-sources/widget-raster-source.ts +0 -14
@@ -1,9 +1,8 @@
1
+ /* eslint-disable @typescript-eslint/require-await */
1
2
  import {TilesetSourceOptions} from '../sources/index.js';
2
- import type {ModelSource} from '../models/index.js';
3
3
  import {
4
4
  CategoryRequestOptions,
5
5
  CategoryResponse,
6
- FeaturesRequestOptions,
7
6
  FeaturesResponse,
8
7
  FormulaRequestOptions,
9
8
  FormulaResponse,
@@ -18,9 +17,9 @@ import {
18
17
  TimeSeriesRequestOptions,
19
18
  TimeSeriesResponse,
20
19
  } from './types.js';
21
- import {InvalidColumnError, getApplicableFilters} from '../utils.js';
20
+ import {InvalidColumnError, assert, getApplicableFilters} from '../utils.js';
22
21
  import {TileFormat} from '../constants.js';
23
- import {SpatialFilter, Tile} from '../types.js';
22
+ import {Filter, SpatialFilter, Tile} from '../types.js';
24
23
  import {
25
24
  TileFeatureExtractOptions,
26
25
  applyFilters,
@@ -39,6 +38,7 @@ import {FeatureData} from '../types-internal.js';
39
38
  import {FeatureCollection} from 'geojson';
40
39
  import {SpatialDataType} from '../sources/types.js';
41
40
  import {WidgetSource, WidgetSourceProps} from './widget-source.js';
41
+ import {booleanEqual} from '@turf/boolean-equal';
42
42
 
43
43
  // TODO(cleanup): Parameter defaults in source functions and widget API calls are
44
44
  // currently duplicated and possibly inconsistent. Consider consolidating and
@@ -75,19 +75,12 @@ export type WidgetTilesetSourceResult = {widgetSource: WidgetTilesetSource};
75
75
  * const { widgetSource } = await data;
76
76
  * ```
77
77
  */
78
- export class WidgetTilesetSource<
79
- Props extends WidgetTilesetSourceProps = WidgetTilesetSourceProps,
80
- > extends WidgetSource<Props> {
81
- protected _tiles: Tile[] = [];
82
- protected _features: FeatureData[] = [];
83
-
84
- protected override getModelSource(owner: string): ModelSource {
85
- return {
86
- ...super._getModelSource(owner),
87
- type: 'tileset',
88
- data: this.props.tableName,
89
- };
90
- }
78
+ export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps> {
79
+ private _tiles: Tile[] = [];
80
+ private _features: FeatureData[] = [];
81
+ private _tileFeatureExtractOptions: TileFeatureExtractOptions = {};
82
+ private _tileFeatureExtractPreviousInputs: {spatialFilter?: SpatialFilter} =
83
+ {};
91
84
 
92
85
  /**
93
86
  * Loads features as a list of tiles (typically provided by deck.gl).
@@ -96,50 +89,61 @@ export class WidgetTilesetSource<
96
89
  */
97
90
  loadTiles(tiles: unknown[]) {
98
91
  this._tiles = tiles as Tile[];
92
+ this._features.length = 0;
99
93
  }
100
94
 
101
- /**
102
- * Extracts feature data from tiles previously loaded with {@link loadTiles}.
103
- * Must be called before computing statistics on tiles.
104
- */
105
- extractTileFeatures({
106
- spatialFilter,
107
- uniqueIdProperty,
108
- options,
109
- }: {
110
- spatialFilter: SpatialFilter;
111
- uniqueIdProperty?: string;
112
- options?: TileFeatureExtractOptions;
113
- }) {
95
+ /** Configures options used to extract features from tiles. */
96
+ setTileFeatureExtractOptions(options: TileFeatureExtractOptions) {
97
+ this._tileFeatureExtractOptions = options;
98
+ this._features.length = 0;
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;
111
+ }
112
+
114
113
  this._features = tileFeatures({
115
114
  tiles: this._tiles,
116
- options,
115
+ tileFormat: this.props.tileFormat,
116
+ ...this._tileFeatureExtractOptions,
117
+
117
118
  spatialFilter,
118
- uniqueIdProperty,
119
- ...this.props,
119
+ spatialDataColumn: this.props.spatialDataColumn,
120
+ spatialDataType: this.props.spatialDataType,
120
121
  });
122
+
123
+ prevInputs.spatialFilter = spatialFilter;
121
124
  }
122
125
 
123
- /** Loads features as GeoJSON (used for testing). */
126
+ /**
127
+ * Loads features as GeoJSON (used for testing).
128
+ * @experimental
129
+ * @internal Not for public use. Spatial filters in other method calls will be ignored.
130
+ */
124
131
  loadGeoJSON({
125
132
  geojson,
126
133
  spatialFilter,
127
- uniqueIdProperty,
128
134
  }: {
129
135
  geojson: FeatureCollection;
130
136
  spatialFilter: SpatialFilter;
131
- uniqueIdProperty?: string;
132
137
  }) {
133
138
  this._features = geojsonFeatures({
134
139
  geojson,
135
140
  spatialFilter,
136
- uniqueIdProperty,
141
+ ...this._tileFeatureExtractOptions,
137
142
  });
143
+ this._tileFeatureExtractPreviousInputs.spatialFilter = spatialFilter;
138
144
  }
139
145
 
140
- override async getFeatures(
141
- options: FeaturesRequestOptions
142
- ): Promise<FeaturesResponse> {
146
+ override async getFeatures(): Promise<FeaturesResponse> {
143
147
  throw new Error('getFeatures not supported for tilesets');
144
148
  }
145
149
 
@@ -147,22 +151,24 @@ export class WidgetTilesetSource<
147
151
  column = '*',
148
152
  operation = 'count',
149
153
  joinOperation,
154
+ filters,
150
155
  filterOwner,
156
+ spatialFilter,
151
157
  }: FormulaRequestOptions): Promise<FormulaResponse> {
152
158
  if (operation === 'custom') {
153
159
  throw new Error('Custom aggregation not supported for tilesets');
154
160
  }
155
161
 
156
- if (!this._features.length) {
157
- return {value: null};
158
- }
159
-
160
162
  // Column is required except when operation is 'count'.
161
163
  if ((column && column !== '*') || operation !== 'count') {
162
164
  assertColumn(this._features, column);
163
165
  }
164
166
 
165
- const filteredFeatures = this._getFilteredFeatures(filterOwner);
167
+ const filteredFeatures = this._getFilteredFeatures(
168
+ spatialFilter,
169
+ filters,
170
+ filterOwner
171
+ );
166
172
 
167
173
  if (filteredFeatures.length === 0 && operation !== 'count') {
168
174
  return {value: null};
@@ -170,11 +176,7 @@ export class WidgetTilesetSource<
170
176
 
171
177
  const targetOperation = aggregationFunctions[operation];
172
178
  return {
173
- value: targetOperation(
174
- filteredFeatures as FeatureData[],
175
- column,
176
- joinOperation
177
- ),
179
+ value: targetOperation(filteredFeatures, column, joinOperation),
178
180
  };
179
181
  }
180
182
 
@@ -183,16 +185,22 @@ export class WidgetTilesetSource<
183
185
  ticks,
184
186
  column,
185
187
  joinOperation,
188
+ filters,
186
189
  filterOwner,
190
+ spatialFilter,
187
191
  }: HistogramRequestOptions): Promise<HistogramResponse> {
192
+ const filteredFeatures = this._getFilteredFeatures(
193
+ spatialFilter,
194
+ filters,
195
+ filterOwner
196
+ );
197
+
198
+ assertColumn(this._features, column);
199
+
188
200
  if (!this._features.length) {
189
201
  return [];
190
202
  }
191
203
 
192
- const filteredFeatures = this._getFilteredFeatures(filterOwner);
193
-
194
- assertColumn(this._features, column);
195
-
196
204
  return histogram({
197
205
  data: filteredFeatures,
198
206
  valuesColumns: normalizeColumns(column),
@@ -207,15 +215,21 @@ export class WidgetTilesetSource<
207
215
  operation = 'count',
208
216
  operationColumn,
209
217
  joinOperation,
218
+ filters,
210
219
  filterOwner,
220
+ spatialFilter,
211
221
  }: CategoryRequestOptions): Promise<CategoryResponse> {
212
- if (!this._features.length) {
222
+ const filteredFeatures = this._getFilteredFeatures(
223
+ spatialFilter,
224
+ filters,
225
+ filterOwner
226
+ );
227
+
228
+ if (!filteredFeatures.length) {
213
229
  return [];
214
230
  }
215
231
 
216
- const filteredFeatures = this._getFilteredFeatures(filterOwner);
217
-
218
- assertColumn(this._features, column as string, operationColumn as string);
232
+ assertColumn(this._features, column, operationColumn as string);
219
233
 
220
234
  const groups = groupValuesByColumn({
221
235
  data: filteredFeatures,
@@ -233,14 +247,20 @@ export class WidgetTilesetSource<
233
247
  yAxisColumn,
234
248
  xAxisJoinOperation,
235
249
  yAxisJoinOperation,
250
+ filters,
236
251
  filterOwner,
252
+ spatialFilter,
237
253
  }: ScatterRequestOptions): Promise<ScatterResponse> {
238
- if (!this._features.length) {
254
+ const filteredFeatures = this._getFilteredFeatures(
255
+ spatialFilter,
256
+ filters,
257
+ filterOwner
258
+ );
259
+
260
+ if (!filteredFeatures.length) {
239
261
  return [];
240
262
  }
241
263
 
242
- const filteredFeatures = this._getFilteredFeatures(filterOwner);
243
-
244
264
  assertColumn(this._features, xAxisColumn, yAxisColumn);
245
265
 
246
266
  return scatterPlot({
@@ -252,35 +272,36 @@ export class WidgetTilesetSource<
252
272
  });
253
273
  }
254
274
 
255
- override async getTable(
256
- options: TableRequestOptions
257
- ): Promise<TableResponse> {
258
- const {filterOwner, spatialFilter, abortController, ...params} = options;
259
- const {
260
- columns,
261
- searchFilterColumn,
262
- searchFilterText,
263
- sortBy,
264
- sortDirection,
265
- sortByColumnType,
266
- offset = 0,
267
- limit = 10,
268
- } = params;
275
+ override async getTable({
276
+ columns,
277
+ searchFilterColumn,
278
+ searchFilterText,
279
+ sortBy,
280
+ sortDirection,
281
+ sortByColumnType,
282
+ offset = 0,
283
+ limit = 10,
284
+ filters,
285
+ filterOwner,
286
+ spatialFilter,
287
+ }: TableRequestOptions): Promise<TableResponse> {
288
+ // Filter.
289
+ let filteredFeatures = this._getFilteredFeatures(
290
+ spatialFilter,
291
+ filters,
292
+ filterOwner
293
+ );
269
294
 
270
- if (!this._features.length) {
295
+ if (!filteredFeatures.length) {
271
296
  return {rows: [], totalCount: 0};
272
297
  }
273
298
 
274
- // Filter.
275
- let filteredFeatures = this._getFilteredFeatures(filterOwner);
276
-
277
299
  // Search.
278
- // TODO: Could we get the same behavior by applying filters in loadTiles()?
279
300
  if (searchFilterColumn && searchFilterText) {
280
301
  filteredFeatures = filteredFeatures.filter(
281
302
  (row) =>
282
303
  row[searchFilterColumn] &&
283
- String(row[searchFilterColumn])
304
+ String(row[searchFilterColumn] as unknown)
284
305
  .toLowerCase()
285
306
  .includes(String(searchFilterText).toLowerCase())
286
307
  );
@@ -318,15 +339,21 @@ export class WidgetTilesetSource<
318
339
  operation,
319
340
  operationColumn,
320
341
  joinOperation,
342
+ filters,
321
343
  filterOwner,
344
+ spatialFilter,
322
345
  }: TimeSeriesRequestOptions): Promise<TimeSeriesResponse> {
323
- if (!this._features.length) {
346
+ const filteredFeatures = this._getFilteredFeatures(
347
+ spatialFilter,
348
+ filters,
349
+ filterOwner
350
+ );
351
+
352
+ if (!filteredFeatures.length) {
324
353
  return {rows: []};
325
354
  }
326
355
 
327
- const filteredFeatures = this._getFilteredFeatures(filterOwner);
328
-
329
- assertColumn(this._features, column as string, operationColumn as string);
356
+ assertColumn(this._features, column, operationColumn as string);
330
357
 
331
358
  const rows =
332
359
  groupValuesByDateColumn({
@@ -343,17 +370,24 @@ export class WidgetTilesetSource<
343
370
 
344
371
  override async getRange({
345
372
  column,
373
+ filters,
346
374
  filterOwner,
375
+ spatialFilter,
347
376
  }: RangeRequestOptions): Promise<RangeResponse> {
377
+ assertColumn(this._features, column);
378
+
379
+ const filteredFeatures = this._getFilteredFeatures(
380
+ spatialFilter,
381
+ filters,
382
+ filterOwner
383
+ );
384
+
348
385
  if (!this._features.length) {
349
386
  // TODO: Is this the only nullable response in the Widgets API? If so,
350
387
  // can we do something more consistent?
351
388
  return null;
352
389
  }
353
390
 
354
- assertColumn(this._features, column);
355
-
356
- const filteredFeatures = this._getFilteredFeatures(filterOwner);
357
391
  return {
358
392
  min: aggregationFunctions.min(filteredFeatures, column),
359
393
  max: aggregationFunctions.max(filteredFeatures, column),
@@ -364,10 +398,16 @@ export class WidgetTilesetSource<
364
398
  * INTERNAL
365
399
  */
366
400
 
367
- private _getFilteredFeatures(filterOwner: string | undefined): FeatureData[] {
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);
368
408
  return applyFilters(
369
409
  this._features,
370
- getApplicableFilters(filterOwner, this.props.filters),
410
+ getApplicableFilters(filterOwner, filters || this.props.filters),
371
411
  this.props.filtersLogicalOperator || 'and'
372
412
  );
373
413
  }
@@ -0,0 +1,238 @@
1
+ import {
2
+ CategoryRequestOptions,
3
+ CategoryResponse,
4
+ FeaturesResponse,
5
+ FormulaRequestOptions,
6
+ FormulaResponse,
7
+ HistogramRequestOptions,
8
+ HistogramResponse,
9
+ RangeRequestOptions,
10
+ RangeResponse,
11
+ ScatterRequestOptions,
12
+ ScatterResponse,
13
+ TableRequestOptions,
14
+ TableResponse,
15
+ TimeSeriesRequestOptions,
16
+ TimeSeriesResponse,
17
+ } from './types.js';
18
+ import {SpatialFilter, Tile} from '../types.js';
19
+ import {TileFeatureExtractOptions} from '../filters/index.js';
20
+ import {FeatureCollection} from 'geojson';
21
+ import {WidgetSource} from './widget-source.js';
22
+ import {WidgetTilesetSourceProps} from './widget-tileset-source.js';
23
+ import {Method} from '../workers/constants.js';
24
+ import {WorkerRequest, WorkerResponse} from '../workers/types.js';
25
+
26
+ /**
27
+ * TODO
28
+ */
29
+ export class WidgetTilesetWorkerSource extends WidgetSource<WidgetTilesetSourceProps> {
30
+ constructor(props: WidgetTilesetSourceProps) {
31
+ super(props);
32
+
33
+ WidgetTilesetWorkerSource.init();
34
+ WidgetTilesetWorkerSource.WORKER.postMessage({
35
+ tableName: this.props.tableName,
36
+ method: Method.INIT,
37
+ params: [this.props],
38
+ } as WorkerRequest);
39
+ }
40
+
41
+ /**
42
+ * Loads features as a list of tiles (typically provided by deck.gl).
43
+ * After tiles are loaded, {@link extractTileFeatures} must be called
44
+ * before computing statistics on the tiles.
45
+ */
46
+ loadTiles(tiles: unknown[]) {
47
+ tiles = (tiles as Tile[]).map(({id, bbox, data}) => ({
48
+ id,
49
+ bbox,
50
+ data,
51
+ }));
52
+
53
+ WidgetTilesetWorkerSource.WORKER.postMessage({
54
+ tableName: this.props.tableName,
55
+ method: Method.LOAD_TILES,
56
+ params: [tiles],
57
+ } as WorkerRequest);
58
+ }
59
+
60
+ /** Configures options used to extract features from tiles. */
61
+ setTileFeatureExtractOptions(options: TileFeatureExtractOptions) {
62
+ WidgetTilesetWorkerSource.WORKER.postMessage({
63
+ tableName: this.props.tableName,
64
+ type: Method.SET_TILE_FEATURE_EXTRACT_OPTIONS,
65
+ params: [options],
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Loads features as GeoJSON (used for testing).
71
+ * @experimental
72
+ * @internal Not for public use. Spatial filters in other method calls will be ignored.
73
+ */
74
+ loadGeoJSON({
75
+ geojson,
76
+ spatialFilter,
77
+ }: {
78
+ geojson: FeatureCollection;
79
+ spatialFilter: SpatialFilter;
80
+ }) {
81
+ WidgetTilesetWorkerSource.WORKER.postMessage({
82
+ tableName: this.props.tableName,
83
+ method: Method.LOAD_GEOJSON,
84
+ params: [{geojson, spatialFilter}],
85
+ } as WorkerRequest);
86
+ }
87
+
88
+ // eslint-disable-next-line @typescript-eslint/require-await
89
+ override async getFeatures(): Promise<FeaturesResponse> {
90
+ throw new Error('getFeatures not supported for tilesets');
91
+ }
92
+
93
+ async getFormula({
94
+ abortController,
95
+ ...options
96
+ }: FormulaRequestOptions): Promise<FormulaResponse> {
97
+ return this._executeWorkerMethod(
98
+ this.props.tableName,
99
+ Method.GET_FORMULA,
100
+ [options],
101
+ abortController?.signal
102
+ );
103
+ }
104
+
105
+ override async getHistogram({
106
+ abortController,
107
+ ...options
108
+ }: HistogramRequestOptions): Promise<HistogramResponse> {
109
+ return this._executeWorkerMethod(
110
+ this.props.tableName,
111
+ Method.GET_HISTOGRAM,
112
+ [options],
113
+ abortController?.signal
114
+ );
115
+ }
116
+
117
+ override async getCategories({
118
+ abortController,
119
+ ...options
120
+ }: CategoryRequestOptions): Promise<CategoryResponse> {
121
+ return this._executeWorkerMethod(
122
+ this.props.tableName,
123
+ Method.GET_CATEGORIES,
124
+ [options],
125
+ abortController?.signal
126
+ );
127
+ }
128
+
129
+ override async getScatter({
130
+ abortController,
131
+ ...options
132
+ }: ScatterRequestOptions): Promise<ScatterResponse> {
133
+ return this._executeWorkerMethod(
134
+ this.props.tableName,
135
+ Method.GET_SCATTER,
136
+ [options],
137
+ abortController?.signal
138
+ );
139
+ }
140
+
141
+ override async getTable({
142
+ abortController,
143
+ ...options
144
+ }: TableRequestOptions): Promise<TableResponse> {
145
+ return this._executeWorkerMethod(
146
+ this.props.tableName,
147
+ Method.GET_TABLE,
148
+ [options],
149
+ abortController?.signal
150
+ );
151
+ }
152
+
153
+ override async getTimeSeries({
154
+ abortController,
155
+ ...options
156
+ }: TimeSeriesRequestOptions): Promise<TimeSeriesResponse> {
157
+ return this._executeWorkerMethod(
158
+ this.props.tableName,
159
+ Method.GET_TIME_SERIES,
160
+ [options],
161
+ abortController?.signal
162
+ );
163
+ }
164
+
165
+ override async getRange({
166
+ abortController,
167
+ ...options
168
+ }: RangeRequestOptions): Promise<RangeResponse> {
169
+ return this._executeWorkerMethod(
170
+ this.props.tableName,
171
+ Method.GET_RANGE,
172
+ [options],
173
+ abortController?.signal
174
+ );
175
+ }
176
+
177
+ /////////////////////////////////////////////////////////////////////////////
178
+ // WEB WORKER MANAGEMENT
179
+
180
+ // TODO: Singleton? Pool shared among datasets? One per dataset?
181
+ protected static WORKER: Worker;
182
+ protected static _nextRequestID = 1;
183
+
184
+ static init() {
185
+ WidgetTilesetWorkerSource.WORKER = new Worker(
186
+ new URL('@carto/api-client/worker', import.meta.url),
187
+ {
188
+ type: 'module',
189
+ name: 'cartowidgettileset',
190
+ }
191
+ );
192
+ }
193
+
194
+ _executeWorkerMethod<T>(
195
+ tableName: string,
196
+ method: Method,
197
+ params: unknown[],
198
+ signal?: AbortSignal
199
+ ): Promise<T> {
200
+ const worker = WidgetTilesetWorkerSource.WORKER;
201
+ const requestId = WidgetTilesetWorkerSource._nextRequestID++;
202
+
203
+ // TODO: ViewState may contain non-serializable data, which we do not need.
204
+ // Remove this sanitization after sc-469614 is fixed.
205
+ const options = params[0] as any;
206
+ if (options?.spatialIndexReferenceViewState) {
207
+ const {zoom, latitude, longitude} =
208
+ options.spatialIndexReferenceViewState;
209
+ options.spatialIndexReferenceViewState = {zoom, latitude, longitude};
210
+ }
211
+
212
+ worker.postMessage({
213
+ requestId,
214
+ tableName,
215
+ method,
216
+ params,
217
+ } as WorkerRequest);
218
+
219
+ return new Promise((resolve, reject) => {
220
+ function listener(e: MessageEvent) {
221
+ const response = e.data as WorkerResponse;
222
+ if (response.requestId !== requestId) return;
223
+
224
+ worker.removeEventListener('message', listener);
225
+
226
+ if (signal?.aborted) {
227
+ reject(new Error(signal.reason));
228
+ } else if (response.ok) {
229
+ resolve(response.result as T);
230
+ } else {
231
+ reject(new Error(response.error));
232
+ }
233
+ }
234
+
235
+ worker.addEventListener('message', listener);
236
+ });
237
+ }
238
+ }
@@ -0,0 +1,13 @@
1
+ export enum Method {
2
+ INIT = 'init',
3
+ LOAD_TILES = 'loadTiles',
4
+ SET_TILE_FEATURE_EXTRACT_OPTIONS = 'setTileFeatureExtractOptions',
5
+ LOAD_GEOJSON = 'loadGeoJSON',
6
+ GET_FORMULA = 'getFormula',
7
+ GET_HISTOGRAM = 'getHistogram',
8
+ GET_CATEGORIES = 'getCategories',
9
+ GET_SCATTER = 'getScatter',
10
+ GET_TABLE = 'getTable',
11
+ GET_TIME_SERIES = 'getTimeSeries',
12
+ GET_RANGE = 'getRange',
13
+ }
@@ -0,0 +1,20 @@
1
+ import type {Method} from './constants.js';
2
+
3
+ export type WorkerRequest = {
4
+ requestId?: number;
5
+ tableName: string; // TODO: Table name is not a unique identifier.
6
+ method: Method;
7
+ params: unknown[];
8
+ };
9
+
10
+ export type WorkerResponse =
11
+ | {
12
+ requestId: number;
13
+ ok: true;
14
+ result: unknown;
15
+ }
16
+ | {
17
+ requestId: number;
18
+ ok: false;
19
+ error: string;
20
+ };
@@ -0,0 +1,33 @@
1
+ let _isModuleWorkerSupported: boolean | null =
2
+ TSUP_FORMAT === 'cjs' ? false : null;
3
+
4
+ /**
5
+ * Checks whether current environment supports ES Module Workers, by way of a
6
+ * two-part check:
7
+ * (1) If this is a CJS build, return false.
8
+ * (2) If this is an ESM build, return true if the runtime supports ESM Workers.
9
+ */
10
+ export function isModuleWorkerSupported(): boolean {
11
+ if (_isModuleWorkerSupported !== null) {
12
+ return _isModuleWorkerSupported;
13
+ }
14
+
15
+ try {
16
+ // https://stackoverflow.com/a/62963963
17
+ new Worker('blob://', {
18
+ get type() {
19
+ _isModuleWorkerSupported = true;
20
+ return 'module' as const;
21
+ },
22
+ });
23
+ } catch {
24
+ // Do nothing. 'blob://' should always throw an error to prevent the
25
+ // browser from creating an (expensive) worker. We care whether the
26
+ // 'type' getter was called, not about the error.
27
+ } finally {
28
+ // If 'type' getter wasn't called, modules are unsupported.
29
+ _isModuleWorkerSupported ||= false;
30
+ }
31
+
32
+ return _isModuleWorkerSupported;
33
+ }