@carto/api-client 0.4.2 → 0.4.4
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/query.d.ts +1 -1
- package/build/api-client.cjs +1136 -984
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +1014 -869
- package/build/api-client.modern.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/models/model.d.ts +7 -1
- package/build/sources/boundary-query-source.d.ts +2 -1
- package/build/sources/boundary-table-source.d.ts +2 -1
- package/build/sources/h3-query-source.d.ts +2 -1
- package/build/sources/h3-table-source.d.ts +2 -1
- package/build/sources/h3-tileset-source.d.ts +2 -1
- package/build/sources/index.d.ts +13 -14
- package/build/sources/quadbin-query-source.d.ts +2 -1
- package/build/sources/quadbin-table-source.d.ts +2 -1
- package/build/sources/quadbin-tileset-source.d.ts +2 -1
- package/build/sources/raster-source.d.ts +2 -1
- package/build/sources/types.d.ts +48 -37
- package/build/sources/vector-query-source.d.ts +2 -1
- package/build/sources/vector-table-source.d.ts +2 -1
- package/build/sources/vector-tileset-source.d.ts +2 -1
- package/build/spatial-index.d.ts +8 -0
- package/build/utils.d.ts +1 -1
- package/build/widget-sources/types.d.ts +9 -1
- package/build/widget-sources/widget-base-source.d.ts +3 -3
- package/package.json +1 -1
- package/src/api/query.ts +1 -2
- package/src/index.ts +1 -36
- package/src/models/model.ts +47 -24
- package/src/sources/boundary-query-source.ts +4 -2
- package/src/sources/boundary-table-source.ts +4 -2
- package/src/sources/h3-query-source.ts +10 -2
- package/src/sources/h3-table-source.ts +9 -2
- package/src/sources/h3-tileset-source.ts +4 -2
- package/src/sources/index.ts +54 -24
- package/src/sources/quadbin-query-source.ts +10 -2
- package/src/sources/quadbin-table-source.ts +10 -2
- package/src/sources/quadbin-tileset-source.ts +4 -2
- package/src/sources/raster-source.ts +4 -2
- package/src/sources/types.ts +54 -40
- package/src/sources/vector-query-source.ts +13 -2
- package/src/sources/vector-table-source.ts +14 -2
- package/src/sources/vector-tileset-source.ts +4 -2
- package/src/spatial-index.ts +111 -0
- package/src/utils.ts +1 -1
- package/src/widget-sources/types.ts +10 -1
- package/src/widget-sources/widget-base-source.ts +183 -23
|
@@ -393,926 +393,1043 @@ const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
|
393
393
|
*/
|
|
394
394
|
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
395
395
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
396
|
+
// deck.gl
|
|
397
|
+
// SPDX-License-Identifier: MIT
|
|
398
|
+
// Copyright (c) vis.gl contributors
|
|
399
|
+
function joinPath(...args) {
|
|
400
|
+
return args.map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
|
|
401
|
+
}
|
|
402
|
+
function buildV3Path(apiBaseUrl, version, endpoint, ...rest) {
|
|
403
|
+
return joinPath(apiBaseUrl, version, endpoint, ...rest);
|
|
404
|
+
}
|
|
405
|
+
/** @internal Required by fetchMap(). */
|
|
406
|
+
function buildPublicMapUrl({
|
|
407
|
+
apiBaseUrl,
|
|
408
|
+
cartoMapId
|
|
403
409
|
}) {
|
|
404
|
-
|
|
405
|
-
if (data.error === 'Column not found') {
|
|
406
|
-
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
407
|
-
}
|
|
408
|
-
if (typeof data.error === 'string' && (_data$error = data.error) != null && _data$error.includes('Missing columns')) {
|
|
409
|
-
throw new InvalidColumnError(data.error);
|
|
410
|
-
}
|
|
411
|
-
switch (response.status) {
|
|
412
|
-
case 401:
|
|
413
|
-
throw new Error('Unauthorized access. Invalid credentials');
|
|
414
|
-
case 403:
|
|
415
|
-
throw new Error('Forbidden access to the requested data');
|
|
416
|
-
default:
|
|
417
|
-
const msg = data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify((data == null ? void 0 : data.hint) || ((_data$error2 = data.error) == null ? void 0 : _data$error2[0]));
|
|
418
|
-
throw new Error(msg);
|
|
419
|
-
}
|
|
410
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
|
|
420
411
|
}
|
|
421
|
-
/** @
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
412
|
+
/** @internal Required by fetchMap(). */
|
|
413
|
+
function buildStatsUrl({
|
|
414
|
+
attribute,
|
|
415
|
+
apiBaseUrl,
|
|
416
|
+
connectionName,
|
|
417
|
+
source,
|
|
418
|
+
type
|
|
426
419
|
}) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const isPost = (opts == null ? void 0 : opts.method) === 'POST';
|
|
430
|
-
try {
|
|
431
|
-
var _opts$abortController;
|
|
432
|
-
response = await fetch(url.toString(), _extends({
|
|
433
|
-
headers: _extends({
|
|
434
|
-
Authorization: `Bearer ${accessToken}`
|
|
435
|
-
}, isPost && {
|
|
436
|
-
'Content-Type': 'application/json'
|
|
437
|
-
})
|
|
438
|
-
}, isPost && {
|
|
439
|
-
method: opts == null ? void 0 : opts.method,
|
|
440
|
-
body: opts == null ? void 0 : opts.body
|
|
441
|
-
}, {
|
|
442
|
-
signal: opts == null || (_opts$abortController = opts.abortController) == null ? void 0 : _opts$abortController.signal
|
|
443
|
-
}, opts == null ? void 0 : opts.otherOptions));
|
|
444
|
-
data = await response.json();
|
|
445
|
-
} catch (error) {
|
|
446
|
-
if (error.name === 'AbortError') throw error;
|
|
447
|
-
throw new Error(`Failed request: ${error}`);
|
|
448
|
-
}
|
|
449
|
-
if (!response.ok) {
|
|
450
|
-
dealWithApiError({
|
|
451
|
-
response,
|
|
452
|
-
data
|
|
453
|
-
});
|
|
420
|
+
if (type === 'query') {
|
|
421
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, attribute);
|
|
454
422
|
}
|
|
455
|
-
|
|
423
|
+
// type === 'table'
|
|
424
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, source, attribute);
|
|
425
|
+
}
|
|
426
|
+
function buildSourceUrl({
|
|
427
|
+
apiBaseUrl,
|
|
428
|
+
connectionName,
|
|
429
|
+
endpoint
|
|
430
|
+
}) {
|
|
431
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
432
|
+
}
|
|
433
|
+
function buildQueryUrl({
|
|
434
|
+
apiBaseUrl,
|
|
435
|
+
connectionName
|
|
436
|
+
}) {
|
|
437
|
+
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
|
|
456
438
|
}
|
|
457
439
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
V3
|
|
462
|
-
} = ApiVersion;
|
|
463
|
-
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
440
|
+
// deck.gl
|
|
441
|
+
// SPDX-License-Identifier: MIT
|
|
442
|
+
// Copyright (c) vis.gl contributors
|
|
464
443
|
/**
|
|
465
|
-
*
|
|
466
|
-
*
|
|
444
|
+
*
|
|
445
|
+
* Custom error for reported errors in CARTO Maps API.
|
|
446
|
+
* Provides useful debugging information in console and context for applications.
|
|
447
|
+
*
|
|
467
448
|
*/
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
491
|
-
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
492
|
-
const {
|
|
493
|
-
data,
|
|
494
|
-
filters,
|
|
495
|
-
filtersLogicalOperator = 'and',
|
|
496
|
-
geoColumn = DEFAULT_GEO_COLUMN
|
|
497
|
-
} = source;
|
|
498
|
-
const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
|
|
499
|
-
const queryParams = {
|
|
500
|
-
type,
|
|
501
|
-
client: clientId,
|
|
502
|
-
source: data,
|
|
503
|
-
params: JSON.stringify(params),
|
|
504
|
-
queryParameters,
|
|
505
|
-
filters: JSON.stringify(filters),
|
|
506
|
-
filtersLogicalOperator
|
|
507
|
-
};
|
|
508
|
-
// Picking Model API requires 'spatialDataColumn'.
|
|
509
|
-
if (model === 'pick') {
|
|
510
|
-
queryParams.spatialDataColumn = geoColumn;
|
|
511
|
-
}
|
|
512
|
-
// API supports multiple filters, we apply it only to geoColumn
|
|
513
|
-
const spatialFilters = source.spatialFilter ? {
|
|
514
|
-
[geoColumn]: source.spatialFilter
|
|
515
|
-
} : undefined;
|
|
516
|
-
if (spatialFilters) {
|
|
517
|
-
queryParams.spatialFilters = JSON.stringify(spatialFilters);
|
|
518
|
-
}
|
|
519
|
-
const urlWithSearchParams = url + '?' + new URLSearchParams(queryParams).toString();
|
|
520
|
-
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
521
|
-
if (isGet) {
|
|
522
|
-
url = urlWithSearchParams;
|
|
523
|
-
} else {
|
|
524
|
-
// undo the JSON.stringify, @TODO find a better pattern
|
|
525
|
-
queryParams.params = params;
|
|
526
|
-
queryParams.filters = filters;
|
|
527
|
-
queryParams.queryParameters = source.queryParameters;
|
|
528
|
-
if (spatialFilters) {
|
|
529
|
-
queryParams.spatialFilters = spatialFilters;
|
|
449
|
+
class CartoAPIError extends Error {
|
|
450
|
+
constructor(error, errorContext, response, responseJson) {
|
|
451
|
+
let responseString = 'Failed to connect';
|
|
452
|
+
if (response) {
|
|
453
|
+
responseString = 'Server returned: ';
|
|
454
|
+
if (response.status === 400) {
|
|
455
|
+
responseString += 'Bad request';
|
|
456
|
+
} else if (response.status === 401 || response.status === 403) {
|
|
457
|
+
responseString += 'Unauthorized access';
|
|
458
|
+
} else if (response.status === 404) {
|
|
459
|
+
responseString += 'Not found';
|
|
460
|
+
} else {
|
|
461
|
+
responseString += 'Error';
|
|
462
|
+
}
|
|
463
|
+
responseString += ` (${response.status}):`;
|
|
464
|
+
}
|
|
465
|
+
responseString += ` ${error.message || error}`;
|
|
466
|
+
let message = `${errorContext.requestType} API request failed`;
|
|
467
|
+
message += `\n${responseString}`;
|
|
468
|
+
for (const key of Object.keys(errorContext)) {
|
|
469
|
+
if (key === 'requestType') continue;
|
|
470
|
+
message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
|
|
530
471
|
}
|
|
472
|
+
message += '\n';
|
|
473
|
+
super(message);
|
|
474
|
+
/** Source error from server */
|
|
475
|
+
this.error = void 0;
|
|
476
|
+
/** Context (API call & parameters) in which error occured */
|
|
477
|
+
this.errorContext = void 0;
|
|
478
|
+
/** Response from server */
|
|
479
|
+
this.response = void 0;
|
|
480
|
+
/** JSON Response from server */
|
|
481
|
+
this.responseJson = void 0;
|
|
482
|
+
this.name = 'CartoAPIError';
|
|
483
|
+
this.response = response;
|
|
484
|
+
this.responseJson = responseJson;
|
|
485
|
+
this.error = error;
|
|
486
|
+
this.errorContext = errorContext;
|
|
531
487
|
}
|
|
532
|
-
return makeCall({
|
|
533
|
-
url,
|
|
534
|
-
accessToken: source.accessToken,
|
|
535
|
-
opts: _extends({}, opts, {
|
|
536
|
-
method: isGet ? 'GET' : 'POST'
|
|
537
|
-
}, !isGet && {
|
|
538
|
-
body: JSON.stringify(queryParams)
|
|
539
|
-
})
|
|
540
|
-
});
|
|
541
488
|
}
|
|
542
|
-
|
|
543
|
-
const _excluded$1 = ["filterOwner", "spatialFilter", "abortController"],
|
|
544
|
-
_excluded2 = ["filterOwner", "spatialFilter", "abortController"],
|
|
545
|
-
_excluded3 = ["filterOwner", "spatialFilter", "abortController", "operationExp"],
|
|
546
|
-
_excluded4 = ["filterOwner", "spatialFilter", "abortController"],
|
|
547
|
-
_excluded5 = ["filterOwner", "spatialFilter", "abortController"],
|
|
548
|
-
_excluded6 = ["filterOwner", "spatialFilter", "abortController"],
|
|
549
|
-
_excluded7 = ["filterOwner", "spatialFilter", "abortController"],
|
|
550
|
-
_excluded8 = ["filterOwner", "abortController", "spatialFilter"];
|
|
551
489
|
/**
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
490
|
+
* Converts camelCase to Camel Case
|
|
555
491
|
*/
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
492
|
+
function formatErrorKey(key) {
|
|
493
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const DEFAULT_HEADERS = {
|
|
497
|
+
Accept: 'application/json',
|
|
498
|
+
'Content-Type': 'application/json'
|
|
499
|
+
};
|
|
500
|
+
const DEFAULT_REQUEST_CACHE = new Map();
|
|
501
|
+
async function requestWithParameters({
|
|
502
|
+
baseUrl,
|
|
503
|
+
parameters = {},
|
|
504
|
+
headers: customHeaders = {},
|
|
505
|
+
errorContext,
|
|
506
|
+
maxLengthURL = DEFAULT_MAX_LENGTH_URL,
|
|
507
|
+
localCache
|
|
508
|
+
}) {
|
|
509
|
+
// Parameters added to all requests issued with `requestWithParameters()`.
|
|
510
|
+
// These parameters override parameters already in the base URL, but not
|
|
511
|
+
// user-provided parameters.
|
|
512
|
+
parameters = _extends({
|
|
513
|
+
v: V3_MINOR_VERSION,
|
|
514
|
+
client: getClient()
|
|
515
|
+
}, typeof deck !== 'undefined' && deck.VERSION && {
|
|
516
|
+
deckglVersion: deck.VERSION
|
|
517
|
+
}, parameters);
|
|
518
|
+
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
519
|
+
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
520
|
+
const {
|
|
521
|
+
cache: REQUEST_CACHE,
|
|
522
|
+
canReadCache,
|
|
523
|
+
canStoreInCache
|
|
524
|
+
} = getCacheSettings(localCache);
|
|
525
|
+
if (canReadCache && REQUEST_CACHE.has(key)) {
|
|
526
|
+
return REQUEST_CACHE.get(key);
|
|
560
527
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
operation,
|
|
591
|
-
operationColumn
|
|
592
|
-
} = params;
|
|
593
|
-
return executeModel({
|
|
594
|
-
model: 'category',
|
|
595
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
596
|
-
spatialFilter
|
|
597
|
-
}),
|
|
598
|
-
params: {
|
|
599
|
-
column,
|
|
600
|
-
operation,
|
|
601
|
-
operationColumn: operationColumn || column
|
|
602
|
-
},
|
|
603
|
-
opts: {
|
|
604
|
-
abortController
|
|
605
|
-
}
|
|
606
|
-
}).then(res => normalizeObjectKeys(res.rows));
|
|
528
|
+
const url = createURLWithParameters(baseUrl, parameters);
|
|
529
|
+
const headers = _extends({}, DEFAULT_HEADERS, customHeaders);
|
|
530
|
+
/* global fetch */
|
|
531
|
+
const fetchPromise = url.length > maxLengthURL ? fetch(baseUrl, {
|
|
532
|
+
method: 'POST',
|
|
533
|
+
body: JSON.stringify(parameters),
|
|
534
|
+
headers
|
|
535
|
+
}) : fetch(url, {
|
|
536
|
+
headers
|
|
537
|
+
});
|
|
538
|
+
let response;
|
|
539
|
+
let responseJson;
|
|
540
|
+
const jsonPromise = fetchPromise.then(_response => {
|
|
541
|
+
response = _response;
|
|
542
|
+
return response.json();
|
|
543
|
+
}).then(json => {
|
|
544
|
+
responseJson = json;
|
|
545
|
+
if (!response || !response.ok) {
|
|
546
|
+
throw new Error(json.error);
|
|
547
|
+
}
|
|
548
|
+
return json;
|
|
549
|
+
}).catch(error => {
|
|
550
|
+
if (canStoreInCache) {
|
|
551
|
+
REQUEST_CACHE.delete(key);
|
|
552
|
+
}
|
|
553
|
+
throw new CartoAPIError(error, errorContext, response, responseJson);
|
|
554
|
+
});
|
|
555
|
+
if (canStoreInCache) {
|
|
556
|
+
REQUEST_CACHE.set(key, jsonPromise);
|
|
607
557
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
z,
|
|
644
|
-
limit: limit || 1000,
|
|
645
|
-
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
646
|
-
},
|
|
647
|
-
opts: {
|
|
648
|
-
abortController
|
|
649
|
-
}
|
|
650
|
-
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
651
|
-
}).then(({
|
|
652
|
-
rows
|
|
653
|
-
}) => ({
|
|
654
|
-
rows
|
|
655
|
-
}));
|
|
558
|
+
return jsonPromise;
|
|
559
|
+
}
|
|
560
|
+
function getCacheSettings(localCache) {
|
|
561
|
+
var _localCache$cacheCont, _localCache$cacheCont2;
|
|
562
|
+
const canReadCache = localCache != null && (_localCache$cacheCont = localCache.cacheControl) != null && _localCache$cacheCont.includes('no-cache') ? false : true;
|
|
563
|
+
const canStoreInCache = localCache != null && (_localCache$cacheCont2 = localCache.cacheControl) != null && _localCache$cacheCont2.includes('no-store') ? false : true;
|
|
564
|
+
const cache = (localCache == null ? void 0 : localCache.cache) || DEFAULT_REQUEST_CACHE;
|
|
565
|
+
return {
|
|
566
|
+
cache,
|
|
567
|
+
canReadCache,
|
|
568
|
+
canStoreInCache
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function createCacheKey(baseUrl, parameters, headers) {
|
|
572
|
+
const parameterEntries = Object.entries(parameters).sort(([a], [b]) => a > b ? 1 : -1);
|
|
573
|
+
const headerEntries = Object.entries(headers).sort(([a], [b]) => a > b ? 1 : -1);
|
|
574
|
+
return JSON.stringify({
|
|
575
|
+
baseUrl,
|
|
576
|
+
parameters: parameterEntries,
|
|
577
|
+
headers: headerEntries
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Appends query string parameters to a URL. Existing URL parameters are kept,
|
|
582
|
+
* unless there is a conflict, in which case the new parameters override
|
|
583
|
+
* those already in the URL.
|
|
584
|
+
*/
|
|
585
|
+
function createURLWithParameters(baseUrlString, parameters) {
|
|
586
|
+
const baseUrl = new URL(baseUrlString);
|
|
587
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
588
|
+
if (isPureObject(value) || Array.isArray(value)) {
|
|
589
|
+
baseUrl.searchParams.set(key, JSON.stringify(value));
|
|
590
|
+
} else {
|
|
591
|
+
baseUrl.searchParams.set(key, value.toString());
|
|
592
|
+
}
|
|
656
593
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
abortController,
|
|
669
|
-
operationExp
|
|
670
|
-
} = options,
|
|
671
|
-
params = _objectWithoutPropertiesLoose(options, _excluded3);
|
|
672
|
-
const {
|
|
673
|
-
column,
|
|
674
|
-
operation
|
|
675
|
-
} = params;
|
|
676
|
-
return executeModel({
|
|
677
|
-
model: 'formula',
|
|
678
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
679
|
-
spatialFilter
|
|
680
|
-
}),
|
|
681
|
-
params: {
|
|
682
|
-
column: column != null ? column : '*',
|
|
683
|
-
operation,
|
|
684
|
-
operationExp
|
|
685
|
-
},
|
|
686
|
-
opts: {
|
|
687
|
-
abortController
|
|
688
|
-
}
|
|
689
|
-
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
594
|
+
return baseUrl.toString();
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Deletes query string parameters from a URL.
|
|
598
|
+
*/
|
|
599
|
+
function excludeURLParameters(baseUrlString, parameters) {
|
|
600
|
+
const baseUrl = new URL(baseUrlString);
|
|
601
|
+
for (const param of parameters) {
|
|
602
|
+
if (baseUrl.searchParams.has(param)) {
|
|
603
|
+
baseUrl.searchParams.delete(param);
|
|
604
|
+
}
|
|
690
605
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
operation,
|
|
718
|
-
ticks
|
|
719
|
-
},
|
|
720
|
-
opts: {
|
|
721
|
-
abortController
|
|
722
|
-
}
|
|
723
|
-
}).then(res => normalizeObjectKeys(res.rows));
|
|
724
|
-
if (data.length) {
|
|
725
|
-
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
726
|
-
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
727
|
-
const result = Array(ticks.length + 1).fill(0);
|
|
728
|
-
data.forEach(({
|
|
729
|
-
tick,
|
|
730
|
-
value
|
|
731
|
-
}) => result[tick] = value);
|
|
732
|
-
return result;
|
|
606
|
+
return baseUrl.toString();
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const _excluded$1 = ["accessToken", "connectionName", "cache"];
|
|
610
|
+
const SOURCE_DEFAULTS = {
|
|
611
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
612
|
+
clientId: getClient(),
|
|
613
|
+
format: 'tilejson',
|
|
614
|
+
headers: {},
|
|
615
|
+
maxLengthURL: DEFAULT_MAX_LENGTH_URL
|
|
616
|
+
};
|
|
617
|
+
async function baseSource(endpoint, options, urlParameters) {
|
|
618
|
+
const {
|
|
619
|
+
accessToken,
|
|
620
|
+
connectionName,
|
|
621
|
+
cache
|
|
622
|
+
} = options,
|
|
623
|
+
optionalOptions = _objectWithoutPropertiesLoose(options, _excluded$1);
|
|
624
|
+
const mergedOptions = _extends({}, SOURCE_DEFAULTS, {
|
|
625
|
+
accessToken,
|
|
626
|
+
connectionName,
|
|
627
|
+
endpoint
|
|
628
|
+
});
|
|
629
|
+
for (const key in optionalOptions) {
|
|
630
|
+
if (optionalOptions[key]) {
|
|
631
|
+
mergedOptions[key] = optionalOptions[key];
|
|
733
632
|
}
|
|
734
|
-
return [];
|
|
735
633
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
/****************************************************************************
|
|
768
|
-
* SCATTER
|
|
769
|
-
*/
|
|
770
|
-
/**
|
|
771
|
-
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
772
|
-
* values. Suitable for rendering scatter plots.
|
|
773
|
-
*/
|
|
774
|
-
async getScatter(options) {
|
|
775
|
-
const {
|
|
776
|
-
filterOwner,
|
|
777
|
-
spatialFilter,
|
|
778
|
-
abortController
|
|
779
|
-
} = options,
|
|
780
|
-
params = _objectWithoutPropertiesLoose(options, _excluded6);
|
|
781
|
-
const {
|
|
782
|
-
xAxisColumn,
|
|
783
|
-
xAxisJoinOperation,
|
|
784
|
-
yAxisColumn,
|
|
785
|
-
yAxisJoinOperation
|
|
786
|
-
} = params;
|
|
787
|
-
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
788
|
-
const HARD_LIMIT = 500;
|
|
789
|
-
return executeModel({
|
|
790
|
-
model: 'scatterplot',
|
|
791
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
792
|
-
spatialFilter
|
|
793
|
-
}),
|
|
794
|
-
params: {
|
|
795
|
-
xAxisColumn,
|
|
796
|
-
xAxisJoinOperation,
|
|
797
|
-
yAxisColumn,
|
|
798
|
-
yAxisJoinOperation,
|
|
799
|
-
limit: HARD_LIMIT
|
|
800
|
-
},
|
|
801
|
-
opts: {
|
|
802
|
-
abortController
|
|
803
|
-
}
|
|
804
|
-
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(({
|
|
805
|
-
x,
|
|
806
|
-
y
|
|
807
|
-
}) => [x, y]));
|
|
808
|
-
}
|
|
809
|
-
/****************************************************************************
|
|
810
|
-
* TABLE
|
|
811
|
-
*/
|
|
812
|
-
/**
|
|
813
|
-
* Returns a list of arbitrary data rows, with support for pagination and
|
|
814
|
-
* sorting. Suitable for displaying tables and lists.
|
|
815
|
-
*/
|
|
816
|
-
async getTable(options) {
|
|
817
|
-
const {
|
|
818
|
-
filterOwner,
|
|
819
|
-
spatialFilter,
|
|
820
|
-
abortController
|
|
821
|
-
} = options,
|
|
822
|
-
params = _objectWithoutPropertiesLoose(options, _excluded7);
|
|
823
|
-
const {
|
|
824
|
-
columns,
|
|
825
|
-
sortBy,
|
|
826
|
-
sortDirection,
|
|
827
|
-
offset = 0,
|
|
828
|
-
limit = 10
|
|
829
|
-
} = params;
|
|
830
|
-
return executeModel({
|
|
831
|
-
model: 'table',
|
|
832
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
833
|
-
spatialFilter
|
|
834
|
-
}),
|
|
835
|
-
params: {
|
|
836
|
-
column: columns,
|
|
837
|
-
sortBy,
|
|
838
|
-
sortDirection,
|
|
839
|
-
limit,
|
|
840
|
-
offset
|
|
841
|
-
},
|
|
842
|
-
opts: {
|
|
843
|
-
abortController
|
|
844
|
-
}
|
|
845
|
-
}).then(res => {
|
|
846
|
-
var _res$rows, _res$metadata$total, _res$metadata, _res$METADATA;
|
|
847
|
-
return {
|
|
848
|
-
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
849
|
-
rows: (_res$rows = res.rows) != null ? _res$rows : res.ROWS,
|
|
850
|
-
totalCount: (_res$metadata$total = (_res$metadata = res.metadata) == null ? void 0 : _res$metadata.total) != null ? _res$metadata$total : (_res$METADATA = res.METADATA) == null ? void 0 : _res$METADATA.TOTAL
|
|
851
|
-
};
|
|
852
|
-
});
|
|
634
|
+
const baseUrl = buildSourceUrl(mergedOptions);
|
|
635
|
+
const {
|
|
636
|
+
clientId,
|
|
637
|
+
maxLengthURL,
|
|
638
|
+
format,
|
|
639
|
+
localCache
|
|
640
|
+
} = mergedOptions;
|
|
641
|
+
const headers = _extends({
|
|
642
|
+
Authorization: `Bearer ${options.accessToken}`
|
|
643
|
+
}, options.headers);
|
|
644
|
+
const parameters = _extends({
|
|
645
|
+
client: clientId
|
|
646
|
+
}, urlParameters);
|
|
647
|
+
const errorContext = {
|
|
648
|
+
requestType: 'Map instantiation',
|
|
649
|
+
connection: options.connectionName,
|
|
650
|
+
type: endpoint,
|
|
651
|
+
source: JSON.stringify(parameters, undefined, 2)
|
|
652
|
+
};
|
|
653
|
+
const mapInstantiation = await requestWithParameters({
|
|
654
|
+
baseUrl,
|
|
655
|
+
parameters,
|
|
656
|
+
headers,
|
|
657
|
+
errorContext,
|
|
658
|
+
maxLengthURL,
|
|
659
|
+
localCache
|
|
660
|
+
});
|
|
661
|
+
const dataUrl = mapInstantiation[format].url[0];
|
|
662
|
+
if (cache) {
|
|
663
|
+
cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
|
|
853
664
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const {
|
|
863
|
-
filterOwner,
|
|
864
|
-
abortController,
|
|
865
|
-
spatialFilter
|
|
866
|
-
} = options,
|
|
867
|
-
params = _objectWithoutPropertiesLoose(options, _excluded8);
|
|
868
|
-
const {
|
|
869
|
-
column,
|
|
870
|
-
operationColumn,
|
|
871
|
-
joinOperation,
|
|
872
|
-
operation,
|
|
873
|
-
stepSize,
|
|
874
|
-
stepMultiplier,
|
|
875
|
-
splitByCategory,
|
|
876
|
-
splitByCategoryLimit,
|
|
877
|
-
splitByCategoryValues
|
|
878
|
-
} = params;
|
|
879
|
-
return executeModel({
|
|
880
|
-
model: 'timeseries',
|
|
881
|
-
source: _extends({}, this.getModelSource(filterOwner), {
|
|
882
|
-
spatialFilter
|
|
883
|
-
}),
|
|
884
|
-
params: {
|
|
885
|
-
column,
|
|
886
|
-
stepSize,
|
|
887
|
-
stepMultiplier,
|
|
888
|
-
operationColumn: operationColumn || column,
|
|
889
|
-
joinOperation,
|
|
890
|
-
operation,
|
|
891
|
-
splitByCategory,
|
|
892
|
-
splitByCategoryLimit,
|
|
893
|
-
splitByCategoryValues
|
|
894
|
-
},
|
|
895
|
-
opts: {
|
|
896
|
-
abortController
|
|
897
|
-
}
|
|
898
|
-
}).then(res => {
|
|
899
|
-
var _res$metadata2;
|
|
900
|
-
return {
|
|
901
|
-
rows: normalizeObjectKeys(res.rows),
|
|
902
|
-
categories: (_res$metadata2 = res.metadata) == null ? void 0 : _res$metadata2.categories
|
|
903
|
-
};
|
|
665
|
+
errorContext.requestType = 'Map data';
|
|
666
|
+
if (format === 'tilejson') {
|
|
667
|
+
const json = await requestWithParameters({
|
|
668
|
+
baseUrl: dataUrl,
|
|
669
|
+
headers,
|
|
670
|
+
errorContext,
|
|
671
|
+
maxLengthURL,
|
|
672
|
+
localCache
|
|
904
673
|
});
|
|
674
|
+
if (accessToken) {
|
|
675
|
+
json.accessToken = accessToken;
|
|
676
|
+
}
|
|
677
|
+
return json;
|
|
905
678
|
}
|
|
679
|
+
return await requestWithParameters({
|
|
680
|
+
baseUrl: dataUrl,
|
|
681
|
+
headers,
|
|
682
|
+
errorContext,
|
|
683
|
+
maxLengthURL,
|
|
684
|
+
localCache
|
|
685
|
+
});
|
|
906
686
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
687
|
+
|
|
688
|
+
// deck.gl
|
|
689
|
+
const boundaryQuerySource = async function boundaryQuerySource(options) {
|
|
690
|
+
const {
|
|
691
|
+
columns,
|
|
692
|
+
filters,
|
|
693
|
+
tilesetTableName,
|
|
694
|
+
propertiesSqlQuery,
|
|
695
|
+
queryParameters
|
|
696
|
+
} = options;
|
|
697
|
+
const urlParameters = {
|
|
698
|
+
tilesetTableName,
|
|
699
|
+
propertiesSqlQuery
|
|
700
|
+
};
|
|
701
|
+
if (columns) {
|
|
702
|
+
urlParameters.columns = columns.join(',');
|
|
703
|
+
}
|
|
704
|
+
if (filters) {
|
|
705
|
+
urlParameters.filters = filters;
|
|
706
|
+
}
|
|
707
|
+
if (queryParameters) {
|
|
708
|
+
urlParameters.queryParameters = queryParameters;
|
|
709
|
+
}
|
|
710
|
+
return baseSource('boundary', options, urlParameters);
|
|
914
711
|
};
|
|
915
712
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
* accessToken: '••••',
|
|
931
|
-
* connectionName: 'carto_dw',
|
|
932
|
-
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
933
|
-
* });
|
|
934
|
-
*
|
|
935
|
-
* const { widgetSource } = await data;
|
|
936
|
-
* ```
|
|
937
|
-
*/
|
|
938
|
-
class WidgetQuerySource extends WidgetBaseSource {
|
|
939
|
-
getModelSource(owner) {
|
|
940
|
-
return _extends({}, super._getModelSource(owner), {
|
|
941
|
-
type: 'query',
|
|
942
|
-
data: this.props.sqlQuery,
|
|
943
|
-
queryParameters: this.props.queryParameters
|
|
944
|
-
});
|
|
713
|
+
// deck.gl
|
|
714
|
+
const boundaryTableSource = async function boundaryTableSource(options) {
|
|
715
|
+
const {
|
|
716
|
+
filters,
|
|
717
|
+
tilesetTableName,
|
|
718
|
+
columns,
|
|
719
|
+
propertiesTableName
|
|
720
|
+
} = options;
|
|
721
|
+
const urlParameters = {
|
|
722
|
+
tilesetTableName,
|
|
723
|
+
propertiesTableName
|
|
724
|
+
};
|
|
725
|
+
if (columns) {
|
|
726
|
+
urlParameters.columns = columns.join(',');
|
|
945
727
|
}
|
|
946
|
-
|
|
728
|
+
if (filters) {
|
|
729
|
+
urlParameters.filters = filters;
|
|
730
|
+
}
|
|
731
|
+
return baseSource('boundary', options, urlParameters);
|
|
732
|
+
};
|
|
947
733
|
|
|
948
734
|
/**
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
* Generally not intended to be constructed directly. Instead, call
|
|
952
|
-
* {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
|
|
953
|
-
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
954
|
-
* for use by widget implementations.
|
|
955
|
-
*
|
|
956
|
-
* Example:
|
|
957
|
-
*
|
|
958
|
-
* ```javascript
|
|
959
|
-
* import { vectorTableSource } from '@carto/api-client';
|
|
960
|
-
*
|
|
961
|
-
* const data = vectorTableSource({
|
|
962
|
-
* accessToken: '••••',
|
|
963
|
-
* connectionName: 'carto_dw',
|
|
964
|
-
* tableName: 'carto-demo-data.demo_tables.retail_stores'
|
|
965
|
-
* });
|
|
966
|
-
*
|
|
967
|
-
* const { widgetSource } = await data;
|
|
968
|
-
* ```
|
|
735
|
+
* Return more descriptive error from API
|
|
736
|
+
* @internalRemarks Source: @carto/react-api
|
|
969
737
|
*/
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
738
|
+
function dealWithApiError({
|
|
739
|
+
response,
|
|
740
|
+
data
|
|
741
|
+
}) {
|
|
742
|
+
var _data$error, _data$error2;
|
|
743
|
+
if (data.error === 'Column not found') {
|
|
744
|
+
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
745
|
+
}
|
|
746
|
+
if (typeof data.error === 'string' && (_data$error = data.error) != null && _data$error.includes('Missing columns')) {
|
|
747
|
+
throw new InvalidColumnError(data.error);
|
|
748
|
+
}
|
|
749
|
+
switch (response.status) {
|
|
750
|
+
case 401:
|
|
751
|
+
throw new Error('Unauthorized access. Invalid credentials');
|
|
752
|
+
case 403:
|
|
753
|
+
throw new Error('Forbidden access to the requested data');
|
|
754
|
+
default:
|
|
755
|
+
const msg = data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify((data == null ? void 0 : data.hint) || ((_data$error2 = data.error) == null ? void 0 : _data$error2[0]));
|
|
756
|
+
throw new Error(msg);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
/** @internalRemarks Source: @carto/react-api */
|
|
760
|
+
async function makeCall({
|
|
761
|
+
url,
|
|
762
|
+
accessToken,
|
|
763
|
+
opts
|
|
764
|
+
}) {
|
|
765
|
+
let response;
|
|
766
|
+
let data;
|
|
767
|
+
const isPost = (opts == null ? void 0 : opts.method) === 'POST';
|
|
768
|
+
try {
|
|
769
|
+
var _opts$abortController;
|
|
770
|
+
response = await fetch(url.toString(), _extends({
|
|
771
|
+
headers: _extends({
|
|
772
|
+
Authorization: `Bearer ${accessToken}`
|
|
773
|
+
}, isPost && {
|
|
774
|
+
'Content-Type': 'application/json'
|
|
775
|
+
})
|
|
776
|
+
}, isPost && {
|
|
777
|
+
method: opts == null ? void 0 : opts.method,
|
|
778
|
+
body: opts == null ? void 0 : opts.body
|
|
779
|
+
}, {
|
|
780
|
+
signal: opts == null || (_opts$abortController = opts.abortController) == null ? void 0 : _opts$abortController.signal
|
|
781
|
+
}, opts == null ? void 0 : opts.otherOptions));
|
|
782
|
+
data = await response.json();
|
|
783
|
+
} catch (error) {
|
|
784
|
+
if (error.name === 'AbortError') throw error;
|
|
785
|
+
throw new Error(`Failed request: ${error}`);
|
|
786
|
+
}
|
|
787
|
+
if (!response.ok) {
|
|
788
|
+
dealWithApiError({
|
|
789
|
+
response,
|
|
790
|
+
data
|
|
975
791
|
});
|
|
976
792
|
}
|
|
793
|
+
return data;
|
|
977
794
|
}
|
|
978
795
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
796
|
+
/** @internalRemarks Source: @carto/react-api */
|
|
797
|
+
const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'pick', 'timeseries', 'range', 'scatterplot', 'table'];
|
|
798
|
+
const {
|
|
799
|
+
V3
|
|
800
|
+
} = ApiVersion;
|
|
801
|
+
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
982
802
|
/**
|
|
983
|
-
*
|
|
984
|
-
*
|
|
985
|
-
* Provides useful debugging information in console and context for applications.
|
|
986
|
-
*
|
|
803
|
+
* Execute a SQL model request.
|
|
804
|
+
* @internalRemarks Source: @carto/react-api
|
|
987
805
|
*/
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
806
|
+
function executeModel(props) {
|
|
807
|
+
assert(props.source, 'executeModel: missing source');
|
|
808
|
+
assert(props.model, 'executeModel: missing model');
|
|
809
|
+
assert(props.params, 'executeModel: missing params');
|
|
810
|
+
assert(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
|
|
811
|
+
const {
|
|
812
|
+
model,
|
|
813
|
+
source,
|
|
814
|
+
params,
|
|
815
|
+
opts
|
|
816
|
+
} = props;
|
|
817
|
+
const {
|
|
818
|
+
type,
|
|
819
|
+
apiVersion,
|
|
820
|
+
apiBaseUrl,
|
|
821
|
+
accessToken,
|
|
822
|
+
connectionName,
|
|
823
|
+
clientId
|
|
824
|
+
} = source;
|
|
825
|
+
assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
|
|
826
|
+
assert(accessToken, 'executeModel: missing accessToken');
|
|
827
|
+
assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
|
|
828
|
+
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
829
|
+
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
830
|
+
const {
|
|
831
|
+
data,
|
|
832
|
+
filters,
|
|
833
|
+
filtersLogicalOperator = 'and',
|
|
834
|
+
spatialDataType = 'geo',
|
|
835
|
+
spatialFiltersMode = 'intersects',
|
|
836
|
+
spatialFiltersResolution = 0
|
|
837
|
+
} = source;
|
|
838
|
+
const queryParams = {
|
|
839
|
+
type,
|
|
840
|
+
client: clientId,
|
|
841
|
+
source: data,
|
|
842
|
+
params,
|
|
843
|
+
queryParameters: source.queryParameters || '',
|
|
844
|
+
filters,
|
|
845
|
+
filtersLogicalOperator
|
|
846
|
+
};
|
|
847
|
+
const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
|
|
848
|
+
// Picking Model API requires 'spatialDataColumn'.
|
|
849
|
+
if (model === 'pick') {
|
|
850
|
+
queryParams.spatialDataColumn = spatialDataColumn;
|
|
851
|
+
}
|
|
852
|
+
// API supports multiple filters, we apply it only to spatialDataColumn
|
|
853
|
+
const spatialFilters = source.spatialFilter ? {
|
|
854
|
+
[spatialDataColumn]: source.spatialFilter
|
|
855
|
+
} : undefined;
|
|
856
|
+
if (spatialFilters) {
|
|
857
|
+
queryParams.spatialFilters = spatialFilters;
|
|
858
|
+
queryParams.spatialDataColumn = spatialDataColumn;
|
|
859
|
+
queryParams.spatialDataType = spatialDataType;
|
|
860
|
+
}
|
|
861
|
+
if (spatialDataType !== 'geo') {
|
|
862
|
+
if (spatialFiltersResolution > 0) {
|
|
863
|
+
queryParams.spatialFiltersResolution = spatialFiltersResolution;
|
|
1010
864
|
}
|
|
1011
|
-
|
|
1012
|
-
super(message);
|
|
1013
|
-
/** Source error from server */
|
|
1014
|
-
this.error = void 0;
|
|
1015
|
-
/** Context (API call & parameters) in which error occured */
|
|
1016
|
-
this.errorContext = void 0;
|
|
1017
|
-
/** Response from server */
|
|
1018
|
-
this.response = void 0;
|
|
1019
|
-
/** JSON Response from server */
|
|
1020
|
-
this.responseJson = void 0;
|
|
1021
|
-
this.name = 'CartoAPIError';
|
|
1022
|
-
this.response = response;
|
|
1023
|
-
this.responseJson = responseJson;
|
|
1024
|
-
this.error = error;
|
|
1025
|
-
this.errorContext = errorContext;
|
|
865
|
+
queryParams.spatialFiltersMode = spatialFiltersMode;
|
|
1026
866
|
}
|
|
867
|
+
const urlWithSearchParams = url + '?' + objectToURLSearchParams(queryParams).toString();
|
|
868
|
+
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
869
|
+
if (isGet) {
|
|
870
|
+
url = urlWithSearchParams;
|
|
871
|
+
}
|
|
872
|
+
return makeCall({
|
|
873
|
+
url,
|
|
874
|
+
accessToken: source.accessToken,
|
|
875
|
+
opts: _extends({}, opts, {
|
|
876
|
+
method: isGet ? 'GET' : 'POST'
|
|
877
|
+
}, !isGet && {
|
|
878
|
+
body: JSON.stringify(queryParams)
|
|
879
|
+
})
|
|
880
|
+
});
|
|
1027
881
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
882
|
+
function objectToURLSearchParams(object) {
|
|
883
|
+
const params = new URLSearchParams();
|
|
884
|
+
for (const key in object) {
|
|
885
|
+
if (isPureObject(object[key])) {
|
|
886
|
+
params.append(key, JSON.stringify(object[key]));
|
|
887
|
+
} else if (Array.isArray(object[key])) {
|
|
888
|
+
params.append(key, JSON.stringify(object[key]));
|
|
889
|
+
} else if (object[key] === null) {
|
|
890
|
+
params.append(key, 'null');
|
|
891
|
+
} else if (object[key] !== undefined) {
|
|
892
|
+
params.append(key, String(object[key]));
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return params;
|
|
1033
896
|
}
|
|
1034
897
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
898
|
+
const DEFAULT_TILE_SIZE = 512;
|
|
899
|
+
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
900
|
+
function getSpatialFiltersResolution(source, viewState) {
|
|
901
|
+
var _source$dataResolutio, _source$aggregationRe;
|
|
902
|
+
const dataResolution = (_source$dataResolutio = source.dataResolution) != null ? _source$dataResolutio : Number.MAX_VALUE;
|
|
903
|
+
const aggregationResLevel = (_source$aggregationRe = source.aggregationResLevel) != null ? _source$aggregationRe : source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN;
|
|
904
|
+
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
905
|
+
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
906
|
+
if (source.spatialDataType === 'h3') {
|
|
907
|
+
var _maxH3SpatialFiltersR, _maxH3SpatialFiltersR2;
|
|
908
|
+
const tileSize = DEFAULT_TILE_SIZE;
|
|
909
|
+
const maxResolutionForZoom = (_maxH3SpatialFiltersR = (_maxH3SpatialFiltersR2 = maxH3SpatialFiltersResolutions.find(([zoom]) => zoom === currentZoomInt)) == null ? void 0 : _maxH3SpatialFiltersR2[1]) != null ? _maxH3SpatialFiltersR : Math.max(0, currentZoomInt - 3);
|
|
910
|
+
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
911
|
+
const hexagonResolution = getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
912
|
+
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
913
|
+
}
|
|
914
|
+
if (source.spatialDataType === 'quadbin') {
|
|
915
|
+
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
916
|
+
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
917
|
+
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
918
|
+
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
919
|
+
}
|
|
920
|
+
return undefined;
|
|
1050
921
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
apiBaseUrl,
|
|
1067
|
-
connectionName,
|
|
1068
|
-
endpoint
|
|
1069
|
-
}) {
|
|
1070
|
-
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
1071
|
-
}
|
|
1072
|
-
function buildQueryUrl({
|
|
1073
|
-
apiBaseUrl,
|
|
1074
|
-
connectionName
|
|
1075
|
-
}) {
|
|
1076
|
-
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
|
|
922
|
+
const maxH3SpatialFiltersResolutions = [[20, 14], [19, 13], [18, 12], [17, 11], [16, 10], [15, 9], [14, 8], [13, 7], [12, 7], [11, 7], [10, 6], [9, 6], [8, 5], [7, 4], [6, 4], [5, 3], [4, 2], [3, 1], [2, 1], [1, 0]];
|
|
923
|
+
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
924
|
+
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
925
|
+
const BIAS = 2;
|
|
926
|
+
// Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
927
|
+
// a H3 resolution such that the screen space size of the hexagons is
|
|
928
|
+
// similar
|
|
929
|
+
function getHexagonResolution(viewport, tileSize) {
|
|
930
|
+
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
931
|
+
// expressed as an offset to the viewport zoom.
|
|
932
|
+
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
933
|
+
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
934
|
+
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
935
|
+
// Clip and bias
|
|
936
|
+
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
1077
937
|
}
|
|
1078
938
|
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
v: V3_MINOR_VERSION,
|
|
1097
|
-
client: getClient()
|
|
1098
|
-
}, typeof deck !== 'undefined' && deck.VERSION && {
|
|
1099
|
-
deckglVersion: deck.VERSION
|
|
1100
|
-
}, parameters);
|
|
1101
|
-
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
1102
|
-
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
1103
|
-
const {
|
|
1104
|
-
cache: REQUEST_CACHE,
|
|
1105
|
-
canReadCache,
|
|
1106
|
-
canStoreInCache
|
|
1107
|
-
} = getCacheSettings(localCache);
|
|
1108
|
-
if (canReadCache && REQUEST_CACHE.has(key)) {
|
|
1109
|
-
return REQUEST_CACHE.get(key);
|
|
939
|
+
const _excluded = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
940
|
+
_excluded2 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
941
|
+
_excluded3 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController", "operationExp"],
|
|
942
|
+
_excluded4 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
943
|
+
_excluded5 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
944
|
+
_excluded6 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
945
|
+
_excluded7 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
946
|
+
_excluded8 = ["filterOwner", "abortController", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"];
|
|
947
|
+
/**
|
|
948
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
949
|
+
*
|
|
950
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
951
|
+
*/
|
|
952
|
+
class WidgetBaseSource {
|
|
953
|
+
constructor(props) {
|
|
954
|
+
this.props = void 0;
|
|
955
|
+
this.props = _extends({}, WidgetBaseSource.defaultProps, props);
|
|
1110
956
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
if (!
|
|
1129
|
-
|
|
957
|
+
_getModelSource(owner) {
|
|
958
|
+
const props = this.props;
|
|
959
|
+
return {
|
|
960
|
+
apiVersion: props.apiVersion,
|
|
961
|
+
apiBaseUrl: props.apiBaseUrl,
|
|
962
|
+
clientId: props.clientId,
|
|
963
|
+
accessToken: props.accessToken,
|
|
964
|
+
connectionName: props.connectionName,
|
|
965
|
+
filters: getApplicableFilters(owner, props.filters),
|
|
966
|
+
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
967
|
+
spatialDataType: props.spatialDataType,
|
|
968
|
+
spatialDataColumn: props.spatialDataColumn,
|
|
969
|
+
dataResolution: props.dataResolution
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
_getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
|
|
973
|
+
// spatialFiltersResolution applies only to spatial index sources.
|
|
974
|
+
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
975
|
+
return;
|
|
1130
976
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
if (canStoreInCache) {
|
|
1134
|
-
REQUEST_CACHE.delete(key);
|
|
977
|
+
if (!referenceViewState) {
|
|
978
|
+
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
1135
979
|
}
|
|
1136
|
-
|
|
1137
|
-
});
|
|
1138
|
-
if (canStoreInCache) {
|
|
1139
|
-
REQUEST_CACHE.set(key, jsonPromise);
|
|
980
|
+
return getSpatialFiltersResolution(source, referenceViewState);
|
|
1140
981
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
982
|
+
/****************************************************************************
|
|
983
|
+
* CATEGORIES
|
|
984
|
+
*/
|
|
985
|
+
/**
|
|
986
|
+
* Returns a list of labeled datapoints for categorical data. Suitable for
|
|
987
|
+
* charts including grouped bar charts, pie charts, and tree charts.
|
|
988
|
+
*/
|
|
989
|
+
async getCategories(options) {
|
|
990
|
+
const {
|
|
991
|
+
filterOwner,
|
|
992
|
+
spatialFilter,
|
|
993
|
+
spatialFiltersMode,
|
|
994
|
+
spatialIndexReferenceViewState,
|
|
995
|
+
abortController
|
|
996
|
+
} = options,
|
|
997
|
+
params = _objectWithoutPropertiesLoose(options, _excluded);
|
|
998
|
+
const {
|
|
999
|
+
column,
|
|
1000
|
+
operation,
|
|
1001
|
+
operationColumn
|
|
1002
|
+
} = params;
|
|
1003
|
+
const source = this.getModelSource(filterOwner);
|
|
1004
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1005
|
+
return executeModel({
|
|
1006
|
+
model: 'category',
|
|
1007
|
+
source: _extends({}, source, {
|
|
1008
|
+
spatialFiltersResolution,
|
|
1009
|
+
spatialFiltersMode,
|
|
1010
|
+
spatialFilter
|
|
1011
|
+
}),
|
|
1012
|
+
params: {
|
|
1013
|
+
column,
|
|
1014
|
+
operation,
|
|
1015
|
+
operationColumn: operationColumn || column
|
|
1016
|
+
},
|
|
1017
|
+
opts: {
|
|
1018
|
+
abortController
|
|
1019
|
+
}
|
|
1020
|
+
}).then(res => normalizeObjectKeys(res.rows));
|
|
1021
|
+
}
|
|
1022
|
+
/****************************************************************************
|
|
1023
|
+
* FEATURES
|
|
1024
|
+
*/
|
|
1025
|
+
/**
|
|
1026
|
+
* Given a list of feature IDs (as found in `_carto_feature_id`) returns all
|
|
1027
|
+
* matching features. In datasets containing features with duplicate geometries,
|
|
1028
|
+
* feature IDs may be duplicated (IDs are a hash of geometry) and so more
|
|
1029
|
+
* results may be returned than IDs in the request.
|
|
1030
|
+
* @internal
|
|
1031
|
+
* @experimental
|
|
1032
|
+
*/
|
|
1033
|
+
async getFeatures(options) {
|
|
1034
|
+
const {
|
|
1035
|
+
filterOwner,
|
|
1036
|
+
spatialFilter,
|
|
1037
|
+
spatialFiltersMode,
|
|
1038
|
+
spatialIndexReferenceViewState,
|
|
1039
|
+
abortController
|
|
1040
|
+
} = options,
|
|
1041
|
+
params = _objectWithoutPropertiesLoose(options, _excluded2);
|
|
1042
|
+
const {
|
|
1043
|
+
columns,
|
|
1044
|
+
dataType,
|
|
1045
|
+
featureIds,
|
|
1046
|
+
z,
|
|
1047
|
+
limit,
|
|
1048
|
+
tileResolution
|
|
1049
|
+
} = params;
|
|
1050
|
+
const source = this.getModelSource(filterOwner);
|
|
1051
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1052
|
+
return executeModel({
|
|
1053
|
+
model: 'pick',
|
|
1054
|
+
source: _extends({}, source, {
|
|
1055
|
+
spatialFiltersResolution,
|
|
1056
|
+
spatialFiltersMode,
|
|
1057
|
+
spatialFilter
|
|
1058
|
+
}),
|
|
1059
|
+
params: {
|
|
1060
|
+
columns,
|
|
1061
|
+
dataType,
|
|
1062
|
+
featureIds,
|
|
1063
|
+
z,
|
|
1064
|
+
limit: limit || 1000,
|
|
1065
|
+
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
1066
|
+
},
|
|
1067
|
+
opts: {
|
|
1068
|
+
abortController
|
|
1069
|
+
}
|
|
1070
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1071
|
+
}).then(({
|
|
1072
|
+
rows
|
|
1073
|
+
}) => ({
|
|
1074
|
+
rows
|
|
1075
|
+
}));
|
|
1076
|
+
}
|
|
1077
|
+
/****************************************************************************
|
|
1078
|
+
* FORMULA
|
|
1079
|
+
*/
|
|
1080
|
+
/**
|
|
1081
|
+
* Returns a scalar numerical statistic over all matching data. Suitable
|
|
1082
|
+
* for 'headline' or 'scorecard' figures such as counts and sums.
|
|
1083
|
+
*/
|
|
1084
|
+
async getFormula(options) {
|
|
1085
|
+
const {
|
|
1086
|
+
filterOwner,
|
|
1087
|
+
spatialFilter,
|
|
1088
|
+
spatialFiltersMode,
|
|
1089
|
+
spatialIndexReferenceViewState,
|
|
1090
|
+
abortController,
|
|
1091
|
+
operationExp
|
|
1092
|
+
} = options,
|
|
1093
|
+
params = _objectWithoutPropertiesLoose(options, _excluded3);
|
|
1094
|
+
const {
|
|
1095
|
+
column,
|
|
1096
|
+
operation
|
|
1097
|
+
} = params;
|
|
1098
|
+
const source = this.getModelSource(filterOwner);
|
|
1099
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1100
|
+
return executeModel({
|
|
1101
|
+
model: 'formula',
|
|
1102
|
+
source: _extends({}, source, {
|
|
1103
|
+
spatialFiltersResolution,
|
|
1104
|
+
spatialFiltersMode,
|
|
1105
|
+
spatialFilter
|
|
1106
|
+
}),
|
|
1107
|
+
params: {
|
|
1108
|
+
column: column != null ? column : '*',
|
|
1109
|
+
operation,
|
|
1110
|
+
operationExp
|
|
1111
|
+
},
|
|
1112
|
+
opts: {
|
|
1113
|
+
abortController
|
|
1114
|
+
}
|
|
1115
|
+
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
1116
|
+
}
|
|
1117
|
+
/****************************************************************************
|
|
1118
|
+
* HISTOGRAM
|
|
1119
|
+
*/
|
|
1120
|
+
/**
|
|
1121
|
+
* Returns a list of labeled datapoints for 'bins' of data defined as ticks
|
|
1122
|
+
* over a numerical range. Suitable for histogram charts.
|
|
1123
|
+
*/
|
|
1124
|
+
async getHistogram(options) {
|
|
1125
|
+
const {
|
|
1126
|
+
filterOwner,
|
|
1127
|
+
spatialFilter,
|
|
1128
|
+
spatialFiltersMode,
|
|
1129
|
+
spatialIndexReferenceViewState,
|
|
1130
|
+
abortController
|
|
1131
|
+
} = options,
|
|
1132
|
+
params = _objectWithoutPropertiesLoose(options, _excluded4);
|
|
1133
|
+
const {
|
|
1134
|
+
column,
|
|
1135
|
+
operation,
|
|
1136
|
+
ticks
|
|
1137
|
+
} = params;
|
|
1138
|
+
const source = this.getModelSource(filterOwner);
|
|
1139
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1140
|
+
const data = await executeModel({
|
|
1141
|
+
model: 'histogram',
|
|
1142
|
+
source: _extends({}, source, {
|
|
1143
|
+
spatialFiltersResolution,
|
|
1144
|
+
spatialFiltersMode,
|
|
1145
|
+
spatialFilter
|
|
1146
|
+
}),
|
|
1147
|
+
params: {
|
|
1148
|
+
column,
|
|
1149
|
+
operation,
|
|
1150
|
+
ticks
|
|
1151
|
+
},
|
|
1152
|
+
opts: {
|
|
1153
|
+
abortController
|
|
1154
|
+
}
|
|
1155
|
+
}).then(res => normalizeObjectKeys(res.rows));
|
|
1156
|
+
if (data.length) {
|
|
1157
|
+
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
1158
|
+
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
1159
|
+
const result = Array(ticks.length + 1).fill(0);
|
|
1160
|
+
data.forEach(({
|
|
1161
|
+
tick,
|
|
1162
|
+
value
|
|
1163
|
+
}) => result[tick] = value);
|
|
1164
|
+
return result;
|
|
1175
1165
|
}
|
|
1166
|
+
return [];
|
|
1167
|
+
}
|
|
1168
|
+
/****************************************************************************
|
|
1169
|
+
* RANGE
|
|
1170
|
+
*/
|
|
1171
|
+
/**
|
|
1172
|
+
* Returns a range (min and max) for a numerical column of matching rows.
|
|
1173
|
+
* Suitable for displaying certain 'headline' or 'scorecard' statistics,
|
|
1174
|
+
* or rendering a range slider UI for filtering.
|
|
1175
|
+
*/
|
|
1176
|
+
async getRange(options) {
|
|
1177
|
+
const {
|
|
1178
|
+
filterOwner,
|
|
1179
|
+
spatialFilter,
|
|
1180
|
+
spatialFiltersMode,
|
|
1181
|
+
spatialIndexReferenceViewState,
|
|
1182
|
+
abortController
|
|
1183
|
+
} = options,
|
|
1184
|
+
params = _objectWithoutPropertiesLoose(options, _excluded5);
|
|
1185
|
+
const {
|
|
1186
|
+
column
|
|
1187
|
+
} = params;
|
|
1188
|
+
const source = this.getModelSource(filterOwner);
|
|
1189
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1190
|
+
return executeModel({
|
|
1191
|
+
model: 'range',
|
|
1192
|
+
source: _extends({}, source, {
|
|
1193
|
+
spatialFiltersResolution,
|
|
1194
|
+
spatialFiltersMode,
|
|
1195
|
+
spatialFilter
|
|
1196
|
+
}),
|
|
1197
|
+
params: {
|
|
1198
|
+
column
|
|
1199
|
+
},
|
|
1200
|
+
opts: {
|
|
1201
|
+
abortController
|
|
1202
|
+
}
|
|
1203
|
+
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
1204
|
+
}
|
|
1205
|
+
/****************************************************************************
|
|
1206
|
+
* SCATTER
|
|
1207
|
+
*/
|
|
1208
|
+
/**
|
|
1209
|
+
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
1210
|
+
* values. Suitable for rendering scatter plots.
|
|
1211
|
+
*/
|
|
1212
|
+
async getScatter(options) {
|
|
1213
|
+
const {
|
|
1214
|
+
filterOwner,
|
|
1215
|
+
spatialFilter,
|
|
1216
|
+
spatialFiltersMode,
|
|
1217
|
+
spatialIndexReferenceViewState,
|
|
1218
|
+
abortController
|
|
1219
|
+
} = options,
|
|
1220
|
+
params = _objectWithoutPropertiesLoose(options, _excluded6);
|
|
1221
|
+
const {
|
|
1222
|
+
xAxisColumn,
|
|
1223
|
+
xAxisJoinOperation,
|
|
1224
|
+
yAxisColumn,
|
|
1225
|
+
yAxisJoinOperation
|
|
1226
|
+
} = params;
|
|
1227
|
+
const source = this.getModelSource(filterOwner);
|
|
1228
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1229
|
+
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
1230
|
+
const HARD_LIMIT = 500;
|
|
1231
|
+
return executeModel({
|
|
1232
|
+
model: 'scatterplot',
|
|
1233
|
+
source: _extends({}, source, {
|
|
1234
|
+
spatialFiltersResolution,
|
|
1235
|
+
spatialFiltersMode,
|
|
1236
|
+
spatialFilter
|
|
1237
|
+
}),
|
|
1238
|
+
params: {
|
|
1239
|
+
xAxisColumn,
|
|
1240
|
+
xAxisJoinOperation,
|
|
1241
|
+
yAxisColumn,
|
|
1242
|
+
yAxisJoinOperation,
|
|
1243
|
+
limit: HARD_LIMIT
|
|
1244
|
+
},
|
|
1245
|
+
opts: {
|
|
1246
|
+
abortController
|
|
1247
|
+
}
|
|
1248
|
+
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(({
|
|
1249
|
+
x,
|
|
1250
|
+
y
|
|
1251
|
+
}) => [x, y]));
|
|
1252
|
+
}
|
|
1253
|
+
/****************************************************************************
|
|
1254
|
+
* TABLE
|
|
1255
|
+
*/
|
|
1256
|
+
/**
|
|
1257
|
+
* Returns a list of arbitrary data rows, with support for pagination and
|
|
1258
|
+
* sorting. Suitable for displaying tables and lists.
|
|
1259
|
+
*/
|
|
1260
|
+
async getTable(options) {
|
|
1261
|
+
const {
|
|
1262
|
+
filterOwner,
|
|
1263
|
+
spatialFilter,
|
|
1264
|
+
spatialFiltersMode,
|
|
1265
|
+
spatialIndexReferenceViewState,
|
|
1266
|
+
abortController
|
|
1267
|
+
} = options,
|
|
1268
|
+
params = _objectWithoutPropertiesLoose(options, _excluded7);
|
|
1269
|
+
const {
|
|
1270
|
+
columns,
|
|
1271
|
+
sortBy,
|
|
1272
|
+
sortDirection,
|
|
1273
|
+
offset = 0,
|
|
1274
|
+
limit = 10
|
|
1275
|
+
} = params;
|
|
1276
|
+
const source = this.getModelSource(filterOwner);
|
|
1277
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1278
|
+
return executeModel({
|
|
1279
|
+
model: 'table',
|
|
1280
|
+
source: _extends({}, source, {
|
|
1281
|
+
spatialFiltersResolution,
|
|
1282
|
+
spatialFiltersMode,
|
|
1283
|
+
spatialFilter
|
|
1284
|
+
}),
|
|
1285
|
+
params: {
|
|
1286
|
+
column: columns,
|
|
1287
|
+
sortBy,
|
|
1288
|
+
sortDirection,
|
|
1289
|
+
limit,
|
|
1290
|
+
offset
|
|
1291
|
+
},
|
|
1292
|
+
opts: {
|
|
1293
|
+
abortController
|
|
1294
|
+
}
|
|
1295
|
+
}).then(res => {
|
|
1296
|
+
var _res$rows, _res$metadata$total, _res$metadata, _res$METADATA;
|
|
1297
|
+
return {
|
|
1298
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1299
|
+
rows: (_res$rows = res.rows) != null ? _res$rows : res.ROWS,
|
|
1300
|
+
totalCount: (_res$metadata$total = (_res$metadata = res.metadata) == null ? void 0 : _res$metadata.total) != null ? _res$metadata$total : (_res$METADATA = res.METADATA) == null ? void 0 : _res$METADATA.TOTAL
|
|
1301
|
+
};
|
|
1302
|
+
});
|
|
1176
1303
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1304
|
+
/****************************************************************************
|
|
1305
|
+
* TIME SERIES
|
|
1306
|
+
*/
|
|
1307
|
+
/**
|
|
1308
|
+
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
1309
|
+
* time intervals. Suitable for rendering time series charts.
|
|
1310
|
+
*/
|
|
1311
|
+
async getTimeSeries(options) {
|
|
1312
|
+
const {
|
|
1313
|
+
filterOwner,
|
|
1314
|
+
abortController,
|
|
1315
|
+
spatialFilter,
|
|
1316
|
+
spatialFiltersMode,
|
|
1317
|
+
spatialIndexReferenceViewState
|
|
1318
|
+
} = options,
|
|
1319
|
+
params = _objectWithoutPropertiesLoose(options, _excluded8);
|
|
1320
|
+
const {
|
|
1321
|
+
column,
|
|
1322
|
+
operationColumn,
|
|
1323
|
+
joinOperation,
|
|
1324
|
+
operation,
|
|
1325
|
+
stepSize,
|
|
1326
|
+
stepMultiplier,
|
|
1327
|
+
splitByCategory,
|
|
1328
|
+
splitByCategoryLimit,
|
|
1329
|
+
splitByCategoryValues
|
|
1330
|
+
} = params;
|
|
1331
|
+
const source = this.getModelSource(filterOwner);
|
|
1332
|
+
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1333
|
+
return executeModel({
|
|
1334
|
+
model: 'timeseries',
|
|
1335
|
+
source: _extends({}, source, {
|
|
1336
|
+
spatialFiltersResolution,
|
|
1337
|
+
spatialFiltersMode,
|
|
1338
|
+
spatialFilter
|
|
1339
|
+
}),
|
|
1340
|
+
params: {
|
|
1341
|
+
column,
|
|
1342
|
+
stepSize,
|
|
1343
|
+
stepMultiplier,
|
|
1344
|
+
operationColumn: operationColumn || column,
|
|
1345
|
+
joinOperation,
|
|
1346
|
+
operation,
|
|
1347
|
+
splitByCategory,
|
|
1348
|
+
splitByCategoryLimit,
|
|
1349
|
+
splitByCategoryValues
|
|
1350
|
+
},
|
|
1351
|
+
opts: {
|
|
1352
|
+
abortController
|
|
1353
|
+
}
|
|
1354
|
+
}).then(res => {
|
|
1355
|
+
var _res$metadata2;
|
|
1356
|
+
return {
|
|
1357
|
+
rows: normalizeObjectKeys(res.rows),
|
|
1358
|
+
categories: (_res$metadata2 = res.metadata) == null ? void 0 : _res$metadata2.categories
|
|
1359
|
+
};
|
|
1360
|
+
});
|
|
1188
1361
|
}
|
|
1189
|
-
return baseUrl.toString();
|
|
1190
1362
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const SOURCE_DEFAULTS = {
|
|
1363
|
+
WidgetBaseSource.defaultProps = {
|
|
1364
|
+
apiVersion: ApiVersion.V3,
|
|
1194
1365
|
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1195
1366
|
clientId: getClient(),
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
maxLengthURL: DEFAULT_MAX_LENGTH_URL
|
|
1367
|
+
filters: {},
|
|
1368
|
+
filtersLogicalOperator: 'and'
|
|
1199
1369
|
};
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}, urlParameters);
|
|
1230
|
-
const errorContext = {
|
|
1231
|
-
requestType: 'Map instantiation',
|
|
1232
|
-
connection: options.connectionName,
|
|
1233
|
-
type: endpoint,
|
|
1234
|
-
source: JSON.stringify(parameters, undefined, 2)
|
|
1235
|
-
};
|
|
1236
|
-
const mapInstantiation = await requestWithParameters({
|
|
1237
|
-
baseUrl,
|
|
1238
|
-
parameters,
|
|
1239
|
-
headers,
|
|
1240
|
-
errorContext,
|
|
1241
|
-
maxLengthURL,
|
|
1242
|
-
localCache
|
|
1243
|
-
});
|
|
1244
|
-
const dataUrl = mapInstantiation[format].url[0];
|
|
1245
|
-
if (cache) {
|
|
1246
|
-
cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
|
|
1247
|
-
}
|
|
1248
|
-
errorContext.requestType = 'Map data';
|
|
1249
|
-
if (format === 'tilejson') {
|
|
1250
|
-
const json = await requestWithParameters({
|
|
1251
|
-
baseUrl: dataUrl,
|
|
1252
|
-
headers,
|
|
1253
|
-
errorContext,
|
|
1254
|
-
maxLengthURL,
|
|
1255
|
-
localCache
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
1373
|
+
*
|
|
1374
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
1375
|
+
* {@link vectorQuerySource}, {@link h3QuerySource}, or {@link quadbinQuerySource},
|
|
1376
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1377
|
+
* for use by widget implementations.
|
|
1378
|
+
*
|
|
1379
|
+
* Example:
|
|
1380
|
+
*
|
|
1381
|
+
* ```javascript
|
|
1382
|
+
* import { vectorQuerySource } from '@carto/api-client';
|
|
1383
|
+
*
|
|
1384
|
+
* const data = vectorQuerySource({
|
|
1385
|
+
* accessToken: '••••',
|
|
1386
|
+
* connectionName: 'carto_dw',
|
|
1387
|
+
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
1388
|
+
* });
|
|
1389
|
+
*
|
|
1390
|
+
* const { widgetSource } = await data;
|
|
1391
|
+
* ```
|
|
1392
|
+
*/
|
|
1393
|
+
class WidgetQuerySource extends WidgetBaseSource {
|
|
1394
|
+
getModelSource(owner) {
|
|
1395
|
+
return _extends({}, super._getModelSource(owner), {
|
|
1396
|
+
type: 'query',
|
|
1397
|
+
data: this.props.sqlQuery,
|
|
1398
|
+
queryParameters: this.props.queryParameters
|
|
1256
1399
|
});
|
|
1257
|
-
if (accessToken) {
|
|
1258
|
-
json.accessToken = accessToken;
|
|
1259
|
-
}
|
|
1260
|
-
return json;
|
|
1261
1400
|
}
|
|
1262
|
-
return await requestWithParameters({
|
|
1263
|
-
baseUrl: dataUrl,
|
|
1264
|
-
headers,
|
|
1265
|
-
errorContext,
|
|
1266
|
-
maxLengthURL,
|
|
1267
|
-
localCache
|
|
1268
|
-
});
|
|
1269
1401
|
}
|
|
1270
1402
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
filters,
|
|
1300
|
-
tilesetTableName,
|
|
1301
|
-
columns,
|
|
1302
|
-
propertiesTableName
|
|
1303
|
-
} = options;
|
|
1304
|
-
const urlParameters = {
|
|
1305
|
-
tilesetTableName,
|
|
1306
|
-
propertiesTableName
|
|
1307
|
-
};
|
|
1308
|
-
if (columns) {
|
|
1309
|
-
urlParameters.columns = columns.join(',');
|
|
1310
|
-
}
|
|
1311
|
-
if (filters) {
|
|
1312
|
-
urlParameters.filters = filters;
|
|
1403
|
+
/**
|
|
1404
|
+
* Source for Widget API requests on a data source defined as a table.
|
|
1405
|
+
*
|
|
1406
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
1407
|
+
* {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
|
|
1408
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1409
|
+
* for use by widget implementations.
|
|
1410
|
+
*
|
|
1411
|
+
* Example:
|
|
1412
|
+
*
|
|
1413
|
+
* ```javascript
|
|
1414
|
+
* import { vectorTableSource } from '@carto/api-client';
|
|
1415
|
+
*
|
|
1416
|
+
* const data = vectorTableSource({
|
|
1417
|
+
* accessToken: '••••',
|
|
1418
|
+
* connectionName: 'carto_dw',
|
|
1419
|
+
* tableName: 'carto-demo-data.demo_tables.retail_stores'
|
|
1420
|
+
* });
|
|
1421
|
+
*
|
|
1422
|
+
* const { widgetSource } = await data;
|
|
1423
|
+
* ```
|
|
1424
|
+
*/
|
|
1425
|
+
class WidgetTableSource extends WidgetBaseSource {
|
|
1426
|
+
getModelSource(owner) {
|
|
1427
|
+
return _extends({}, super._getModelSource(owner), {
|
|
1428
|
+
type: 'table',
|
|
1429
|
+
data: this.props.tableName
|
|
1430
|
+
});
|
|
1313
1431
|
}
|
|
1314
|
-
|
|
1315
|
-
};
|
|
1432
|
+
}
|
|
1316
1433
|
|
|
1317
1434
|
const h3QuerySource = async function h3QuerySource(options) {
|
|
1318
1435
|
const {
|
|
@@ -1339,7 +1456,11 @@ const h3QuerySource = async function h3QuerySource(options) {
|
|
|
1339
1456
|
urlParameters.filters = filters;
|
|
1340
1457
|
}
|
|
1341
1458
|
return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
|
|
1342
|
-
widgetSource: new WidgetQuerySource(options
|
|
1459
|
+
widgetSource: new WidgetQuerySource(_extends({}, options, {
|
|
1460
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
|
|
1461
|
+
spatialDataColumn,
|
|
1462
|
+
spatialDataType: 'h3'
|
|
1463
|
+
}))
|
|
1343
1464
|
}));
|
|
1344
1465
|
};
|
|
1345
1466
|
|
|
@@ -1364,7 +1485,11 @@ const h3TableSource = async function h3TableSource(options) {
|
|
|
1364
1485
|
urlParameters.filters = filters;
|
|
1365
1486
|
}
|
|
1366
1487
|
return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
|
|
1367
|
-
widgetSource: new WidgetTableSource(options
|
|
1488
|
+
widgetSource: new WidgetTableSource(_extends({}, options, {
|
|
1489
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
|
|
1490
|
+
spatialDataColumn,
|
|
1491
|
+
spatialDataType: 'h3'
|
|
1492
|
+
}))
|
|
1368
1493
|
}));
|
|
1369
1494
|
};
|
|
1370
1495
|
|
|
@@ -1419,7 +1544,11 @@ const quadbinQuerySource = async function quadbinQuerySource(options) {
|
|
|
1419
1544
|
urlParameters.filters = filters;
|
|
1420
1545
|
}
|
|
1421
1546
|
return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
|
|
1422
|
-
widgetSource: new WidgetQuerySource(options
|
|
1547
|
+
widgetSource: new WidgetQuerySource(_extends({}, options, {
|
|
1548
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
|
|
1549
|
+
spatialDataColumn,
|
|
1550
|
+
spatialDataType: 'quadbin'
|
|
1551
|
+
}))
|
|
1423
1552
|
}));
|
|
1424
1553
|
};
|
|
1425
1554
|
|
|
@@ -1444,7 +1573,11 @@ const quadbinTableSource = async function quadbinTableSource(options) {
|
|
|
1444
1573
|
urlParameters.filters = filters;
|
|
1445
1574
|
}
|
|
1446
1575
|
return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
|
|
1447
|
-
widgetSource: new WidgetTableSource(options
|
|
1576
|
+
widgetSource: new WidgetTableSource(_extends({}, options, {
|
|
1577
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
|
|
1578
|
+
spatialDataColumn,
|
|
1579
|
+
spatialDataType: 'quadbin'
|
|
1580
|
+
}))
|
|
1448
1581
|
}));
|
|
1449
1582
|
};
|
|
1450
1583
|
|
|
@@ -1466,7 +1599,8 @@ const vectorQuerySource = async function vectorQuerySource(options) {
|
|
|
1466
1599
|
spatialDataColumn = 'geom',
|
|
1467
1600
|
sqlQuery,
|
|
1468
1601
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1469
|
-
queryParameters
|
|
1602
|
+
queryParameters,
|
|
1603
|
+
aggregationExp
|
|
1470
1604
|
} = options;
|
|
1471
1605
|
const urlParameters = {
|
|
1472
1606
|
spatialDataColumn,
|
|
@@ -1483,8 +1617,13 @@ const vectorQuerySource = async function vectorQuerySource(options) {
|
|
|
1483
1617
|
if (queryParameters) {
|
|
1484
1618
|
urlParameters.queryParameters = queryParameters;
|
|
1485
1619
|
}
|
|
1620
|
+
if (aggregationExp) {
|
|
1621
|
+
urlParameters.aggregationExp = aggregationExp;
|
|
1622
|
+
}
|
|
1486
1623
|
return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
|
|
1487
|
-
widgetSource: new WidgetQuerySource(options
|
|
1624
|
+
widgetSource: new WidgetQuerySource(_extends({}, options, {
|
|
1625
|
+
spatialDataType: 'geo'
|
|
1626
|
+
}))
|
|
1488
1627
|
}));
|
|
1489
1628
|
};
|
|
1490
1629
|
|
|
@@ -1494,7 +1633,8 @@ const vectorTableSource = async function vectorTableSource(options) {
|
|
|
1494
1633
|
filters,
|
|
1495
1634
|
spatialDataColumn = 'geom',
|
|
1496
1635
|
tableName,
|
|
1497
|
-
tileResolution = DEFAULT_TILE_RESOLUTION
|
|
1636
|
+
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1637
|
+
aggregationExp
|
|
1498
1638
|
} = options;
|
|
1499
1639
|
const urlParameters = {
|
|
1500
1640
|
name: tableName,
|
|
@@ -1508,8 +1648,13 @@ const vectorTableSource = async function vectorTableSource(options) {
|
|
|
1508
1648
|
if (filters) {
|
|
1509
1649
|
urlParameters.filters = filters;
|
|
1510
1650
|
}
|
|
1651
|
+
if (aggregationExp) {
|
|
1652
|
+
urlParameters.aggregationExp = aggregationExp;
|
|
1653
|
+
}
|
|
1511
1654
|
return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
|
|
1512
|
-
widgetSource: new WidgetTableSource(options
|
|
1655
|
+
widgetSource: new WidgetTableSource(_extends({}, options, {
|
|
1656
|
+
spatialDataType: 'geo'
|
|
1657
|
+
}))
|
|
1513
1658
|
}));
|
|
1514
1659
|
};
|
|
1515
1660
|
|