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