@carto/api-client 0.5.13 → 0.5.15-alpha.raster-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 +8 -0
- package/build/api-client.cjs +829 -56
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.d.cts +85 -3
- package/build/api-client.d.ts +85 -3
- package/build/api-client.js +817 -54
- package/build/api-client.js.map +1 -1
- package/build/worker-compat.js +4 -0
- package/build/worker-compat.js.map +1 -1
- package/build/worker.js +4 -0
- package/build/worker.js.map +1 -1
- package/package.json +6 -4
- package/src/fetch-map/index.ts +1 -0
- package/src/fetch-map/layer-map.ts +40 -12
- package/src/fetch-map/parse-map.ts +153 -70
- package/src/fetch-map/raster-layer.ts +534 -0
- package/src/fetch-map/types.ts +14 -0
- package/src/fetch-map/vec-expr-evaluator.ts +386 -0
- package/src/index.ts +12 -1
- package/src/sources/h3-tileset-source.ts +1 -0
- package/src/sources/quadbin-tileset-source.ts +1 -0
- package/src/sources/raster-source.ts +1 -0
- package/src/sources/vector-tileset-source.ts +1 -0
- package/src/widget-sources/types.ts +7 -0
- package/src/widget-sources/widget-remote-source.ts +64 -0
- package/src/widget-sources/widget-source.ts +5 -0
- package/src/widget-sources/widget-tileset-source-impl.ts +6 -0
- package/src/widget-sources/widget-tileset-source.ts +13 -1
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import jsep from 'jsep';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create vector expresion evaluator.
|
|
5
|
+
*
|
|
6
|
+
* Used to calculate vector expressions, such as `(band_1 * 3) + band_2/2`,
|
|
7
|
+
* where `band_1` and `band_2` are arrays or typed arrays.
|
|
8
|
+
*
|
|
9
|
+
* Note that all vector operations are element-wise, in paricular `band_1 * band_2`
|
|
10
|
+
* is not "mathematical" dot or cross product, but just element-wise multiplication.
|
|
11
|
+
*
|
|
12
|
+
* Based on:
|
|
13
|
+
* - Copyright (c) 2013 Stephen Oney, http://jsep.from.so/, MIT License
|
|
14
|
+
* - Copyright (c) 2023 Don McCurdy, https://github.com/donmccurdy/expression-eval, MIT License
|
|
15
|
+
*/
|
|
16
|
+
export function createVecExprEvaluator(
|
|
17
|
+
expression: string | jsep.Expression
|
|
18
|
+
): VecExprEvaluator | null {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = compile(expression);
|
|
21
|
+
const evalFun = (context: Record<string, VecExprResult>) =>
|
|
22
|
+
evaluate(parsed, context);
|
|
23
|
+
evalFun.symbols = getSymbols(parsed);
|
|
24
|
+
return evalFun as VecExprEvaluator;
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function evaluateVecExpr(
|
|
31
|
+
expression: string | jsep.Expression,
|
|
32
|
+
context: Record<string, VecExprResult>
|
|
33
|
+
) {
|
|
34
|
+
try {
|
|
35
|
+
return createVecExprEvaluator(expression)?.(context);
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export enum ErrorCode {
|
|
42
|
+
InvalidSyntax,
|
|
43
|
+
UnknownIdentifier,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type ValidationResult = {
|
|
47
|
+
valid: boolean;
|
|
48
|
+
errorCode?: ErrorCode;
|
|
49
|
+
errorMessage?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export function validateVecExprSyntax(
|
|
53
|
+
expression: string | jsep.Expression,
|
|
54
|
+
context: Record<string, unknown>
|
|
55
|
+
): ValidationResult {
|
|
56
|
+
let parsed: jsep.Expression;
|
|
57
|
+
try {
|
|
58
|
+
parsed = compile(expression);
|
|
59
|
+
} catch (e: any) {
|
|
60
|
+
return {
|
|
61
|
+
valid: false,
|
|
62
|
+
errorCode: ErrorCode.InvalidSyntax,
|
|
63
|
+
errorMessage: e && 'message' in e ? String(e.message) : String(e),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return validate(parsed, context);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function createValidationContext(
|
|
70
|
+
validSymbols: string[]
|
|
71
|
+
): Record<string, unknown> {
|
|
72
|
+
return validSymbols.reduce(
|
|
73
|
+
(acc, symbol) => {
|
|
74
|
+
acc[symbol] = 1;
|
|
75
|
+
return acc;
|
|
76
|
+
},
|
|
77
|
+
{} as Record<string, unknown>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type VecExprVecLike =
|
|
82
|
+
| number[]
|
|
83
|
+
| Float32Array
|
|
84
|
+
| Float64Array
|
|
85
|
+
| Uint8Array
|
|
86
|
+
| Int8Array
|
|
87
|
+
| Int32Array
|
|
88
|
+
| Uint32Array
|
|
89
|
+
| Uint16Array
|
|
90
|
+
| Int16Array;
|
|
91
|
+
|
|
92
|
+
export type VecExprResult = number | VecExprVecLike;
|
|
93
|
+
|
|
94
|
+
export type VecExprEvaluator = {
|
|
95
|
+
(context: object): VecExprResult;
|
|
96
|
+
|
|
97
|
+
symbols?: string[];
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function createResultArray(
|
|
101
|
+
typeTemplate: VecExprVecLike,
|
|
102
|
+
length: number = typeTemplate.length
|
|
103
|
+
): number[] {
|
|
104
|
+
return new Array(length);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isVecLike(a: unknown): a is number[] {
|
|
108
|
+
return Array.isArray(a) || ArrayBuffer.isView(a);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const createBinopVec =
|
|
112
|
+
(scalarBinOp: (a: number, b: number) => number) =>
|
|
113
|
+
(left: number[], right: number[]) => {
|
|
114
|
+
const length = Math.min(left.length, right.length);
|
|
115
|
+
const r = createResultArray(left, length);
|
|
116
|
+
for (let i = 0; i < length; i++) {
|
|
117
|
+
r[i] = scalarBinOp(left[i], right[i]);
|
|
118
|
+
}
|
|
119
|
+
return r;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const createBinopVecNum =
|
|
123
|
+
(scalarBinOp: (a: number, b: number) => number) =>
|
|
124
|
+
(left: number[], right: number) => {
|
|
125
|
+
const length = left.length;
|
|
126
|
+
const r = createResultArray(left, length);
|
|
127
|
+
for (let i = 0; i < length; i++) {
|
|
128
|
+
r[i] = scalarBinOp(left[i], right);
|
|
129
|
+
}
|
|
130
|
+
return r;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// number vec op
|
|
134
|
+
const createBinopNumVec =
|
|
135
|
+
(scalarBinOp: (a: number, b: number) => number) =>
|
|
136
|
+
(left: number, right: number[]) => {
|
|
137
|
+
const length = right.length;
|
|
138
|
+
const r = createResultArray(right, length);
|
|
139
|
+
for (let i = 0; i < length; i++) {
|
|
140
|
+
r[i] = scalarBinOp(left, right[i]);
|
|
141
|
+
}
|
|
142
|
+
return r;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const createUnopVec = (scalarUnop: (a: number) => number) => (a: number[]) => {
|
|
146
|
+
const length = a.length;
|
|
147
|
+
const r = createResultArray(a, length);
|
|
148
|
+
for (let i = 0; i < length; i++) {
|
|
149
|
+
r[i] = scalarUnop(a[i]);
|
|
150
|
+
}
|
|
151
|
+
return r;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
function mapDictValues<V, NewV>(dict: Record<string, V>, fun: (v: V) => NewV) {
|
|
155
|
+
return Object.keys(dict).reduce(
|
|
156
|
+
(acc, key) => {
|
|
157
|
+
acc[key] = fun(dict[key]);
|
|
158
|
+
return acc;
|
|
159
|
+
},
|
|
160
|
+
{} as Record<string, NewV>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const binopsNum: Record<string, (a: number, b: number) => number> = {
|
|
165
|
+
'||': (a: number, b: number) => a || b,
|
|
166
|
+
'&&': (a: number, b: number) => a && b,
|
|
167
|
+
'|': (a: number, b: number) => a | b,
|
|
168
|
+
'^': (a: number, b: number) => a ^ b,
|
|
169
|
+
'&': (a: number, b: number) => a & b,
|
|
170
|
+
'==': (a: number, b: number) => Number(a == b),
|
|
171
|
+
'!=': (a: number, b: number) => Number(a != b),
|
|
172
|
+
'===': (a: number, b: number) => Number(a === b),
|
|
173
|
+
'!==': (a: number, b: number) => Number(a !== b),
|
|
174
|
+
'<': (a: number, b: number) => Number(a < b),
|
|
175
|
+
'>': (a: number, b: number) => Number(a > b),
|
|
176
|
+
'<=': (a: number, b: number) => Number(a <= b),
|
|
177
|
+
'>=': (a: number, b: number) => Number(a >= b),
|
|
178
|
+
'<<': (a: number, b: number) => a << b,
|
|
179
|
+
'>>': (a: number, b: number) => a >> b,
|
|
180
|
+
'>>>': (a: number, b: number) => a >>> b,
|
|
181
|
+
'+': (a: number, b: number) => a + b,
|
|
182
|
+
'-': (a: number, b: number) => a - b,
|
|
183
|
+
'*': (a: number, b: number) => a * b,
|
|
184
|
+
'/': (a: number, b: number) => a / b,
|
|
185
|
+
'%': (a: number, b: number) => a % b,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const unopsNum: Record<string, (a: number) => number> = {
|
|
189
|
+
'-': (a: number) => -a,
|
|
190
|
+
'+': (a: number) => +a,
|
|
191
|
+
'~': (a: number) => ~a,
|
|
192
|
+
'!': (a: number) => Number(!a),
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const binopsVector = mapDictValues(binopsNum, createBinopVec) as Record<
|
|
196
|
+
string,
|
|
197
|
+
Binop
|
|
198
|
+
>;
|
|
199
|
+
const binopsNumVec = mapDictValues(binopsNum, createBinopNumVec) as Record<
|
|
200
|
+
string,
|
|
201
|
+
Binop
|
|
202
|
+
>;
|
|
203
|
+
const binopsVecNum = mapDictValues(binopsNum, createBinopVecNum) as Record<
|
|
204
|
+
string,
|
|
205
|
+
Binop
|
|
206
|
+
>;
|
|
207
|
+
|
|
208
|
+
const unopsVector = mapDictValues(unopsNum, createUnopVec) as Record<
|
|
209
|
+
string,
|
|
210
|
+
UnOp
|
|
211
|
+
>;
|
|
212
|
+
|
|
213
|
+
type UnOp = (a: VecExprResult) => VecExprResult;
|
|
214
|
+
type Binop = (a: VecExprResult, b: VecExprResult) => VecExprResult;
|
|
215
|
+
|
|
216
|
+
function getBinop(
|
|
217
|
+
operator: string,
|
|
218
|
+
left: VecExprResult,
|
|
219
|
+
right: VecExprResult
|
|
220
|
+
): Binop {
|
|
221
|
+
const isLeftVec = isVecLike(left);
|
|
222
|
+
const isRightVec = isVecLike(right);
|
|
223
|
+
if (isLeftVec && isRightVec) {
|
|
224
|
+
return binopsVector[operator];
|
|
225
|
+
} else if (isLeftVec) {
|
|
226
|
+
return binopsVecNum[operator];
|
|
227
|
+
} else if (isRightVec) {
|
|
228
|
+
return binopsNumVec[operator];
|
|
229
|
+
} else {
|
|
230
|
+
return binopsNum[operator] as Binop;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
type AnyExpression =
|
|
235
|
+
| jsep.ArrayExpression
|
|
236
|
+
| jsep.BinaryExpression
|
|
237
|
+
| jsep.MemberExpression
|
|
238
|
+
| jsep.CallExpression
|
|
239
|
+
| jsep.ConditionalExpression
|
|
240
|
+
| jsep.Identifier
|
|
241
|
+
| jsep.Literal
|
|
242
|
+
| jsep.ThisExpression
|
|
243
|
+
| jsep.UnaryExpression;
|
|
244
|
+
|
|
245
|
+
export function evaluate(
|
|
246
|
+
_node: jsep.Expression,
|
|
247
|
+
context: Record<string, VecExprResult>
|
|
248
|
+
): VecExprResult {
|
|
249
|
+
const node = _node as AnyExpression;
|
|
250
|
+
|
|
251
|
+
switch (node.type) {
|
|
252
|
+
case 'BinaryExpression': {
|
|
253
|
+
const left = evaluate(node.left, context);
|
|
254
|
+
const right = evaluate(node.right, context);
|
|
255
|
+
const binopFun = getBinop(node.operator, left, right);
|
|
256
|
+
|
|
257
|
+
return binopFun(left, right);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
case 'ConditionalExpression': {
|
|
261
|
+
const val = evaluate(node.test, context);
|
|
262
|
+
if (isVecLike(val)) {
|
|
263
|
+
const length = val.length;
|
|
264
|
+
const consequentVal = evaluate(node.consequent, context);
|
|
265
|
+
const alternateVal = evaluate(node.alternate, context);
|
|
266
|
+
const r = createResultArray(val);
|
|
267
|
+
for (let i = 0; i < length; i++) {
|
|
268
|
+
const entryVal = val[i] ? consequentVal : alternateVal;
|
|
269
|
+
r[i] = isVecLike(entryVal)
|
|
270
|
+
? (entryVal[i] ?? NaN)
|
|
271
|
+
: (entryVal as number);
|
|
272
|
+
}
|
|
273
|
+
return r;
|
|
274
|
+
} else {
|
|
275
|
+
return val
|
|
276
|
+
? evaluate(node.consequent, context)
|
|
277
|
+
: evaluate(node.alternate, context);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
case 'Identifier':
|
|
282
|
+
return context[node.name];
|
|
283
|
+
|
|
284
|
+
case 'Literal':
|
|
285
|
+
return node.value as number;
|
|
286
|
+
|
|
287
|
+
case 'UnaryExpression': {
|
|
288
|
+
const val = evaluate(node.argument, context);
|
|
289
|
+
const unopFun = isVecLike(val)
|
|
290
|
+
? unopsVector[node.operator]
|
|
291
|
+
: (unopsNum[node.operator] as UnOp);
|
|
292
|
+
return unopFun(val);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
default:
|
|
296
|
+
return undefined as unknown as VecExprResult;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const validResult = {valid: true};
|
|
301
|
+
|
|
302
|
+
function visit(_node: jsep.Expression, visitor: (node: AnyExpression) => void) {
|
|
303
|
+
const node = _node as AnyExpression;
|
|
304
|
+
|
|
305
|
+
visitor(node);
|
|
306
|
+
switch (node.type) {
|
|
307
|
+
case 'BinaryExpression': {
|
|
308
|
+
visit(node.left, visitor);
|
|
309
|
+
visit(node.right, visitor);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
case 'ConditionalExpression': {
|
|
314
|
+
visit(node.test, visitor);
|
|
315
|
+
visit(node.consequent, visitor);
|
|
316
|
+
visit(node.alternate, visitor);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
case 'UnaryExpression': {
|
|
321
|
+
visit(node.argument, visitor);
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const supportedExpressionTypes = [
|
|
328
|
+
'BinaryExpression',
|
|
329
|
+
'UnaryExpression',
|
|
330
|
+
'ConditionalExpression',
|
|
331
|
+
'LogicalExpression',
|
|
332
|
+
'Identifier',
|
|
333
|
+
'Literal',
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
function validate(_node: jsep.Expression, context: object): ValidationResult {
|
|
337
|
+
const node = _node as AnyExpression;
|
|
338
|
+
|
|
339
|
+
const errors: ValidationResult[] = [];
|
|
340
|
+
|
|
341
|
+
visit(node, (node) => {
|
|
342
|
+
if (!supportedExpressionTypes.includes(node.type)) {
|
|
343
|
+
errors.push({
|
|
344
|
+
valid: false,
|
|
345
|
+
errorCode: ErrorCode.InvalidSyntax,
|
|
346
|
+
errorMessage: `Not allowed`,
|
|
347
|
+
});
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (node.type === 'Identifier') {
|
|
351
|
+
if (!Object.prototype.hasOwnProperty.call(context, node.name)) {
|
|
352
|
+
return errors.push({
|
|
353
|
+
valid: false,
|
|
354
|
+
errorCode: ErrorCode.UnknownIdentifier,
|
|
355
|
+
errorMessage: `"${node.name}" not found`,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (node.type === 'Literal') {
|
|
360
|
+
// we actually support only numbers
|
|
361
|
+
if (typeof node.value !== 'number') {
|
|
362
|
+
return errors.push({
|
|
363
|
+
valid: false,
|
|
364
|
+
errorCode: ErrorCode.InvalidSyntax,
|
|
365
|
+
errorMessage: `Only number literals are supported`,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
return errors.length ? errors[0] : validResult;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function getSymbols(node: jsep.Expression): string[] {
|
|
374
|
+
const symbols = new Set<string>();
|
|
375
|
+
|
|
376
|
+
visit(node, (node) => {
|
|
377
|
+
if (node.type === 'Identifier') {
|
|
378
|
+
symbols.add(node.name);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
return Array.from(symbols);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function compile(expression: string | jsep.Expression) {
|
|
385
|
+
return jsep(expression);
|
|
386
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,18 @@ 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 type {
|
|
5
|
+
export type {
|
|
6
|
+
LayerDescriptor,
|
|
7
|
+
LayerType,
|
|
8
|
+
_getRasterTileLayerStyleProps,
|
|
9
|
+
} from './fetch-map/index.js';
|
|
10
|
+
export {
|
|
11
|
+
createVecExprEvaluator as _createVecExprEvaluator,
|
|
12
|
+
evaluateVecExpr as _evaluateVecExpr,
|
|
13
|
+
validateVecExprSyntax as _validateVecExprSyntax,
|
|
14
|
+
type VecExprResult as _VecExprResult,
|
|
15
|
+
ErrorCode as _ErrorCode,
|
|
16
|
+
} from './fetch-map/vec-expr-evaluator.js';
|
|
6
17
|
export * from './filters.js';
|
|
7
18
|
export * from './geo.js';
|
|
8
19
|
export * from './sources/index.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type {BBox} from 'geojson';
|
|
1
2
|
import type {
|
|
2
3
|
SpatialFilterPolyfillMode,
|
|
3
4
|
TileResolution,
|
|
@@ -206,6 +207,9 @@ export interface TimeSeriesRequestOptions extends BaseRequestOptions {
|
|
|
206
207
|
splitByCategoryValues?: string[];
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
/** @experimental */
|
|
211
|
+
export type ExtentRequestOptions = BaseRequestOptions;
|
|
212
|
+
|
|
209
213
|
/******************************************************************************
|
|
210
214
|
* WIDGET API RESPONSES
|
|
211
215
|
*/
|
|
@@ -256,3 +260,6 @@ export type TimeSeriesResponse = {
|
|
|
256
260
|
|
|
257
261
|
/** Response from {@link WidgetRemoteSource#getHistogram}. */
|
|
258
262
|
export type HistogramResponse = number[];
|
|
263
|
+
|
|
264
|
+
/** @experimental */
|
|
265
|
+
export type ExtentResponse = {bbox: BBox};
|
|
@@ -2,6 +2,8 @@ import {executeModel, type ModelSource} from '../models/index.js';
|
|
|
2
2
|
import type {
|
|
3
3
|
CategoryRequestOptions,
|
|
4
4
|
CategoryResponse,
|
|
5
|
+
ExtentRequestOptions,
|
|
6
|
+
ExtentResponse,
|
|
5
7
|
FeaturesRequestOptions,
|
|
6
8
|
FeaturesResponse,
|
|
7
9
|
FormulaRequestOptions,
|
|
@@ -24,6 +26,8 @@ import type {Filters} from '../types.js';
|
|
|
24
26
|
import {AggregationTypes, ApiVersion} from '../constants.js';
|
|
25
27
|
import {getApplicableFilters} from '../filters.js';
|
|
26
28
|
import {OTHERS_CATEGORY_NAME} from './constants.js';
|
|
29
|
+
import {requestWithParameters} from '../api/request-with-parameters.js';
|
|
30
|
+
import type {APIErrorContext} from '../api/carto-api-error.js';
|
|
27
31
|
|
|
28
32
|
export type WidgetRemoteSourceProps = WidgetSourceProps;
|
|
29
33
|
|
|
@@ -389,4 +393,64 @@ export abstract class WidgetRemoteSource<
|
|
|
389
393
|
categories: res.metadata?.categories,
|
|
390
394
|
}));
|
|
391
395
|
}
|
|
396
|
+
|
|
397
|
+
/** @experimental */
|
|
398
|
+
async getExtent(options: ExtentRequestOptions = {}): Promise<ExtentResponse> {
|
|
399
|
+
const {signal, filters = this.props.filters, filterOwner} = options;
|
|
400
|
+
|
|
401
|
+
const {
|
|
402
|
+
type,
|
|
403
|
+
data,
|
|
404
|
+
apiBaseUrl,
|
|
405
|
+
apiVersion,
|
|
406
|
+
connectionName,
|
|
407
|
+
spatialDataColumn,
|
|
408
|
+
spatialDataType,
|
|
409
|
+
queryParameters,
|
|
410
|
+
} = this.getModelSource(filters, filterOwner);
|
|
411
|
+
|
|
412
|
+
assert(apiVersion === ApiVersion.V3, 'Stats API requires CARTO 3+');
|
|
413
|
+
|
|
414
|
+
let url: string;
|
|
415
|
+
|
|
416
|
+
const parameters: Record<string, unknown> = {filters, spatialDataType};
|
|
417
|
+
|
|
418
|
+
if (type === 'query') {
|
|
419
|
+
url = `${apiBaseUrl}/${apiVersion}/stats/${connectionName}/${spatialDataColumn}`;
|
|
420
|
+
parameters.q = data;
|
|
421
|
+
parameters.queryParameters = queryParameters;
|
|
422
|
+
} else {
|
|
423
|
+
url = `${apiBaseUrl}/${apiVersion}/stats/${connectionName}/${data}/${spatialDataColumn}`;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const headers = {
|
|
427
|
+
Authorization: `Bearer ${this.props.accessToken}`,
|
|
428
|
+
...this.props.headers,
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const errorContext: APIErrorContext = {
|
|
432
|
+
requestType: 'Tile stats',
|
|
433
|
+
connection: connectionName,
|
|
434
|
+
type: type,
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
type StatsResponse = {
|
|
438
|
+
extent: {
|
|
439
|
+
xmin: number;
|
|
440
|
+
ymin: number;
|
|
441
|
+
xmax: number;
|
|
442
|
+
ymax: number;
|
|
443
|
+
};
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
return requestWithParameters<StatsResponse>({
|
|
447
|
+
baseUrl: url,
|
|
448
|
+
headers,
|
|
449
|
+
signal,
|
|
450
|
+
errorContext,
|
|
451
|
+
parameters,
|
|
452
|
+
}).then(({extent: {xmin, ymin, xmax, ymax}}) => ({
|
|
453
|
+
bbox: [xmin, ymin, xmax, ymax],
|
|
454
|
+
}));
|
|
455
|
+
}
|
|
392
456
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
CategoryRequestOptions,
|
|
3
3
|
CategoryResponse,
|
|
4
|
+
ExtentRequestOptions,
|
|
5
|
+
ExtentResponse,
|
|
4
6
|
FeaturesRequestOptions,
|
|
5
7
|
FeaturesResponse,
|
|
6
8
|
FormulaRequestOptions,
|
|
@@ -124,4 +126,7 @@ export abstract class WidgetSource<
|
|
|
124
126
|
abstract getTimeSeries(
|
|
125
127
|
options: TimeSeriesRequestOptions
|
|
126
128
|
): Promise<TimeSeriesResponse>;
|
|
129
|
+
|
|
130
|
+
/** @experimental */
|
|
131
|
+
abstract getExtent(options?: ExtentRequestOptions): Promise<ExtentResponse>;
|
|
127
132
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type {
|
|
3
3
|
CategoryRequestOptions,
|
|
4
4
|
CategoryResponse,
|
|
5
|
+
ExtentResponse,
|
|
5
6
|
FeaturesResponse,
|
|
6
7
|
FormulaRequestOptions,
|
|
7
8
|
FormulaResponse,
|
|
@@ -390,6 +391,11 @@ export class WidgetTilesetSourceImpl extends WidgetSource<WidgetTilesetSourcePro
|
|
|
390
391
|
};
|
|
391
392
|
}
|
|
392
393
|
|
|
394
|
+
/** @experimental */
|
|
395
|
+
async getExtent(): Promise<ExtentResponse> {
|
|
396
|
+
return Promise.reject(new Error('not implemented'));
|
|
397
|
+
}
|
|
398
|
+
|
|
393
399
|
/****************************************************************************
|
|
394
400
|
* INTERNAL
|
|
395
401
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
CategoryRequestOptions,
|
|
3
3
|
CategoryResponse,
|
|
4
|
+
ExtentResponse,
|
|
4
5
|
FeaturesResponse,
|
|
5
6
|
FormulaRequestOptions,
|
|
6
7
|
FormulaResponse,
|
|
@@ -17,7 +18,7 @@ import type {
|
|
|
17
18
|
} from './types.js';
|
|
18
19
|
import type {SpatialFilter, Tile} from '../types.js';
|
|
19
20
|
import type {TileFeatureExtractOptions} from '../filters/index.js';
|
|
20
|
-
import type {FeatureCollection} from 'geojson';
|
|
21
|
+
import type {BBox, FeatureCollection} from 'geojson';
|
|
21
22
|
import {WidgetSource, type WidgetSourceProps} from './widget-source.js';
|
|
22
23
|
import {Method} from '../workers/constants.js';
|
|
23
24
|
import type {WorkerRequest, WorkerResponse} from '../workers/types.js';
|
|
@@ -29,6 +30,10 @@ export type WidgetTilesetSourceProps = WidgetSourceProps &
|
|
|
29
30
|
Omit<TilesetSourceOptions, 'filters'> & {
|
|
30
31
|
tileFormat: TileFormat;
|
|
31
32
|
spatialDataType: SpatialDataType;
|
|
33
|
+
/**
|
|
34
|
+
* Extent of spatial data, typically from TileJSON. Does not include filters.
|
|
35
|
+
*/
|
|
36
|
+
spatialDataBounds: BBox;
|
|
32
37
|
};
|
|
33
38
|
|
|
34
39
|
export type WidgetTilesetSourceResult = {widgetSource: WidgetTilesetSource};
|
|
@@ -320,4 +325,11 @@ export class WidgetTilesetSource<
|
|
|
320
325
|
}: RangeRequestOptions): Promise<RangeResponse> {
|
|
321
326
|
return this._executeWorkerMethod(Method.GET_RANGE, [options], signal);
|
|
322
327
|
}
|
|
328
|
+
|
|
329
|
+
/** @experimental */
|
|
330
|
+
async getExtent(): Promise<ExtentResponse> {
|
|
331
|
+
return Promise.resolve({
|
|
332
|
+
bbox: this.props.spatialDataBounds,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
323
335
|
}
|