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