@carto/api-client 0.4.7-0 → 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.
- package/CHANGELOG.md +17 -1
- package/build/api/carto-api-error.d.ts +26 -0
- package/build/api/endpoints.d.ts +24 -0
- package/build/api/index.d.ts +5 -0
- package/build/api/query.d.ts +3 -0
- package/build/api/request-with-parameters.d.ts +10 -0
- package/build/api-client.cjs +2910 -2741
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +3718 -0
- package/build/api-client.modern.js.map +1 -0
- package/build/client.d.ts +14 -0
- package/build/constants-internal.d.ts +26 -0
- package/build/constants.d.ts +53 -0
- package/build/deck/get-data-filter-extension-props.d.ts +28 -0
- package/build/deck/index.d.ts +1 -0
- package/build/filters/Filter.d.ts +25 -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/filters.d.ts +39 -0
- package/build/geo.d.ts +19 -0
- package/build/index.d.ts +16 -0
- package/build/models/common.d.ts +28 -0
- package/build/models/index.d.ts +3 -0
- package/build/models/model.d.ts +37 -0
- 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/base-source.d.ts +4 -0
- package/build/sources/boundary-query-source.d.ts +10 -0
- package/build/sources/boundary-table-source.d.ts +8 -0
- package/build/sources/h3-query-source.d.ts +5 -0
- package/build/sources/h3-table-source.d.ts +5 -0
- package/build/sources/h3-tileset-source.d.ts +4 -0
- package/build/sources/index.d.ts +26 -0
- package/build/sources/quadbin-query-source.d.ts +5 -0
- package/build/sources/quadbin-table-source.d.ts +5 -0
- package/build/sources/quadbin-tileset-source.d.ts +4 -0
- package/build/sources/raster-source.d.ts +4 -0
- package/build/sources/types.d.ts +366 -0
- package/build/sources/vector-query-source.d.ts +5 -0
- package/build/sources/vector-table-source.d.ts +5 -0
- package/build/sources/vector-tileset-source.d.ts +4 -0
- package/build/spatial-index.d.ts +14 -0
- package/build/types-internal.d.ts +56 -0
- package/build/types.d.ts +140 -0
- 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/utils.d.ts +32 -0
- package/build/widget-sources/index.d.ts +6 -0
- package/build/widget-sources/types.d.ts +162 -0
- package/build/widget-sources/widget-query-source.d.ts +34 -0
- package/build/widget-sources/widget-remote-source.d.ts +18 -0
- package/build/widget-sources/widget-source.d.ts +74 -0
- package/build/widget-sources/widget-table-source.d.ts +34 -0
- package/build/widget-sources/widget-tileset-source.d.ts +75 -0
- package/package.json +18 -19
- package/src/constants-internal.ts +6 -0
- package/src/deck/get-data-filter-extension-props.ts +27 -9
- package/src/filters/tileFeatures.ts +0 -1
- package/src/global.d.ts +8 -3
- package/src/sources/h3-tileset-source.ts +6 -18
- package/src/sources/quadbin-tileset-source.ts +6 -18
- package/src/sources/types.ts +0 -6
- package/src/sources/vector-tileset-source.ts +6 -19
- package/src/widget-sources/types.ts +2 -0
- package/src/widget-sources/widget-remote-source.ts +16 -42
- package/src/widget-sources/widget-source.ts +40 -12
- package/src/widget-sources/widget-tileset-source.ts +341 -196
- package/build/api-client.d.cts +0 -1389
- package/build/api-client.d.ts +0 -1389
- package/build/api-client.js +0 -3676
- package/build/api-client.js.map +0 -1
- package/build/worker.d.ts +0 -2
- package/build/worker.js +0 -1949
- package/build/worker.js.map +0 -1
- package/src/widget-sources/widget-tileset-source-impl.ts +0 -417
- package/src/workers/constants.ts +0 -13
- package/src/workers/types.ts +0 -19
- package/src/workers/widget-tileset-worker.ts +0 -40
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
2
|
+
import {TilesetSourceOptions} from '../sources/index.js';
|
|
3
|
+
import type {ModelSource} from '../models/index.js';
|
|
1
4
|
import {
|
|
2
5
|
CategoryRequestOptions,
|
|
3
6
|
CategoryResponse,
|
|
@@ -15,15 +18,33 @@ import {
|
|
|
15
18
|
TimeSeriesRequestOptions,
|
|
16
19
|
TimeSeriesResponse,
|
|
17
20
|
} from './types.js';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
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';
|
|
20
39
|
import {FeatureCollection} from 'geojson';
|
|
40
|
+
import {SpatialDataType} from '../sources/types.js';
|
|
21
41
|
import {WidgetSource, WidgetSourceProps} from './widget-source.js';
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
27
48
|
|
|
28
49
|
export type WidgetTilesetSourceProps = WidgetSourceProps &
|
|
29
50
|
Omit<TilesetSourceOptions, 'filters'> & {
|
|
@@ -56,176 +77,62 @@ export type WidgetTilesetSourceResult = {widgetSource: WidgetTilesetSource};
|
|
|
56
77
|
* ```
|
|
57
78
|
*/
|
|
58
79
|
export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps> {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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();
|
|
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
|
+
};
|
|
86
95
|
}
|
|
87
96
|
|
|
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
|
-
onAbort();
|
|
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
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/DOMException#aborterror
|
|
161
|
-
const abortError = new Error(signal!.reason);
|
|
162
|
-
abortError.name = 'AbortError';
|
|
163
|
-
reject!(abortError);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
worker.addEventListener('message', onMessage);
|
|
167
|
-
signal?.addEventListener('abort', onAbort);
|
|
168
|
-
|
|
169
|
-
// Send the task to the worker, creating a Promise to resolve/reject later.
|
|
170
|
-
const promise = new Promise<T>((_resolve, _reject) => {
|
|
171
|
-
resolve = _resolve;
|
|
172
|
-
reject = _reject;
|
|
173
|
-
|
|
174
|
-
worker.postMessage({
|
|
175
|
-
requestId,
|
|
176
|
-
method,
|
|
177
|
-
params,
|
|
178
|
-
} as WorkerRequest);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// Whether the task completes, fails, or aborts: clean up afterward.
|
|
182
|
-
void promise.finally(() => {
|
|
183
|
-
worker.removeEventListener('message', onMessage);
|
|
184
|
-
signal?.removeEventListener('abort', onAbort);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
return promise;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/////////////////////////////////////////////////////////////////////////////
|
|
191
|
-
// DATA LOADING
|
|
192
|
-
|
|
193
97
|
/**
|
|
194
98
|
* Loads features as a list of tiles (typically provided by deck.gl).
|
|
195
99
|
* After tiles are loaded, {@link extractTileFeatures} must be called
|
|
196
100
|
* before computing statistics on the tiles.
|
|
197
101
|
*/
|
|
198
102
|
loadTiles(tiles: unknown[]) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const worker = this._getWorker();
|
|
204
|
-
|
|
205
|
-
tiles = (tiles as Tile[]).map(({id, bbox, data}) => ({
|
|
206
|
-
id,
|
|
207
|
-
bbox,
|
|
208
|
-
data,
|
|
209
|
-
}));
|
|
210
|
-
|
|
211
|
-
worker.postMessage({
|
|
212
|
-
method: Method.LOAD_TILES,
|
|
213
|
-
params: [tiles],
|
|
214
|
-
} as WorkerRequest);
|
|
103
|
+
this._tiles = tiles as Tile[];
|
|
104
|
+
this._features.length = 0;
|
|
215
105
|
}
|
|
216
106
|
|
|
217
107
|
/** Configures options used to extract features from tiles. */
|
|
218
108
|
setTileFeatureExtractOptions(options: TileFeatureExtractOptions) {
|
|
219
|
-
|
|
220
|
-
|
|
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;
|
|
221
123
|
}
|
|
222
124
|
|
|
223
|
-
|
|
125
|
+
this._features = tileFeatures({
|
|
126
|
+
tiles: this._tiles,
|
|
127
|
+
tileFormat: this.props.tileFormat,
|
|
128
|
+
...this._tileFeatureExtractOptions,
|
|
224
129
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
130
|
+
spatialFilter,
|
|
131
|
+
spatialDataColumn: this.props.spatialDataColumn,
|
|
132
|
+
spatialDataType: this.props.spatialDataType,
|
|
228
133
|
});
|
|
134
|
+
|
|
135
|
+
prevInputs.spatialFilter = spatialFilter;
|
|
229
136
|
}
|
|
230
137
|
|
|
231
138
|
/**
|
|
@@ -240,72 +147,310 @@ export class WidgetTilesetSource extends WidgetSource<WidgetTilesetSourceProps>
|
|
|
240
147
|
geojson: FeatureCollection;
|
|
241
148
|
spatialFilter: SpatialFilter;
|
|
242
149
|
}) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
worker.postMessage({
|
|
250
|
-
method: Method.LOAD_GEOJSON,
|
|
251
|
-
params: [{geojson, spatialFilter}],
|
|
252
|
-
} as WorkerRequest);
|
|
150
|
+
this._features = geojsonFeatures({
|
|
151
|
+
geojson,
|
|
152
|
+
spatialFilter,
|
|
153
|
+
...this._tileFeatureExtractOptions,
|
|
154
|
+
});
|
|
155
|
+
this._tileFeatureExtractPreviousInputs.spatialFilter = spatialFilter;
|
|
253
156
|
}
|
|
254
157
|
|
|
255
|
-
/////////////////////////////////////////////////////////////////////////////
|
|
256
|
-
// WIDGETS API
|
|
257
|
-
|
|
258
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
259
158
|
override async getFeatures(): Promise<FeaturesResponse> {
|
|
260
159
|
throw new Error('getFeatures not supported for tilesets');
|
|
261
160
|
}
|
|
262
161
|
|
|
263
162
|
async getFormula({
|
|
264
|
-
|
|
265
|
-
|
|
163
|
+
column = '*',
|
|
164
|
+
operation = 'count',
|
|
165
|
+
joinOperation,
|
|
166
|
+
filters,
|
|
167
|
+
filterOwner,
|
|
168
|
+
spatialFilter,
|
|
266
169
|
}: FormulaRequestOptions): Promise<FormulaResponse> {
|
|
267
|
-
|
|
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
|
+
};
|
|
268
193
|
}
|
|
269
194
|
|
|
270
195
|
override async getHistogram({
|
|
271
|
-
|
|
272
|
-
|
|
196
|
+
operation = 'count',
|
|
197
|
+
ticks,
|
|
198
|
+
column,
|
|
199
|
+
joinOperation,
|
|
200
|
+
filters,
|
|
201
|
+
filterOwner,
|
|
202
|
+
spatialFilter,
|
|
273
203
|
}: HistogramRequestOptions): Promise<HistogramResponse> {
|
|
274
|
-
|
|
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
|
+
});
|
|
275
223
|
}
|
|
276
224
|
|
|
277
225
|
override async getCategories({
|
|
278
|
-
|
|
279
|
-
|
|
226
|
+
column,
|
|
227
|
+
operation = 'count',
|
|
228
|
+
operationColumn,
|
|
229
|
+
joinOperation,
|
|
230
|
+
filters,
|
|
231
|
+
filterOwner,
|
|
232
|
+
spatialFilter,
|
|
280
233
|
}: CategoryRequestOptions): Promise<CategoryResponse> {
|
|
281
|
-
|
|
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 || [];
|
|
282
255
|
}
|
|
283
256
|
|
|
284
257
|
override async getScatter({
|
|
285
|
-
|
|
286
|
-
|
|
258
|
+
xAxisColumn,
|
|
259
|
+
yAxisColumn,
|
|
260
|
+
xAxisJoinOperation,
|
|
261
|
+
yAxisJoinOperation,
|
|
262
|
+
filters,
|
|
263
|
+
filterOwner,
|
|
264
|
+
spatialFilter,
|
|
287
265
|
}: ScatterRequestOptions): Promise<ScatterResponse> {
|
|
288
|
-
|
|
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
|
+
});
|
|
289
285
|
}
|
|
290
286
|
|
|
291
287
|
override async getTable({
|
|
292
|
-
|
|
293
|
-
|
|
288
|
+
columns,
|
|
289
|
+
searchFilterColumn,
|
|
290
|
+
searchFilterText,
|
|
291
|
+
sortBy,
|
|
292
|
+
sortDirection,
|
|
293
|
+
sortByColumnType,
|
|
294
|
+
offset = 0,
|
|
295
|
+
limit = 10,
|
|
296
|
+
filters,
|
|
297
|
+
filterOwner,
|
|
298
|
+
spatialFilter,
|
|
294
299
|
}: TableRequestOptions): Promise<TableResponse> {
|
|
295
|
-
|
|
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;
|
|
296
346
|
}
|
|
297
347
|
|
|
298
348
|
override async getTimeSeries({
|
|
299
|
-
|
|
300
|
-
|
|
349
|
+
column,
|
|
350
|
+
stepSize,
|
|
351
|
+
operation,
|
|
352
|
+
operationColumn,
|
|
353
|
+
joinOperation,
|
|
354
|
+
filters,
|
|
355
|
+
filterOwner,
|
|
356
|
+
spatialFilter,
|
|
301
357
|
}: TimeSeriesRequestOptions): Promise<TimeSeriesResponse> {
|
|
302
|
-
|
|
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};
|
|
303
381
|
}
|
|
304
382
|
|
|
305
383
|
override async getRange({
|
|
306
|
-
|
|
307
|
-
|
|
384
|
+
column,
|
|
385
|
+
filters,
|
|
386
|
+
filterOwner,
|
|
387
|
+
spatialFilter,
|
|
308
388
|
}: RangeRequestOptions): Promise<RangeResponse> {
|
|
309
|
-
|
|
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
|
+
);
|
|
310
447
|
}
|
|
311
448
|
}
|
|
449
|
+
|
|
450
|
+
function normalizeColumns(columns: string | string[]): string[] {
|
|
451
|
+
return Array.isArray(columns)
|
|
452
|
+
? columns
|
|
453
|
+
: typeof columns === 'string'
|
|
454
|
+
? [columns]
|
|
455
|
+
: [];
|
|
456
|
+
}
|