@carto/api-client 0.4.6 → 0.4.7-alpha.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/build/api/carto-api-error.d.ts +1 -1
  3. package/build/api/query.d.ts +1 -1
  4. package/build/api/request-with-parameters.d.ts +2 -2
  5. package/build/api-client.cjs +2365 -279
  6. package/build/api-client.cjs.map +1 -1
  7. package/build/api-client.modern.js +2274 -298
  8. package/build/api-client.modern.js.map +1 -1
  9. package/build/client.d.ts +2 -2
  10. package/build/constants-internal.d.ts +5 -5
  11. package/build/constants.d.ts +25 -3
  12. package/build/deck/get-data-filter-extension-props.d.ts +28 -0
  13. package/build/deck/index.d.ts +1 -0
  14. package/build/filters/Filter.d.ts +25 -0
  15. package/build/filters/FilterTypes.d.ts +3 -0
  16. package/build/filters/geosjonFeatures.d.ts +8 -0
  17. package/build/filters/index.d.ts +6 -0
  18. package/build/filters/tileFeatures.d.ts +20 -0
  19. package/build/filters/tileFeaturesGeometries.d.ts +13 -0
  20. package/build/filters/tileFeaturesSpatialIndex.d.ts +10 -0
  21. package/build/filters.d.ts +2 -2
  22. package/build/geo.d.ts +1 -1
  23. package/build/index.d.ts +5 -0
  24. package/build/models/common.d.ts +5 -4
  25. package/build/models/index.d.ts +1 -1
  26. package/build/models/model.d.ts +2 -2
  27. package/build/operations/aggregation.d.ts +8 -0
  28. package/build/operations/applySorting.d.ts +20 -0
  29. package/build/operations/groupBy.d.ts +15 -0
  30. package/build/operations/groupByDate.d.ts +11 -0
  31. package/build/operations/histogram.d.ts +13 -0
  32. package/build/operations/index.d.ts +6 -0
  33. package/build/operations/scatterPlot.d.ts +14 -0
  34. package/build/sources/base-source.d.ts +2 -2
  35. package/build/sources/boundary-query-source.d.ts +1 -1
  36. package/build/sources/boundary-table-source.d.ts +1 -1
  37. package/build/sources/h3-query-source.d.ts +2 -2
  38. package/build/sources/h3-table-source.d.ts +2 -2
  39. package/build/sources/h3-tileset-source.d.ts +1 -1
  40. package/build/sources/index.d.ts +26 -26
  41. package/build/sources/quadbin-query-source.d.ts +2 -2
  42. package/build/sources/quadbin-table-source.d.ts +2 -2
  43. package/build/sources/quadbin-tileset-source.d.ts +1 -1
  44. package/build/sources/raster-source.d.ts +1 -1
  45. package/build/sources/types.d.ts +3 -3
  46. package/build/sources/vector-query-source.d.ts +1 -1
  47. package/build/sources/vector-table-source.d.ts +1 -1
  48. package/build/sources/vector-tileset-source.d.ts +1 -1
  49. package/build/spatial-index.d.ts +3 -3
  50. package/build/types-internal.d.ts +9 -5
  51. package/build/types.d.ts +74 -14
  52. package/build/utils/dateUtils.d.ts +10 -0
  53. package/build/utils/getTileFormat.d.ts +3 -0
  54. package/build/utils/makeIntervalComplete.d.ts +2 -0
  55. package/build/utils/transformTileCoordsToWGS84.d.ts +8 -0
  56. package/build/utils/transformToTileCoords.d.ts +9 -0
  57. package/build/utils.d.ts +3 -3
  58. package/build/widget-sources/index.d.ts +3 -1
  59. package/build/widget-sources/types.d.ts +38 -25
  60. package/build/widget-sources/widget-query-source.d.ts +4 -3
  61. package/build/widget-sources/widget-remote-source.d.ts +18 -0
  62. package/build/widget-sources/{widget-base-source.d.ts → widget-source.d.ts} +16 -41
  63. package/build/widget-sources/widget-table-source.d.ts +4 -3
  64. package/build/widget-sources/widget-tileset-source.d.ts +75 -0
  65. package/package.json +46 -29
  66. package/src/api/carto-api-error.ts +1 -1
  67. package/src/api/query.ts +5 -5
  68. package/src/api/request-with-parameters.ts +6 -6
  69. package/src/client.ts +3 -3
  70. package/src/constants-internal.ts +5 -5
  71. package/src/constants.ts +28 -3
  72. package/src/deck/get-data-filter-extension-props.ts +164 -0
  73. package/src/deck/index.ts +1 -0
  74. package/src/filters/Filter.ts +179 -0
  75. package/src/filters/FilterTypes.ts +109 -0
  76. package/src/filters/geosjonFeatures.ts +32 -0
  77. package/src/filters/index.ts +6 -0
  78. package/src/filters/tileFeatures.ts +50 -0
  79. package/src/filters/tileFeaturesGeometries.ts +444 -0
  80. package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
  81. package/src/filters.ts +4 -4
  82. package/src/geo.ts +12 -14
  83. package/src/index.ts +7 -0
  84. package/src/models/common.ts +11 -9
  85. package/src/models/index.ts +1 -1
  86. package/src/models/model.ts +3 -4
  87. package/src/operations/aggregation.ts +154 -0
  88. package/src/operations/applySorting.ts +109 -0
  89. package/src/operations/groupBy.ts +59 -0
  90. package/src/operations/groupByDate.ts +98 -0
  91. package/src/operations/histogram.ts +66 -0
  92. package/src/operations/index.ts +6 -0
  93. package/src/operations/scatterPlot.ts +50 -0
  94. package/src/sources/base-source.ts +8 -8
  95. package/src/sources/boundary-query-source.ts +2 -2
  96. package/src/sources/boundary-table-source.ts +2 -2
  97. package/src/sources/h3-query-source.ts +7 -5
  98. package/src/sources/h3-table-source.ts +7 -5
  99. package/src/sources/h3-tileset-source.ts +2 -2
  100. package/src/sources/index.ts +26 -26
  101. package/src/sources/quadbin-query-source.ts +7 -5
  102. package/src/sources/quadbin-table-source.ts +7 -5
  103. package/src/sources/quadbin-tileset-source.ts +2 -2
  104. package/src/sources/raster-source.ts +3 -2
  105. package/src/sources/types.ts +3 -3
  106. package/src/sources/vector-query-source.ts +7 -5
  107. package/src/sources/vector-table-source.ts +7 -5
  108. package/src/sources/vector-tileset-source.ts +2 -2
  109. package/src/spatial-index.ts +4 -5
  110. package/src/types-internal.ts +11 -5
  111. package/src/types.ts +73 -15
  112. package/src/utils/dateUtils.ts +28 -0
  113. package/src/utils/getTileFormat.ts +9 -0
  114. package/src/utils/makeIntervalComplete.ts +17 -0
  115. package/src/utils/transformTileCoordsToWGS84.ts +77 -0
  116. package/src/utils/transformToTileCoords.ts +85 -0
  117. package/src/utils.ts +3 -3
  118. package/src/widget-sources/index.ts +3 -1
  119. package/src/widget-sources/types.ts +39 -25
  120. package/src/widget-sources/widget-query-source.ts +12 -5
  121. package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +51 -171
  122. package/src/widget-sources/widget-source.ts +173 -0
  123. package/src/widget-sources/widget-table-source.ts +12 -5
  124. package/src/widget-sources/widget-tileset-source.ts +456 -0
@@ -0,0 +1,456 @@
1
+ /* eslint-disable @typescript-eslint/require-await */
2
+ import {TilesetSourceOptions} from '../sources/index.js';
3
+ import type {ModelSource} from '../models/index.js';
4
+ import {
5
+ CategoryRequestOptions,
6
+ CategoryResponse,
7
+ FeaturesResponse,
8
+ FormulaRequestOptions,
9
+ FormulaResponse,
10
+ HistogramRequestOptions,
11
+ HistogramResponse,
12
+ RangeRequestOptions,
13
+ RangeResponse,
14
+ ScatterRequestOptions,
15
+ ScatterResponse,
16
+ TableRequestOptions,
17
+ TableResponse,
18
+ TimeSeriesRequestOptions,
19
+ TimeSeriesResponse,
20
+ } from './types.js';
21
+ import {InvalidColumnError, assert, getApplicableFilters} from '../utils.js';
22
+ import {TileFormat} from '../constants.js';
23
+ import {Filter, Filters, SpatialFilter, Tile} from '../types.js';
24
+ import {
25
+ TileFeatureExtractOptions,
26
+ applyFilters,
27
+ geojsonFeatures,
28
+ tileFeatures,
29
+ } from '../filters/index.js';
30
+ import {
31
+ aggregationFunctions,
32
+ applySorting,
33
+ groupValuesByColumn,
34
+ groupValuesByDateColumn,
35
+ histogram,
36
+ scatterPlot,
37
+ } from '../operations/index.js';
38
+ import {FeatureData} from '../types-internal.js';
39
+ import {FeatureCollection} from 'geojson';
40
+ import {SpatialDataType} from '../sources/types.js';
41
+ import {WidgetSource, WidgetSourceProps} from './widget-source.js';
42
+ import {booleanEqual} from '@turf/boolean-equal';
43
+
44
+ // TODO(cleanup): Parameter defaults in source functions and widget API calls are
45
+ // currently duplicated and possibly inconsistent. Consider consolidating and
46
+ // operating on Required<T> objects. See:
47
+ // https://github.com/CartoDB/carto-api-client/issues/39
48
+
49
+ export type WidgetTilesetSourceProps = WidgetSourceProps &
50
+ Omit<TilesetSourceOptions, 'filters'> & {
51
+ tileFormat: TileFormat;
52
+ spatialDataType: SpatialDataType;
53
+ };
54
+
55
+ export type WidgetTilesetSourceResult = {widgetSource: WidgetTilesetSource};
56
+
57
+ /**
58
+ * Source for Widget API requests on a data source defined by a tileset.
59
+ *
60
+ * Generally not intended to be constructed directly. Instead, call
61
+ * {@link vectorTilesetSource}, {@link h3TilesetSource}, or {@link quadbinTilesetSource},
62
+ * which can be shared with map layers. Sources contain a `widgetSource`
63
+ * property, for use by widget implementations.
64
+ *
65
+ * Example:
66
+ *
67
+ * ```javascript
68
+ * import { vectorTilesetSource } from '@carto/api-client';
69
+ *
70
+ * const data = vectorTilesetSource({
71
+ * accessToken: '••••',
72
+ * connectionName: 'carto_dw',
73
+ * tableName: 'carto-demo-data.demo_rasters.my_tileset_source'
74
+ * });
75
+ *
76
+ * const { widgetSource } = await data;
77
+ * ```
78
+ */
79
+ export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps> {
80
+ private _tiles: Tile[] = [];
81
+ private _features: FeatureData[] = [];
82
+ private _tileFeatureExtractOptions: TileFeatureExtractOptions = {};
83
+ private _tileFeatureExtractPreviousInputs: {spatialFilter?: SpatialFilter} =
84
+ {};
85
+
86
+ protected override getModelSource(
87
+ filters: Filters | undefined,
88
+ filterOwner: string
89
+ ): ModelSource {
90
+ return {
91
+ ...super._getModelSource(filters, filterOwner),
92
+ type: 'tileset',
93
+ data: this.props.tableName,
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Loads features as a list of tiles (typically provided by deck.gl).
99
+ * After tiles are loaded, {@link extractTileFeatures} must be called
100
+ * before computing statistics on the tiles.
101
+ */
102
+ loadTiles(tiles: unknown[]) {
103
+ this._tiles = tiles as Tile[];
104
+ this._features.length = 0;
105
+ }
106
+
107
+ /** Configures options used to extract features from tiles. */
108
+ setTileFeatureExtractOptions(options: TileFeatureExtractOptions) {
109
+ this._tileFeatureExtractOptions = options;
110
+ this._features.length = 0;
111
+ }
112
+
113
+ protected _extractTileFeatures(spatialFilter: SpatialFilter) {
114
+ // When spatial filter has not changed, don't redo extraction. If tiles or
115
+ // tile extract options change, features will have been cleared already.
116
+ const prevInputs = this._tileFeatureExtractPreviousInputs;
117
+ if (
118
+ this._features.length &&
119
+ prevInputs.spatialFilter &&
120
+ booleanEqual(prevInputs.spatialFilter, spatialFilter)
121
+ ) {
122
+ return;
123
+ }
124
+
125
+ this._features = tileFeatures({
126
+ tiles: this._tiles,
127
+ tileFormat: this.props.tileFormat,
128
+ ...this._tileFeatureExtractOptions,
129
+
130
+ spatialFilter,
131
+ spatialDataColumn: this.props.spatialDataColumn,
132
+ spatialDataType: this.props.spatialDataType,
133
+ });
134
+
135
+ prevInputs.spatialFilter = spatialFilter;
136
+ }
137
+
138
+ /**
139
+ * Loads features as GeoJSON (used for testing).
140
+ * @experimental
141
+ * @internal Not for public use. Spatial filters in other method calls will be ignored.
142
+ */
143
+ loadGeoJSON({
144
+ geojson,
145
+ spatialFilter,
146
+ }: {
147
+ geojson: FeatureCollection;
148
+ spatialFilter: SpatialFilter;
149
+ }) {
150
+ this._features = geojsonFeatures({
151
+ geojson,
152
+ spatialFilter,
153
+ ...this._tileFeatureExtractOptions,
154
+ });
155
+ this._tileFeatureExtractPreviousInputs.spatialFilter = spatialFilter;
156
+ }
157
+
158
+ override async getFeatures(): Promise<FeaturesResponse> {
159
+ throw new Error('getFeatures not supported for tilesets');
160
+ }
161
+
162
+ async getFormula({
163
+ column = '*',
164
+ operation = 'count',
165
+ joinOperation,
166
+ filters,
167
+ filterOwner,
168
+ spatialFilter,
169
+ }: FormulaRequestOptions): Promise<FormulaResponse> {
170
+ if (operation === 'custom') {
171
+ throw new Error('Custom aggregation not supported for tilesets');
172
+ }
173
+
174
+ // Column is required except when operation is 'count'.
175
+ if ((column && column !== '*') || operation !== 'count') {
176
+ assertColumn(this._features, column);
177
+ }
178
+
179
+ const filteredFeatures = this._getFilteredFeatures(
180
+ spatialFilter,
181
+ filters,
182
+ filterOwner
183
+ );
184
+
185
+ if (filteredFeatures.length === 0 && operation !== 'count') {
186
+ return {value: null};
187
+ }
188
+
189
+ const targetOperation = aggregationFunctions[operation];
190
+ return {
191
+ value: targetOperation(filteredFeatures, column, joinOperation),
192
+ };
193
+ }
194
+
195
+ override async getHistogram({
196
+ operation = 'count',
197
+ ticks,
198
+ column,
199
+ joinOperation,
200
+ filters,
201
+ filterOwner,
202
+ spatialFilter,
203
+ }: HistogramRequestOptions): Promise<HistogramResponse> {
204
+ const filteredFeatures = this._getFilteredFeatures(
205
+ spatialFilter,
206
+ filters,
207
+ filterOwner
208
+ );
209
+
210
+ assertColumn(this._features, column);
211
+
212
+ if (!this._features.length) {
213
+ return [];
214
+ }
215
+
216
+ return histogram({
217
+ data: filteredFeatures,
218
+ valuesColumns: normalizeColumns(column),
219
+ joinOperation,
220
+ ticks,
221
+ operation,
222
+ });
223
+ }
224
+
225
+ override async getCategories({
226
+ column,
227
+ operation = 'count',
228
+ operationColumn,
229
+ joinOperation,
230
+ filters,
231
+ filterOwner,
232
+ spatialFilter,
233
+ }: CategoryRequestOptions): Promise<CategoryResponse> {
234
+ const filteredFeatures = this._getFilteredFeatures(
235
+ spatialFilter,
236
+ filters,
237
+ filterOwner
238
+ );
239
+
240
+ if (!filteredFeatures.length) {
241
+ return [];
242
+ }
243
+
244
+ assertColumn(this._features, column, operationColumn as string);
245
+
246
+ const groups = groupValuesByColumn({
247
+ data: filteredFeatures,
248
+ valuesColumns: normalizeColumns(operationColumn || column),
249
+ joinOperation,
250
+ keysColumn: column,
251
+ operation,
252
+ });
253
+
254
+ return groups || [];
255
+ }
256
+
257
+ override async getScatter({
258
+ xAxisColumn,
259
+ yAxisColumn,
260
+ xAxisJoinOperation,
261
+ yAxisJoinOperation,
262
+ filters,
263
+ filterOwner,
264
+ spatialFilter,
265
+ }: ScatterRequestOptions): Promise<ScatterResponse> {
266
+ const filteredFeatures = this._getFilteredFeatures(
267
+ spatialFilter,
268
+ filters,
269
+ filterOwner
270
+ );
271
+
272
+ if (!filteredFeatures.length) {
273
+ return [];
274
+ }
275
+
276
+ assertColumn(this._features, xAxisColumn, yAxisColumn);
277
+
278
+ return scatterPlot({
279
+ data: filteredFeatures,
280
+ xAxisColumns: normalizeColumns(xAxisColumn),
281
+ xAxisJoinOperation,
282
+ yAxisColumns: normalizeColumns(yAxisColumn),
283
+ yAxisJoinOperation,
284
+ });
285
+ }
286
+
287
+ override async getTable({
288
+ columns,
289
+ searchFilterColumn,
290
+ searchFilterText,
291
+ sortBy,
292
+ sortDirection,
293
+ sortByColumnType,
294
+ offset = 0,
295
+ limit = 10,
296
+ filters,
297
+ filterOwner,
298
+ spatialFilter,
299
+ }: TableRequestOptions): Promise<TableResponse> {
300
+ // Filter.
301
+ let filteredFeatures = this._getFilteredFeatures(
302
+ spatialFilter,
303
+ filters,
304
+ filterOwner
305
+ );
306
+
307
+ if (!filteredFeatures.length) {
308
+ return {rows: [], totalCount: 0};
309
+ }
310
+
311
+ // Search.
312
+ if (searchFilterColumn && searchFilterText) {
313
+ filteredFeatures = filteredFeatures.filter(
314
+ (row) =>
315
+ row[searchFilterColumn] &&
316
+ String(row[searchFilterColumn] as unknown)
317
+ .toLowerCase()
318
+ .includes(String(searchFilterText).toLowerCase())
319
+ );
320
+ }
321
+
322
+ // Sort.
323
+ let rows = applySorting(filteredFeatures, {
324
+ sortBy,
325
+ sortByDirection: sortDirection,
326
+ sortByColumnType,
327
+ });
328
+ const totalCount = rows.length;
329
+
330
+ // Offset and limit.
331
+ rows = rows.slice(
332
+ Math.min(offset, totalCount),
333
+ Math.min(offset + limit, totalCount)
334
+ );
335
+
336
+ // Select columns.
337
+ rows = rows.map((srcRow: FeatureData) => {
338
+ const dstRow: FeatureData = {};
339
+ for (const column of columns) {
340
+ dstRow[column] = srcRow[column];
341
+ }
342
+ return dstRow;
343
+ });
344
+
345
+ return {rows, totalCount} as TableResponse;
346
+ }
347
+
348
+ override async getTimeSeries({
349
+ column,
350
+ stepSize,
351
+ operation,
352
+ operationColumn,
353
+ joinOperation,
354
+ filters,
355
+ filterOwner,
356
+ spatialFilter,
357
+ }: TimeSeriesRequestOptions): Promise<TimeSeriesResponse> {
358
+ const filteredFeatures = this._getFilteredFeatures(
359
+ spatialFilter,
360
+ filters,
361
+ filterOwner
362
+ );
363
+
364
+ if (!filteredFeatures.length) {
365
+ return {rows: []};
366
+ }
367
+
368
+ assertColumn(this._features, column, operationColumn as string);
369
+
370
+ const rows =
371
+ groupValuesByDateColumn({
372
+ data: filteredFeatures,
373
+ valuesColumns: normalizeColumns(operationColumn || column),
374
+ keysColumn: column,
375
+ groupType: stepSize,
376
+ operation,
377
+ joinOperation,
378
+ }) || [];
379
+
380
+ return {rows};
381
+ }
382
+
383
+ override async getRange({
384
+ column,
385
+ filters,
386
+ filterOwner,
387
+ spatialFilter,
388
+ }: RangeRequestOptions): Promise<RangeResponse> {
389
+ assertColumn(this._features, column);
390
+
391
+ const filteredFeatures = this._getFilteredFeatures(
392
+ spatialFilter,
393
+ filters,
394
+ filterOwner
395
+ );
396
+
397
+ if (!this._features.length) {
398
+ // TODO: Is this the only nullable response in the Widgets API? If so,
399
+ // can we do something more consistent?
400
+ return null;
401
+ }
402
+
403
+ return {
404
+ min: aggregationFunctions.min(filteredFeatures, column),
405
+ max: aggregationFunctions.max(filteredFeatures, column),
406
+ };
407
+ }
408
+
409
+ /****************************************************************************
410
+ * INTERNAL
411
+ */
412
+
413
+ private _getFilteredFeatures(
414
+ spatialFilter?: SpatialFilter,
415
+ filters?: Record<string, Filter>,
416
+ filterOwner?: string
417
+ ): FeatureData[] {
418
+ assert(spatialFilter, 'spatialFilter required for tilesets');
419
+ this._extractTileFeatures(spatialFilter);
420
+ return applyFilters(
421
+ this._features,
422
+ getApplicableFilters(filterOwner, filters || this.props.filters),
423
+ this.props.filtersLogicalOperator || 'and'
424
+ );
425
+ }
426
+ }
427
+
428
+ function assertColumn(
429
+ features: FeatureData[],
430
+ ...columnArgs: string[] | string[][]
431
+ ) {
432
+ // TODO(cleanup): Can drop support for multiple column shapes here?
433
+
434
+ // Due to the multiple column shape, we normalise it as an array with normalizeColumns
435
+ const columns = Array.from(new Set(columnArgs.map(normalizeColumns).flat()));
436
+
437
+ const featureKeys = Object.keys(features[0]);
438
+
439
+ const invalidColumns = columns.filter(
440
+ (column) => !featureKeys.includes(column)
441
+ );
442
+
443
+ if (invalidColumns.length) {
444
+ throw new InvalidColumnError(
445
+ `Missing column(s): ${invalidColumns.join(', ')}`
446
+ );
447
+ }
448
+ }
449
+
450
+ function normalizeColumns(columns: string | string[]): string[] {
451
+ return Array.isArray(columns)
452
+ ? columns
453
+ : typeof columns === 'string'
454
+ ? [columns]
455
+ : [];
456
+ }