@carto/api-client 0.5.15-alpha.raster-5 → 0.5.16

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/src/index.ts CHANGED
@@ -2,13 +2,7 @@ export * from './client.js';
2
2
  export * from './constants.js';
3
3
  export * from './deck/index.js';
4
4
  export * from './fetch-map/index.js';
5
- export {
6
- createVecExprEvaluator as _createVecExprEvaluator,
7
- evaluateVecExpr as _evaluateVecExpr,
8
- validateVecExprSyntax as _validateVecExprSyntax,
9
- type VecExprResult as _VecExprResult,
10
- ErrorCode as _ErrorCode,
11
- } from './fetch-map/vec-expr-evaluator.js';
5
+ export type {LayerDescriptor, LayerType} from './fetch-map/index.js';
12
6
  export * from './filters.js';
13
7
  export * from './geo.js';
14
8
  export * from './sources/index.js';
@@ -24,6 +24,7 @@ const AVAILABLE_MODELS = [
24
24
  'range',
25
25
  'scatterplot',
26
26
  'table',
27
+ 'aggregations',
27
28
  ] as const;
28
29
 
29
30
  export type Model = (typeof AVAILABLE_MODELS)[number];
@@ -97,3 +97,15 @@ export type {
97
97
  VectorTilesetSourceOptions,
98
98
  VectorTilesetSourceResponse,
99
99
  } from './vector-tileset-source.js';
100
+
101
+ export {trajectoryQuerySource} from './trajectory-query-source.js';
102
+ export type {
103
+ TrajectoryQuerySourceOptions,
104
+ TrajectoryQuerySourceResponse,
105
+ } from './trajectory-query-source.js';
106
+
107
+ export {trajectoryTableSource} from './trajectory-table-source.js';
108
+ export type {
109
+ TrajectoryTableSourceOptions,
110
+ TrajectoryTableSourceResponse,
111
+ } from './trajectory-table-source.js';
@@ -0,0 +1,101 @@
1
+ import {
2
+ DEFAULT_GEO_COLUMN,
3
+ DEFAULT_TILE_RESOLUTION,
4
+ } from '../constants-internal.js';
5
+ import {
6
+ WidgetQuerySource,
7
+ type RangeResponse,
8
+ type WidgetQuerySourceResult,
9
+ } from '../widget-sources/index.js';
10
+ import {baseSource} from './base-source.js';
11
+ import type {
12
+ SourceOptions,
13
+ QuerySourceOptions,
14
+ SpatialDataType,
15
+ TilejsonResult,
16
+ ColumnsOption,
17
+ } from './types.js';
18
+
19
+ export type TrajectoryQuerySourceOptions = SourceOptions &
20
+ QuerySourceOptions &
21
+ ColumnsOption & {
22
+ /** Column name containing the trajectory identifier */
23
+ trajectoryIdColumn: string;
24
+ /** Column name containing the timestamp */
25
+ timestampColumn: string;
26
+ };
27
+
28
+ type UrlParameters = {
29
+ columns?: string;
30
+ spatialDataType: SpatialDataType;
31
+ spatialDataColumn?: string;
32
+ tileResolution?: string;
33
+ q: string;
34
+ queryParameters?: Record<string, unknown> | unknown[];
35
+ aggregationExp?: string;
36
+ trajectoryIdColumn: string;
37
+ timestampColumn: string;
38
+ };
39
+
40
+ export type TrajectoryQuerySourceResponse = TilejsonResult &
41
+ WidgetQuerySourceResult & {
42
+ timestampRange: RangeResponse;
43
+ };
44
+
45
+ export const trajectoryQuerySource = async function (
46
+ options: TrajectoryQuerySourceOptions
47
+ ): Promise<TrajectoryQuerySourceResponse> {
48
+ const {
49
+ columns,
50
+ spatialDataColumn = DEFAULT_GEO_COLUMN,
51
+ sqlQuery,
52
+ tileResolution = DEFAULT_TILE_RESOLUTION,
53
+ queryParameters,
54
+ aggregationExp,
55
+ trajectoryIdColumn,
56
+ timestampColumn,
57
+ } = options;
58
+
59
+ const spatialDataType = 'trajectory';
60
+
61
+ const urlParameters: UrlParameters = {
62
+ spatialDataColumn,
63
+ spatialDataType,
64
+ tileResolution: tileResolution.toString(),
65
+ q: sqlQuery,
66
+ trajectoryIdColumn,
67
+ timestampColumn,
68
+ };
69
+
70
+ if (columns) {
71
+ urlParameters.columns = columns.join(',');
72
+ }
73
+ if (queryParameters) {
74
+ urlParameters.queryParameters = queryParameters;
75
+ }
76
+ if (aggregationExp) {
77
+ urlParameters.aggregationExp = aggregationExp;
78
+ }
79
+
80
+ const result = await baseSource<UrlParameters>(
81
+ 'query',
82
+ options,
83
+ urlParameters
84
+ );
85
+
86
+ const widgetSource = new WidgetQuerySource({
87
+ ...options,
88
+ // NOTE: Parameters with default values above must be explicitly passed here.
89
+ spatialDataColumn,
90
+ spatialDataType,
91
+ tileResolution,
92
+ });
93
+
94
+ const timestampRange = await widgetSource.getRange({column: timestampColumn});
95
+
96
+ return {
97
+ ...result,
98
+ widgetSource,
99
+ timestampRange,
100
+ };
101
+ };
@@ -0,0 +1,96 @@
1
+ import {
2
+ DEFAULT_GEO_COLUMN,
3
+ DEFAULT_TILE_RESOLUTION,
4
+ } from '../constants-internal.js';
5
+ import {
6
+ WidgetTableSource,
7
+ type RangeResponse,
8
+ type WidgetTableSourceResult,
9
+ } from '../widget-sources/index.js';
10
+ import {baseSource} from './base-source.js';
11
+ import type {
12
+ ColumnsOption,
13
+ SourceOptions,
14
+ SpatialDataType,
15
+ TableSourceOptions,
16
+ TilejsonResult,
17
+ } from './types.js';
18
+
19
+ export type TrajectoryTableSourceOptions = SourceOptions &
20
+ TableSourceOptions &
21
+ ColumnsOption & {
22
+ /** Column name containing the trajectory identifier */
23
+ trajectoryIdColumn: string;
24
+ /** Column name containing the timestamp */
25
+ timestampColumn: string;
26
+ };
27
+
28
+ type UrlParameters = {
29
+ columns?: string;
30
+ spatialDataType: SpatialDataType;
31
+ spatialDataColumn?: string;
32
+ tileResolution?: string;
33
+ name: string;
34
+ aggregationExp?: string;
35
+ trajectoryIdColumn: string;
36
+ timestampColumn: string;
37
+ };
38
+
39
+ export type TrajectoryTableSourceResponse = TilejsonResult &
40
+ WidgetTableSourceResult & {
41
+ timestampRange: RangeResponse;
42
+ };
43
+
44
+ export const trajectoryTableSource = async function (
45
+ options: TrajectoryTableSourceOptions
46
+ ): Promise<TrajectoryTableSourceResponse> {
47
+ const {
48
+ columns,
49
+ spatialDataColumn = DEFAULT_GEO_COLUMN,
50
+ tableName,
51
+ tileResolution = DEFAULT_TILE_RESOLUTION,
52
+ aggregationExp,
53
+ trajectoryIdColumn,
54
+ timestampColumn,
55
+ } = options;
56
+
57
+ const spatialDataType = 'trajectory';
58
+
59
+ const urlParameters: UrlParameters = {
60
+ name: tableName,
61
+ spatialDataColumn,
62
+ spatialDataType,
63
+ tileResolution: tileResolution.toString(),
64
+ trajectoryIdColumn,
65
+ timestampColumn,
66
+ };
67
+
68
+ if (columns) {
69
+ urlParameters.columns = columns.join(',');
70
+ }
71
+ if (aggregationExp) {
72
+ urlParameters.aggregationExp = aggregationExp;
73
+ }
74
+
75
+ const result = await baseSource<UrlParameters>(
76
+ 'table',
77
+ options,
78
+ urlParameters
79
+ );
80
+
81
+ const widgetSource = new WidgetTableSource({
82
+ ...options,
83
+ // NOTE: Parameters with default values above must be explicitly passed here.
84
+ spatialDataColumn,
85
+ spatialDataType,
86
+ tileResolution,
87
+ });
88
+
89
+ const timestampRange = await widgetSource.getRange({column: timestampColumn});
90
+
91
+ return {
92
+ ...result,
93
+ widgetSource,
94
+ timestampRange,
95
+ };
96
+ };
@@ -211,7 +211,7 @@ export type ColumnsOption = {
211
211
  columns?: string[];
212
212
  };
213
213
 
214
- export type SpatialDataType = 'geo' | 'h3' | 'quadbin';
214
+ export type SpatialDataType = 'geo' | 'h3' | 'quadbin' | 'trajectory';
215
215
 
216
216
  /**
217
217
  * Strategy used for covering spatial filter geometry with spatial indexes.
@@ -292,64 +292,14 @@ export interface Tilestats {
292
292
 
293
293
  export interface Layer {
294
294
  layer: string;
295
- /** Number of features in the layer. */
296
295
  count: number;
297
-
298
- /** Number of attributes in the layer. */
299
296
  attributeCount: number;
300
297
  attributes: Attribute[];
301
-
302
- /** Type of geometry as in geojson geometry type (Point, LineString, Polygon, etc.) */
303
- geometry?: string;
304
- }
305
-
306
- export interface AttributeCategoryItem {
307
- category: string;
308
- frequency: number;
309
- }
310
-
311
- /**
312
- * Quantiles by number of buckets.
313
- *
314
- * Example:
315
- * ```ts
316
- * {
317
- * // for 3 buckets, first 1/3 of items lies in range [min, 20], second 1/3 is in [20, 40], and last 1/3 is in [40, max]
318
- * 3: [20, 40],
319
- * 4: [20, 30, 50], for 4 buckets ...
320
- * }
321
- * ```
322
- */
323
- export interface QuantileStats {
324
- [bucketCount: number]: number[];
325
298
  }
326
299
 
327
300
  export interface Attribute {
328
- /**
329
- * String, Number, Timestamp, Boolean
330
- */
331
- type: string;
332
-
333
- /**
334
- * Attribute name.
335
- */
336
301
  attribute: string;
337
-
338
- // Stats for numeric attributes
339
- min?: number;
340
- max?: number;
341
- sum?: number;
342
-
343
- /** Quantiles by number of buckets */
344
- quantiles?:
345
- | {
346
- // Quantile stats for numeric attributes in static spatial index tilesets are enclosed in extra global object
347
- global: QuantileStats;
348
- }
349
- | QuantileStats;
350
-
351
- // Stats for string/boolean attributes
352
- categories?: AttributeCategoryItem[];
302
+ type: string;
353
303
  }
354
304
 
355
305
  export interface VectorLayer {
@@ -375,8 +325,17 @@ export type RasterMetadataBandStats = {
375
325
 
376
326
  /**
377
327
  * Quantiles by number of buckets.
328
+ *
329
+ * Example:
330
+ * ```ts
331
+ * {
332
+ * // for 3 buckets, first 1/3 of items lies in range [min, 20], second 1/3 is in [20, 40], and last 1/3 is in [40, max]
333
+ * 3: [20, 40],
334
+ * 4: [20, 30, 50], for 4 buckets ...
335
+ * }
336
+ * ```
378
337
  */
379
- quantiles?: QuantileStats;
338
+ quantiles?: Record<number, number[]>;
380
339
 
381
340
  /**
382
341
  * Top values by number of values.
@@ -207,6 +207,26 @@ export interface TimeSeriesRequestOptions extends BaseRequestOptions {
207
207
  splitByCategoryValues?: string[];
208
208
  }
209
209
 
210
+ /**
211
+ * Examples:
212
+ * * aggregations with array syntax
213
+ * * aggregations: [{column: 'pop_high', operation: 'sum', alias: 'high_pop'}, {column: 'pop_low', operation: 'avg'}]
214
+ * * aggregations with string syntax
215
+ * * aggregations: 'sum(pop_high) as high_pop, avg(pop_low) as avg_low'
216
+ *
217
+ * Options for {@link WidgetRemoteSource#getAggregations}.
218
+ */
219
+ export interface AggregationsRequestOptions extends BaseRequestOptions {
220
+ /** Aggregations to compute. Can be an array of objects or a SQL string expression. */
221
+ aggregations:
222
+ | {
223
+ column: string;
224
+ operation: Exclude<AggregationType, 'custom'>;
225
+ alias: string;
226
+ }[]
227
+ | string;
228
+ }
229
+
210
230
  /** @experimental */
211
231
  export type ExtentRequestOptions = BaseRequestOptions;
212
232
 
@@ -241,7 +261,10 @@ export type CategoryResponseRaw = {
241
261
  };
242
262
 
243
263
  /** Response from {@link WidgetRemoteSource#getRange}. */
244
- export type RangeResponse = {min: number; max: number} | null;
264
+ export type RangeResponse =
265
+ | {min: number; max: number}
266
+ | {min: string; max: string}
267
+ | null;
245
268
 
246
269
  /** Response from {@link WidgetRemoteSource#getTable}. */
247
270
  export type TableResponse = {
@@ -261,5 +284,10 @@ export type TimeSeriesResponse = {
261
284
  /** Response from {@link WidgetRemoteSource#getHistogram}. */
262
285
  export type HistogramResponse = number[];
263
286
 
287
+ /** Response from {@link WidgetRemoteSource#getAggregations}. */
288
+ export type AggregationsResponse = {
289
+ rows: Record<string, number | string | null>[];
290
+ };
291
+
264
292
  /** @experimental */
265
293
  export type ExtentResponse = {bbox: BBox};
@@ -1,5 +1,7 @@
1
1
  import {executeModel, type ModelSource} from '../models/index.js';
2
2
  import type {
3
+ AggregationsRequestOptions,
4
+ AggregationsResponse,
3
5
  CategoryRequestOptions,
4
6
  CategoryResponse,
5
7
  ExtentRequestOptions,
@@ -394,6 +396,34 @@ export abstract class WidgetRemoteSource<
394
396
  }));
395
397
  }
396
398
 
399
+ async getAggregations(
400
+ options: AggregationsRequestOptions
401
+ ): Promise<AggregationsResponse> {
402
+ const {
403
+ signal,
404
+ filters = this.props.filters,
405
+ filterOwner,
406
+ spatialFilter,
407
+ spatialFiltersMode,
408
+ aggregations,
409
+ } = options;
410
+
411
+ return executeModel({
412
+ model: 'aggregations',
413
+ source: {
414
+ ...this.getModelSource(filters, filterOwner),
415
+ spatialFiltersMode,
416
+ spatialFilter,
417
+ },
418
+ params: {
419
+ aggregations,
420
+ },
421
+ opts: {signal, headers: this.props.headers},
422
+ }).then((res: AggregationsResponse) => ({
423
+ rows: res.rows.map((row) => normalizeObjectKeys(row)),
424
+ }));
425
+ }
426
+
397
427
  /** @experimental */
398
428
  async getExtent(options: ExtentRequestOptions = {}): Promise<ExtentResponse> {
399
429
  const {signal, filters = this.props.filters, filterOwner} = options;
@@ -1,4 +1,6 @@
1
1
  import type {
2
+ AggregationsRequestOptions,
3
+ AggregationsResponse,
2
4
  CategoryRequestOptions,
3
5
  CategoryResponse,
4
6
  ExtentRequestOptions,
@@ -127,6 +129,15 @@ export abstract class WidgetSource<
127
129
  options: TimeSeriesRequestOptions
128
130
  ): Promise<TimeSeriesResponse>;
129
131
 
132
+ /**
133
+ * Returns multiple aggregated values computed over matching data. Suitable
134
+ * for aggregated statistics from pivoted tables, such as H3 tables with
135
+ * pre-computed aggregations across multiple columns.
136
+ */
137
+ abstract getAggregations(
138
+ options: AggregationsRequestOptions
139
+ ): Promise<AggregationsResponse>;
140
+
130
141
  /** @experimental */
131
142
  abstract getExtent(options?: ExtentRequestOptions): Promise<ExtentResponse>;
132
143
  }
@@ -1,5 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/require-await */
2
2
  import type {
3
+ AggregationsRequestOptions,
4
+ AggregationsResponse,
3
5
  CategoryRequestOptions,
4
6
  CategoryResponse,
5
7
  ExtentResponse,
@@ -148,6 +150,8 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
148
150
  }
149
151
 
150
152
  const targetOperation = aggregationFunctions[operation];
153
+ assert(targetOperation, `Unsupported aggregation operation: ${operation}`);
154
+
151
155
  return {
152
156
  value: targetOperation(filteredFeatures, column, joinOperation),
153
157
  };
@@ -391,6 +395,51 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
391
395
  };
392
396
  }
393
397
 
398
+ async getAggregations({
399
+ aggregations,
400
+ filters,
401
+ filterOwner,
402
+ spatialFilter,
403
+ }: AggregationsRequestOptions): Promise<AggregationsResponse> {
404
+ const filteredFeatures = this._getFilteredFeatures(
405
+ spatialFilter,
406
+ filters,
407
+ filterOwner
408
+ );
409
+
410
+ if (!this._features.length) {
411
+ return {rows: []};
412
+ }
413
+
414
+ // SQL aggregations require remote execution, and are not supported for tilesets.
415
+ assert(
416
+ typeof aggregations !== 'string',
417
+ 'Unsupported tileset SQL aggregation'
418
+ );
419
+
420
+ // Handle array-based aggregations
421
+ const result: Record<string, number> = {};
422
+ const usedAliases = new Set<string>();
423
+
424
+ for (const {column, operation, alias} of aggregations) {
425
+ // Column is required except when operation is 'count'.
426
+ if ((column && column !== '*') || operation !== AggregationTypes.Count) {
427
+ assertColumn(this._features, column);
428
+ }
429
+
430
+ const aliasKey = alias.toLowerCase();
431
+ assert(!usedAliases.has(aliasKey), `Duplicate alias: ${aliasKey}`);
432
+ usedAliases.add(aliasKey);
433
+
434
+ const targetOperation = aggregationFunctions[operation];
435
+ assert(targetOperation, `Unsupported operation: ${operation}`);
436
+
437
+ result[alias] = targetOperation(filteredFeatures, column);
438
+ }
439
+
440
+ return {rows: [result]};
441
+ }
442
+
394
443
  /** @experimental */
395
444
  async getExtent(): Promise<ExtentResponse> {
396
445
  return Promise.reject(new Error('not implemented'));
@@ -1,4 +1,6 @@
1
1
  import type {
2
+ AggregationsRequestOptions,
3
+ AggregationsResponse,
2
4
  CategoryRequestOptions,
3
5
  CategoryResponse,
4
6
  ExtentResponse,
@@ -326,6 +328,17 @@ export class WidgetTilesetSource<
326
328
  return this._executeWorkerMethod(Method.GET_RANGE, [options], signal);
327
329
  }
328
330
 
331
+ async getAggregations({
332
+ signal,
333
+ ...options
334
+ }: AggregationsRequestOptions): Promise<AggregationsResponse> {
335
+ return this._executeWorkerMethod(
336
+ Method.GET_AGGREGATIONS,
337
+ [options],
338
+ signal
339
+ );
340
+ }
341
+
329
342
  /** @experimental */
330
343
  async getExtent(): Promise<ExtentResponse> {
331
344
  return Promise.resolve({
@@ -10,4 +10,5 @@ export enum Method {
10
10
  GET_TABLE = 'getTable',
11
11
  GET_TIME_SERIES = 'getTimeSeries',
12
12
  GET_RANGE = 'getRange',
13
+ GET_AGGREGATIONS = 'getAggregations',
13
14
  }