@carto/api-client 0.4.3 → 0.5.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/build/api/query.d.ts +1 -1
- package/build/api-client.cjs +2388 -261
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +2237 -262
- package/build/api-client.modern.js.map +1 -1
- package/build/constants.d.ts +22 -0
- package/build/filters/Filter.d.ts +13 -0
- package/build/filters/FilterTypes.d.ts +3 -0
- package/build/filters/geosjonFeatures.d.ts +8 -0
- package/build/filters/index.d.ts +6 -0
- package/build/filters/tileFeatures.d.ts +20 -0
- package/build/filters/tileFeaturesGeometries.d.ts +13 -0
- package/build/filters/tileFeaturesSpatialIndex.d.ts +10 -0
- package/build/index.d.ts +4 -0
- package/build/models/index.d.ts +1 -1
- package/build/models/model.d.ts +7 -1
- package/build/operations/aggregation.d.ts +8 -0
- package/build/operations/applySorting.d.ts +20 -0
- package/build/operations/groupBy.d.ts +15 -0
- package/build/operations/groupByDate.d.ts +11 -0
- package/build/operations/histogram.d.ts +13 -0
- package/build/operations/index.d.ts +6 -0
- package/build/operations/scatterPlot.d.ts +14 -0
- package/build/sources/h3-tileset-source.d.ts +2 -1
- package/build/sources/index.d.ts +1 -1
- package/build/sources/quadbin-tileset-source.d.ts +2 -1
- package/build/sources/types.d.ts +36 -41
- package/build/sources/vector-tileset-source.d.ts +2 -1
- package/build/spatial-index.d.ts +8 -0
- package/build/types-internal.d.ts +4 -0
- package/build/types.d.ts +61 -1
- package/build/utils/dateUtils.d.ts +10 -0
- package/build/utils/getTileFormat.d.ts +3 -0
- package/build/utils/makeIntervalComplete.d.ts +2 -0
- package/build/utils/transformTileCoordsToWGS84.d.ts +8 -0
- package/build/utils/transformToTileCoords.d.ts +9 -0
- package/build/utils.d.ts +1 -1
- package/build/widget-sources/index.d.ts +2 -1
- package/build/widget-sources/types.d.ts +40 -23
- package/build/widget-sources/widget-query-source.d.ts +2 -2
- package/build/widget-sources/widget-remote-source.d.ts +18 -0
- package/build/widget-sources/{widget-base-source.d.ts → widget-source.d.ts} +16 -41
- package/build/widget-sources/widget-table-source.d.ts +2 -2
- package/build/widget-sources/widget-tileset-source.d.ts +67 -0
- package/package.json +36 -35
- package/src/api/query.ts +1 -2
- package/src/constants.ts +25 -0
- package/src/filters/Filter.ts +169 -0
- package/src/filters/FilterTypes.ts +109 -0
- package/src/filters/geosjonFeatures.ts +32 -0
- package/src/filters/index.ts +6 -0
- package/src/filters/tileFeatures.ts +56 -0
- package/src/filters/tileFeaturesGeometries.ts +444 -0
- package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
- package/src/index.ts +6 -0
- package/src/models/index.ts +1 -1
- package/src/models/model.ts +47 -24
- package/src/operations/aggregation.ts +154 -0
- package/src/operations/applySorting.ts +109 -0
- package/src/operations/groupBy.ts +59 -0
- package/src/operations/groupByDate.ts +98 -0
- package/src/operations/histogram.ts +66 -0
- package/src/operations/index.ts +6 -0
- package/src/operations/scatterPlot.ts +50 -0
- package/src/sources/h3-query-source.ts +7 -1
- package/src/sources/h3-table-source.ts +6 -1
- package/src/sources/h3-tileset-source.ts +18 -6
- package/src/sources/index.ts +1 -1
- package/src/sources/quadbin-query-source.ts +6 -1
- package/src/sources/quadbin-table-source.ts +6 -1
- package/src/sources/quadbin-tileset-source.ts +18 -6
- package/src/sources/raster-source.ts +1 -0
- package/src/sources/types.ts +41 -45
- package/src/sources/vector-query-source.ts +10 -3
- package/src/sources/vector-table-source.ts +10 -3
- package/src/sources/vector-tileset-source.ts +19 -6
- package/src/spatial-index.ts +111 -0
- package/src/types-internal.ts +6 -0
- package/src/types.ts +60 -2
- package/src/utils/dateUtils.ts +28 -0
- package/src/utils/getTileFormat.ts +9 -0
- package/src/utils/makeIntervalComplete.ts +17 -0
- package/src/utils/transformTileCoordsToWGS84.ts +77 -0
- package/src/utils/transformToTileCoords.ts +85 -0
- package/src/utils.ts +9 -6
- package/src/widget-sources/index.ts +2 -1
- package/src/widget-sources/types.ts +42 -23
- package/src/widget-sources/widget-query-source.ts +6 -3
- package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +169 -144
- package/src/widget-sources/widget-source.ts +160 -0
- package/src/widget-sources/widget-table-source.ts +6 -3
- package/src/widget-sources/widget-tileset-source.ts +396 -0
package/build/api-client.cjs
CHANGED
|
@@ -3,12 +3,10 @@ var bboxPolygon = require('@turf/bbox-polygon');
|
|
|
3
3
|
var union = require('@turf/union');
|
|
4
4
|
var invariant = require('@turf/invariant');
|
|
5
5
|
var helpers = require('@turf/helpers');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var
|
|
10
|
-
var bboxPolygon__default = /*#__PURE__*/_interopDefaultLegacy(bboxPolygon);
|
|
11
|
-
var union__default = /*#__PURE__*/_interopDefaultLegacy(union);
|
|
6
|
+
var intersects = require('@turf/boolean-intersects');
|
|
7
|
+
var booleanWithin = require('@turf/boolean-within');
|
|
8
|
+
var intersect = require('@turf/intersect');
|
|
9
|
+
var h3Js = require('h3-js');
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* @internal
|
|
@@ -67,6 +65,31 @@ exports.ApiVersion = void 0;
|
|
|
67
65
|
})(exports.ApiVersion || (exports.ApiVersion = {}));
|
|
68
66
|
/** @internalRemarks Source: @carto/constants, @deck.gl/carto */
|
|
69
67
|
const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
|
|
68
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
69
|
+
exports.TileFormat = void 0;
|
|
70
|
+
(function (TileFormat) {
|
|
71
|
+
TileFormat["MVT"] = "mvt";
|
|
72
|
+
TileFormat["JSON"] = "json";
|
|
73
|
+
TileFormat["GEOJSON"] = "geojson";
|
|
74
|
+
TileFormat["BINARY"] = "binary";
|
|
75
|
+
})(exports.TileFormat || (exports.TileFormat = {}));
|
|
76
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
77
|
+
exports.SpatialIndex = void 0;
|
|
78
|
+
(function (SpatialIndex) {
|
|
79
|
+
SpatialIndex["H3"] = "h3";
|
|
80
|
+
SpatialIndex["S2"] = "s2";
|
|
81
|
+
SpatialIndex["QUADBIN"] = "quadbin";
|
|
82
|
+
})(exports.SpatialIndex || (exports.SpatialIndex = {}));
|
|
83
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
84
|
+
exports.Provider = void 0;
|
|
85
|
+
(function (Provider) {
|
|
86
|
+
Provider["BIGQUERY"] = "bigquery";
|
|
87
|
+
Provider["REDSHIFT"] = "redshift";
|
|
88
|
+
Provider["POSTGRES"] = "postgres";
|
|
89
|
+
Provider["SNOWFLAKE"] = "snowflake";
|
|
90
|
+
Provider["DATABRICKS"] = "databricks";
|
|
91
|
+
Provider["DATABRICKS_REST"] = "databricksRest";
|
|
92
|
+
})(exports.Provider || (exports.Provider = {}));
|
|
70
93
|
|
|
71
94
|
const FILTER_TYPES = new Set(Object.values(exports.FilterType));
|
|
72
95
|
const isFilterType = type => FILTER_TYPES.has(type);
|
|
@@ -110,7 +133,7 @@ function normalizeObjectKeys(el) {
|
|
|
110
133
|
}, {});
|
|
111
134
|
}
|
|
112
135
|
/** @internalRemarks Source: @carto/react-core */
|
|
113
|
-
function assert(condition, message) {
|
|
136
|
+
function assert$1(condition, message) {
|
|
114
137
|
if (!condition) {
|
|
115
138
|
throw new Error(message);
|
|
116
139
|
}
|
|
@@ -245,7 +268,7 @@ function createViewportSpatialFilter(viewport) {
|
|
|
245
268
|
if (_isGlobalViewport(viewport)) {
|
|
246
269
|
return;
|
|
247
270
|
}
|
|
248
|
-
return createPolygonSpatialFilter(
|
|
271
|
+
return createPolygonSpatialFilter(bboxPolygon(viewport).geometry);
|
|
249
272
|
}
|
|
250
273
|
/**
|
|
251
274
|
* Returns a {@link SpatialFilter} for a given {@link Polygon} or
|
|
@@ -277,23 +300,23 @@ function _isGlobalViewport(viewport) {
|
|
|
277
300
|
*/
|
|
278
301
|
function _normalizeGeometry(geometry) {
|
|
279
302
|
const WORLD = [-180, -90, +180, +90];
|
|
280
|
-
const worldClip = _clean(
|
|
303
|
+
const worldClip = _clean(bboxClip(geometry, WORLD).geometry);
|
|
281
304
|
const geometryTxWest = _tx(geometry, 360);
|
|
282
305
|
const geometryTxEast = _tx(geometry, -360);
|
|
283
306
|
let result = worldClip;
|
|
284
307
|
if (result && geometryTxWest) {
|
|
285
|
-
const worldWestClip = _clean(
|
|
308
|
+
const worldWestClip = _clean(bboxClip(geometryTxWest, WORLD).geometry);
|
|
286
309
|
if (worldWestClip) {
|
|
287
310
|
const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldWestClip)]);
|
|
288
|
-
const merged =
|
|
311
|
+
const merged = union(collection);
|
|
289
312
|
result = merged ? _clean(merged.geometry) : result;
|
|
290
313
|
}
|
|
291
314
|
}
|
|
292
315
|
if (result && geometryTxEast) {
|
|
293
|
-
const worldEastClip = _clean(
|
|
316
|
+
const worldEastClip = _clean(bboxClip(geometryTxEast, WORLD).geometry);
|
|
294
317
|
if (worldEastClip) {
|
|
295
318
|
const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldEastClip)]);
|
|
296
|
-
const merged =
|
|
319
|
+
const merged = union(collection);
|
|
297
320
|
result = merged ? _clean(merged.geometry) : result;
|
|
298
321
|
}
|
|
299
322
|
}
|
|
@@ -486,6 +509,8 @@ function formatErrorKey(key) {
|
|
|
486
509
|
}
|
|
487
510
|
|
|
488
511
|
// deck.gl
|
|
512
|
+
// SPDX-License-Identifier: MIT
|
|
513
|
+
// Copyright (c) vis.gl contributors
|
|
489
514
|
const requestWithParameters = function (_ref) {
|
|
490
515
|
let {
|
|
491
516
|
baseUrl,
|
|
@@ -617,6 +642,8 @@ function excludeURLParameters(baseUrlString, parameters) {
|
|
|
617
642
|
}
|
|
618
643
|
|
|
619
644
|
// deck.gl
|
|
645
|
+
// SPDX-License-Identifier: MIT
|
|
646
|
+
// Copyright (c) vis.gl contributors
|
|
620
647
|
const baseSource = function (endpoint, options, urlParameters) {
|
|
621
648
|
try {
|
|
622
649
|
const {
|
|
@@ -712,6 +739,8 @@ const SOURCE_DEFAULTS = {
|
|
|
712
739
|
};
|
|
713
740
|
|
|
714
741
|
// deck.gl
|
|
742
|
+
// SPDX-License-Identifier: MIT
|
|
743
|
+
// Copyright (c) vis.gl contributors
|
|
715
744
|
const boundaryQuerySource = function (options) {
|
|
716
745
|
try {
|
|
717
746
|
const {
|
|
@@ -741,6 +770,8 @@ const boundaryQuerySource = function (options) {
|
|
|
741
770
|
};
|
|
742
771
|
|
|
743
772
|
// deck.gl
|
|
773
|
+
// SPDX-License-Identifier: MIT
|
|
774
|
+
// Copyright (c) vis.gl contributors
|
|
744
775
|
const boundaryTableSource = function (options) {
|
|
745
776
|
try {
|
|
746
777
|
const {
|
|
@@ -866,10 +897,10 @@ const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
|
866
897
|
* @internalRemarks Source: @carto/react-api
|
|
867
898
|
*/
|
|
868
899
|
function executeModel(props) {
|
|
869
|
-
assert(props.source, 'executeModel: missing source');
|
|
870
|
-
assert(props.model, 'executeModel: missing model');
|
|
871
|
-
assert(props.params, 'executeModel: missing params');
|
|
872
|
-
assert(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
|
|
900
|
+
assert$1(props.source, 'executeModel: missing source');
|
|
901
|
+
assert$1(props.model, 'executeModel: missing model');
|
|
902
|
+
assert$1(props.params, 'executeModel: missing params');
|
|
903
|
+
assert$1(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
|
|
873
904
|
const {
|
|
874
905
|
model,
|
|
875
906
|
source,
|
|
@@ -884,50 +915,52 @@ function executeModel(props) {
|
|
|
884
915
|
connectionName,
|
|
885
916
|
clientId
|
|
886
917
|
} = source;
|
|
887
|
-
assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
|
|
888
|
-
assert(accessToken, 'executeModel: missing accessToken');
|
|
889
|
-
assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
|
|
890
|
-
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
918
|
+
assert$1(apiBaseUrl, 'executeModel: missing apiBaseUrl');
|
|
919
|
+
assert$1(accessToken, 'executeModel: missing accessToken');
|
|
920
|
+
assert$1(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
|
|
921
|
+
assert$1(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
891
922
|
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
892
923
|
const {
|
|
893
924
|
data,
|
|
894
925
|
filters,
|
|
895
926
|
filtersLogicalOperator = 'and',
|
|
896
|
-
|
|
927
|
+
spatialDataType = 'geo',
|
|
928
|
+
spatialFiltersMode = 'intersects',
|
|
929
|
+
spatialFiltersResolution = 0
|
|
897
930
|
} = source;
|
|
898
|
-
const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
|
|
899
931
|
const queryParams = {
|
|
900
932
|
type,
|
|
901
933
|
client: clientId,
|
|
902
934
|
source: data,
|
|
903
|
-
params
|
|
904
|
-
queryParameters,
|
|
905
|
-
filters
|
|
935
|
+
params,
|
|
936
|
+
queryParameters: source.queryParameters || '',
|
|
937
|
+
filters,
|
|
906
938
|
filtersLogicalOperator
|
|
907
939
|
};
|
|
940
|
+
const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
|
|
908
941
|
// Picking Model API requires 'spatialDataColumn'.
|
|
909
942
|
if (model === 'pick') {
|
|
910
|
-
queryParams.spatialDataColumn =
|
|
943
|
+
queryParams.spatialDataColumn = spatialDataColumn;
|
|
911
944
|
}
|
|
912
|
-
// API supports multiple filters, we apply it only to
|
|
945
|
+
// API supports multiple filters, we apply it only to spatialDataColumn
|
|
913
946
|
const spatialFilters = source.spatialFilter ? {
|
|
914
|
-
[
|
|
947
|
+
[spatialDataColumn]: source.spatialFilter
|
|
915
948
|
} : undefined;
|
|
916
949
|
if (spatialFilters) {
|
|
917
|
-
queryParams.spatialFilters =
|
|
950
|
+
queryParams.spatialFilters = spatialFilters;
|
|
951
|
+
queryParams.spatialDataColumn = spatialDataColumn;
|
|
952
|
+
queryParams.spatialDataType = spatialDataType;
|
|
918
953
|
}
|
|
919
|
-
|
|
954
|
+
if (spatialDataType !== 'geo') {
|
|
955
|
+
if (spatialFiltersResolution > 0) {
|
|
956
|
+
queryParams.spatialFiltersResolution = spatialFiltersResolution;
|
|
957
|
+
}
|
|
958
|
+
queryParams.spatialFiltersMode = spatialFiltersMode;
|
|
959
|
+
}
|
|
960
|
+
const urlWithSearchParams = url + '?' + objectToURLSearchParams(queryParams).toString();
|
|
920
961
|
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
921
962
|
if (isGet) {
|
|
922
963
|
url = urlWithSearchParams;
|
|
923
|
-
} else {
|
|
924
|
-
// undo the JSON.stringify, @TODO find a better pattern
|
|
925
|
-
queryParams.params = params;
|
|
926
|
-
queryParams.filters = filters;
|
|
927
|
-
queryParams.queryParameters = source.queryParameters;
|
|
928
|
-
if (spatialFilters) {
|
|
929
|
-
queryParams.spatialFilters = spatialFilters;
|
|
930
|
-
}
|
|
931
964
|
}
|
|
932
965
|
return makeCall({
|
|
933
966
|
url,
|
|
@@ -941,17 +974,74 @@ function executeModel(props) {
|
|
|
941
974
|
}
|
|
942
975
|
});
|
|
943
976
|
}
|
|
977
|
+
function objectToURLSearchParams(object) {
|
|
978
|
+
const params = new URLSearchParams();
|
|
979
|
+
for (const key in object) {
|
|
980
|
+
if (isPureObject(object[key])) {
|
|
981
|
+
params.append(key, JSON.stringify(object[key]));
|
|
982
|
+
} else if (Array.isArray(object[key])) {
|
|
983
|
+
params.append(key, JSON.stringify(object[key]));
|
|
984
|
+
} else if (object[key] === null) {
|
|
985
|
+
params.append(key, 'null');
|
|
986
|
+
} else if (object[key] !== undefined) {
|
|
987
|
+
params.append(key, String(object[key]));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
return params;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
const DEFAULT_TILE_SIZE = 512;
|
|
994
|
+
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
995
|
+
function getSpatialFiltersResolution(source, viewState) {
|
|
996
|
+
const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
|
|
997
|
+
const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
|
|
998
|
+
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
999
|
+
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
1000
|
+
if (source.spatialDataType === 'h3') {
|
|
1001
|
+
const tileSize = DEFAULT_TILE_SIZE;
|
|
1002
|
+
const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref => {
|
|
1003
|
+
let [zoom] = _ref;
|
|
1004
|
+
return zoom === currentZoomInt;
|
|
1005
|
+
})?.[1] ?? Math.max(0, currentZoomInt - 3);
|
|
1006
|
+
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
1007
|
+
const hexagonResolution = getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
1008
|
+
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
1009
|
+
}
|
|
1010
|
+
if (source.spatialDataType === 'quadbin') {
|
|
1011
|
+
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
1012
|
+
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
1013
|
+
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
1014
|
+
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
1015
|
+
}
|
|
1016
|
+
return undefined;
|
|
1017
|
+
}
|
|
1018
|
+
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]];
|
|
1019
|
+
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
1020
|
+
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
1021
|
+
const BIAS = 2;
|
|
1022
|
+
// Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
1023
|
+
// a H3 resolution such that the screen space size of the hexagons is
|
|
1024
|
+
// similar
|
|
1025
|
+
function getHexagonResolution(viewport, tileSize) {
|
|
1026
|
+
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
1027
|
+
// expressed as an offset to the viewport zoom.
|
|
1028
|
+
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
1029
|
+
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
1030
|
+
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
1031
|
+
// Clip and bias
|
|
1032
|
+
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
1033
|
+
}
|
|
944
1034
|
|
|
945
1035
|
/**
|
|
946
1036
|
* Source for Widget API requests on a data source defined by a SQL query.
|
|
947
1037
|
*
|
|
948
1038
|
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
949
1039
|
*/
|
|
950
|
-
class
|
|
1040
|
+
class WidgetSource {
|
|
951
1041
|
constructor(props) {
|
|
952
1042
|
this.props = void 0;
|
|
953
1043
|
this.props = {
|
|
954
|
-
...
|
|
1044
|
+
...WidgetSource.defaultProps,
|
|
955
1045
|
...props
|
|
956
1046
|
};
|
|
957
1047
|
}
|
|
@@ -965,22 +1055,44 @@ class WidgetBaseSource {
|
|
|
965
1055
|
connectionName: props.connectionName,
|
|
966
1056
|
filters: getApplicableFilters(owner, props.filters),
|
|
967
1057
|
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
968
|
-
|
|
1058
|
+
spatialDataType: props.spatialDataType,
|
|
1059
|
+
spatialDataColumn: props.spatialDataColumn,
|
|
1060
|
+
dataResolution: props.dataResolution
|
|
969
1061
|
};
|
|
970
1062
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1063
|
+
_getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
|
|
1064
|
+
// spatialFiltersResolution applies only to spatial index sources.
|
|
1065
|
+
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
if (!referenceViewState) {
|
|
1069
|
+
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
1070
|
+
}
|
|
1071
|
+
return getSpatialFiltersResolution(source, referenceViewState);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
WidgetSource.defaultProps = {
|
|
1075
|
+
apiVersion: exports.ApiVersion.V3,
|
|
1076
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1077
|
+
clientId: getClient(),
|
|
1078
|
+
filters: {},
|
|
1079
|
+
filtersLogicalOperator: 'and'
|
|
1080
|
+
};
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Source for Widget API requests.
|
|
1084
|
+
*
|
|
1085
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
1086
|
+
*/
|
|
1087
|
+
class WidgetRemoteSource extends WidgetSource {
|
|
978
1088
|
getCategories(options) {
|
|
979
1089
|
try {
|
|
980
1090
|
const _this = this;
|
|
981
1091
|
const {
|
|
982
1092
|
filterOwner,
|
|
983
1093
|
spatialFilter,
|
|
1094
|
+
spatialFiltersMode,
|
|
1095
|
+
spatialIndexReferenceViewState,
|
|
984
1096
|
abortController,
|
|
985
1097
|
...params
|
|
986
1098
|
} = options;
|
|
@@ -989,10 +1101,14 @@ class WidgetBaseSource {
|
|
|
989
1101
|
operation,
|
|
990
1102
|
operationColumn
|
|
991
1103
|
} = params;
|
|
1104
|
+
const source = _this.getModelSource(filterOwner);
|
|
1105
|
+
const spatialFiltersResolution = _this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
992
1106
|
return Promise.resolve(executeModel({
|
|
993
1107
|
model: 'category',
|
|
994
1108
|
source: {
|
|
995
|
-
...
|
|
1109
|
+
...source,
|
|
1110
|
+
spatialFiltersResolution,
|
|
1111
|
+
spatialFiltersMode,
|
|
996
1112
|
spatialFilter
|
|
997
1113
|
},
|
|
998
1114
|
params: {
|
|
@@ -1008,23 +1124,14 @@ class WidgetBaseSource {
|
|
|
1008
1124
|
return Promise.reject(e);
|
|
1009
1125
|
}
|
|
1010
1126
|
}
|
|
1011
|
-
/****************************************************************************
|
|
1012
|
-
* FEATURES
|
|
1013
|
-
*/
|
|
1014
|
-
/**
|
|
1015
|
-
* Given a list of feature IDs (as found in `_carto_feature_id`) returns all
|
|
1016
|
-
* matching features. In datasets containing features with duplicate geometries,
|
|
1017
|
-
* feature IDs may be duplicated (IDs are a hash of geometry) and so more
|
|
1018
|
-
* results may be returned than IDs in the request.
|
|
1019
|
-
* @internal
|
|
1020
|
-
* @experimental
|
|
1021
|
-
*/
|
|
1022
1127
|
getFeatures(options) {
|
|
1023
1128
|
try {
|
|
1024
1129
|
const _this2 = this;
|
|
1025
1130
|
const {
|
|
1026
1131
|
filterOwner,
|
|
1027
1132
|
spatialFilter,
|
|
1133
|
+
spatialFiltersMode,
|
|
1134
|
+
spatialIndexReferenceViewState,
|
|
1028
1135
|
abortController,
|
|
1029
1136
|
...params
|
|
1030
1137
|
} = options;
|
|
@@ -1036,10 +1143,14 @@ class WidgetBaseSource {
|
|
|
1036
1143
|
limit,
|
|
1037
1144
|
tileResolution
|
|
1038
1145
|
} = params;
|
|
1146
|
+
const source = _this2.getModelSource(filterOwner);
|
|
1147
|
+
const spatialFiltersResolution = _this2._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1039
1148
|
return Promise.resolve(executeModel({
|
|
1040
1149
|
model: 'pick',
|
|
1041
1150
|
source: {
|
|
1042
|
-
...
|
|
1151
|
+
...source,
|
|
1152
|
+
spatialFiltersResolution,
|
|
1153
|
+
spatialFiltersMode,
|
|
1043
1154
|
spatialFilter
|
|
1044
1155
|
},
|
|
1045
1156
|
params: {
|
|
@@ -1066,19 +1177,14 @@ class WidgetBaseSource {
|
|
|
1066
1177
|
return Promise.reject(e);
|
|
1067
1178
|
}
|
|
1068
1179
|
}
|
|
1069
|
-
/****************************************************************************
|
|
1070
|
-
* FORMULA
|
|
1071
|
-
*/
|
|
1072
|
-
/**
|
|
1073
|
-
* Returns a scalar numerical statistic over all matching data. Suitable
|
|
1074
|
-
* for 'headline' or 'scorecard' figures such as counts and sums.
|
|
1075
|
-
*/
|
|
1076
1180
|
getFormula(options) {
|
|
1077
1181
|
try {
|
|
1078
1182
|
const _this3 = this;
|
|
1079
1183
|
const {
|
|
1080
1184
|
filterOwner,
|
|
1081
1185
|
spatialFilter,
|
|
1186
|
+
spatialFiltersMode,
|
|
1187
|
+
spatialIndexReferenceViewState,
|
|
1082
1188
|
abortController,
|
|
1083
1189
|
operationExp,
|
|
1084
1190
|
...params
|
|
@@ -1087,15 +1193,19 @@ class WidgetBaseSource {
|
|
|
1087
1193
|
column,
|
|
1088
1194
|
operation
|
|
1089
1195
|
} = params;
|
|
1196
|
+
const source = _this3.getModelSource(filterOwner);
|
|
1197
|
+
const spatialFiltersResolution = _this3._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1090
1198
|
return Promise.resolve(executeModel({
|
|
1091
1199
|
model: 'formula',
|
|
1092
1200
|
source: {
|
|
1093
|
-
...
|
|
1201
|
+
...source,
|
|
1202
|
+
spatialFiltersResolution,
|
|
1203
|
+
spatialFiltersMode,
|
|
1094
1204
|
spatialFilter
|
|
1095
1205
|
},
|
|
1096
1206
|
params: {
|
|
1097
1207
|
column: column ?? '*',
|
|
1098
|
-
operation,
|
|
1208
|
+
operation: operation ?? 'count',
|
|
1099
1209
|
operationExp
|
|
1100
1210
|
},
|
|
1101
1211
|
opts: {
|
|
@@ -1106,19 +1216,14 @@ class WidgetBaseSource {
|
|
|
1106
1216
|
return Promise.reject(e);
|
|
1107
1217
|
}
|
|
1108
1218
|
}
|
|
1109
|
-
/****************************************************************************
|
|
1110
|
-
* HISTOGRAM
|
|
1111
|
-
*/
|
|
1112
|
-
/**
|
|
1113
|
-
* Returns a list of labeled datapoints for 'bins' of data defined as ticks
|
|
1114
|
-
* over a numerical range. Suitable for histogram charts.
|
|
1115
|
-
*/
|
|
1116
1219
|
getHistogram(options) {
|
|
1117
1220
|
try {
|
|
1118
1221
|
const _this4 = this;
|
|
1119
1222
|
const {
|
|
1120
1223
|
filterOwner,
|
|
1121
1224
|
spatialFilter,
|
|
1225
|
+
spatialFiltersMode,
|
|
1226
|
+
spatialIndexReferenceViewState,
|
|
1122
1227
|
abortController,
|
|
1123
1228
|
...params
|
|
1124
1229
|
} = options;
|
|
@@ -1127,10 +1232,14 @@ class WidgetBaseSource {
|
|
|
1127
1232
|
operation,
|
|
1128
1233
|
ticks
|
|
1129
1234
|
} = params;
|
|
1235
|
+
const source = _this4.getModelSource(filterOwner);
|
|
1236
|
+
const spatialFiltersResolution = _this4._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1130
1237
|
return Promise.resolve(executeModel({
|
|
1131
1238
|
model: 'histogram',
|
|
1132
1239
|
source: {
|
|
1133
|
-
...
|
|
1240
|
+
...source,
|
|
1241
|
+
spatialFiltersResolution,
|
|
1242
|
+
spatialFiltersMode,
|
|
1134
1243
|
spatialFilter
|
|
1135
1244
|
},
|
|
1136
1245
|
params: {
|
|
@@ -1161,30 +1270,28 @@ class WidgetBaseSource {
|
|
|
1161
1270
|
return Promise.reject(e);
|
|
1162
1271
|
}
|
|
1163
1272
|
}
|
|
1164
|
-
/****************************************************************************
|
|
1165
|
-
* RANGE
|
|
1166
|
-
*/
|
|
1167
|
-
/**
|
|
1168
|
-
* Returns a range (min and max) for a numerical column of matching rows.
|
|
1169
|
-
* Suitable for displaying certain 'headline' or 'scorecard' statistics,
|
|
1170
|
-
* or rendering a range slider UI for filtering.
|
|
1171
|
-
*/
|
|
1172
1273
|
getRange(options) {
|
|
1173
1274
|
try {
|
|
1174
1275
|
const _this5 = this;
|
|
1175
1276
|
const {
|
|
1176
1277
|
filterOwner,
|
|
1177
1278
|
spatialFilter,
|
|
1279
|
+
spatialFiltersMode,
|
|
1280
|
+
spatialIndexReferenceViewState,
|
|
1178
1281
|
abortController,
|
|
1179
1282
|
...params
|
|
1180
1283
|
} = options;
|
|
1181
1284
|
const {
|
|
1182
1285
|
column
|
|
1183
1286
|
} = params;
|
|
1287
|
+
const source = _this5.getModelSource(filterOwner);
|
|
1288
|
+
const spatialFiltersResolution = _this5._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1184
1289
|
return Promise.resolve(executeModel({
|
|
1185
1290
|
model: 'range',
|
|
1186
1291
|
source: {
|
|
1187
|
-
...
|
|
1292
|
+
...source,
|
|
1293
|
+
spatialFiltersResolution,
|
|
1294
|
+
spatialFiltersMode,
|
|
1188
1295
|
spatialFilter
|
|
1189
1296
|
},
|
|
1190
1297
|
params: {
|
|
@@ -1198,19 +1305,14 @@ class WidgetBaseSource {
|
|
|
1198
1305
|
return Promise.reject(e);
|
|
1199
1306
|
}
|
|
1200
1307
|
}
|
|
1201
|
-
/****************************************************************************
|
|
1202
|
-
* SCATTER
|
|
1203
|
-
*/
|
|
1204
|
-
/**
|
|
1205
|
-
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
1206
|
-
* values. Suitable for rendering scatter plots.
|
|
1207
|
-
*/
|
|
1208
1308
|
getScatter(options) {
|
|
1209
1309
|
try {
|
|
1210
1310
|
const _this6 = this;
|
|
1211
1311
|
const {
|
|
1212
1312
|
filterOwner,
|
|
1213
1313
|
spatialFilter,
|
|
1314
|
+
spatialFiltersMode,
|
|
1315
|
+
spatialIndexReferenceViewState,
|
|
1214
1316
|
abortController,
|
|
1215
1317
|
...params
|
|
1216
1318
|
} = options;
|
|
@@ -1220,12 +1322,16 @@ class WidgetBaseSource {
|
|
|
1220
1322
|
yAxisColumn,
|
|
1221
1323
|
yAxisJoinOperation
|
|
1222
1324
|
} = params;
|
|
1325
|
+
const source = _this6.getModelSource(filterOwner);
|
|
1326
|
+
const spatialFiltersResolution = _this6._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1223
1327
|
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
1224
1328
|
const HARD_LIMIT = 500;
|
|
1225
1329
|
return Promise.resolve(executeModel({
|
|
1226
1330
|
model: 'scatterplot',
|
|
1227
1331
|
source: {
|
|
1228
|
-
...
|
|
1332
|
+
...source,
|
|
1333
|
+
spatialFiltersResolution,
|
|
1334
|
+
spatialFiltersMode,
|
|
1229
1335
|
spatialFilter
|
|
1230
1336
|
},
|
|
1231
1337
|
params: {
|
|
@@ -1249,19 +1355,14 @@ class WidgetBaseSource {
|
|
|
1249
1355
|
return Promise.reject(e);
|
|
1250
1356
|
}
|
|
1251
1357
|
}
|
|
1252
|
-
/****************************************************************************
|
|
1253
|
-
* TABLE
|
|
1254
|
-
*/
|
|
1255
|
-
/**
|
|
1256
|
-
* Returns a list of arbitrary data rows, with support for pagination and
|
|
1257
|
-
* sorting. Suitable for displaying tables and lists.
|
|
1258
|
-
*/
|
|
1259
1358
|
getTable(options) {
|
|
1260
1359
|
try {
|
|
1261
1360
|
const _this7 = this;
|
|
1262
1361
|
const {
|
|
1263
1362
|
filterOwner,
|
|
1264
1363
|
spatialFilter,
|
|
1364
|
+
spatialFiltersMode,
|
|
1365
|
+
spatialIndexReferenceViewState,
|
|
1265
1366
|
abortController,
|
|
1266
1367
|
...params
|
|
1267
1368
|
} = options;
|
|
@@ -1272,10 +1373,14 @@ class WidgetBaseSource {
|
|
|
1272
1373
|
offset = 0,
|
|
1273
1374
|
limit = 10
|
|
1274
1375
|
} = params;
|
|
1376
|
+
const source = _this7.getModelSource(filterOwner);
|
|
1377
|
+
const spatialFiltersResolution = _this7._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1275
1378
|
return Promise.resolve(executeModel({
|
|
1276
1379
|
model: 'table',
|
|
1277
1380
|
source: {
|
|
1278
|
-
...
|
|
1381
|
+
...source,
|
|
1382
|
+
spatialFiltersResolution,
|
|
1383
|
+
spatialFiltersMode,
|
|
1279
1384
|
spatialFilter
|
|
1280
1385
|
},
|
|
1281
1386
|
params: {
|
|
@@ -1297,13 +1402,6 @@ class WidgetBaseSource {
|
|
|
1297
1402
|
return Promise.reject(e);
|
|
1298
1403
|
}
|
|
1299
1404
|
}
|
|
1300
|
-
/****************************************************************************
|
|
1301
|
-
* TIME SERIES
|
|
1302
|
-
*/
|
|
1303
|
-
/**
|
|
1304
|
-
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
1305
|
-
* time intervals. Suitable for rendering time series charts.
|
|
1306
|
-
*/
|
|
1307
1405
|
getTimeSeries(options) {
|
|
1308
1406
|
try {
|
|
1309
1407
|
const _this8 = this;
|
|
@@ -1311,6 +1409,8 @@ class WidgetBaseSource {
|
|
|
1311
1409
|
filterOwner,
|
|
1312
1410
|
abortController,
|
|
1313
1411
|
spatialFilter,
|
|
1412
|
+
spatialFiltersMode,
|
|
1413
|
+
spatialIndexReferenceViewState,
|
|
1314
1414
|
...params
|
|
1315
1415
|
} = options;
|
|
1316
1416
|
const {
|
|
@@ -1324,10 +1424,14 @@ class WidgetBaseSource {
|
|
|
1324
1424
|
splitByCategoryLimit,
|
|
1325
1425
|
splitByCategoryValues
|
|
1326
1426
|
} = params;
|
|
1427
|
+
const source = _this8.getModelSource(filterOwner);
|
|
1428
|
+
const spatialFiltersResolution = _this8._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1327
1429
|
return Promise.resolve(executeModel({
|
|
1328
1430
|
model: 'timeseries',
|
|
1329
1431
|
source: {
|
|
1330
|
-
...
|
|
1432
|
+
...source,
|
|
1433
|
+
spatialFiltersResolution,
|
|
1434
|
+
spatialFiltersMode,
|
|
1331
1435
|
spatialFilter
|
|
1332
1436
|
},
|
|
1333
1437
|
params: {
|
|
@@ -1353,14 +1457,6 @@ class WidgetBaseSource {
|
|
|
1353
1457
|
}
|
|
1354
1458
|
}
|
|
1355
1459
|
}
|
|
1356
|
-
WidgetBaseSource.defaultProps = {
|
|
1357
|
-
apiVersion: exports.ApiVersion.V3,
|
|
1358
|
-
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1359
|
-
clientId: getClient(),
|
|
1360
|
-
filters: {},
|
|
1361
|
-
filtersLogicalOperator: 'and',
|
|
1362
|
-
geoColumn: DEFAULT_GEO_COLUMN
|
|
1363
|
-
};
|
|
1364
1460
|
|
|
1365
1461
|
/**
|
|
1366
1462
|
* Source for Widget API requests on a data source defined by a SQL query.
|
|
@@ -1384,7 +1480,7 @@ WidgetBaseSource.defaultProps = {
|
|
|
1384
1480
|
* const { widgetSource } = await data;
|
|
1385
1481
|
* ```
|
|
1386
1482
|
*/
|
|
1387
|
-
class WidgetQuerySource extends
|
|
1483
|
+
class WidgetQuerySource extends WidgetRemoteSource {
|
|
1388
1484
|
getModelSource(owner) {
|
|
1389
1485
|
return {
|
|
1390
1486
|
...super._getModelSource(owner),
|
|
@@ -1417,7 +1513,7 @@ class WidgetQuerySource extends WidgetBaseSource {
|
|
|
1417
1513
|
* const { widgetSource } = await data;
|
|
1418
1514
|
* ```
|
|
1419
1515
|
*/
|
|
1420
|
-
class WidgetTableSource extends
|
|
1516
|
+
class WidgetTableSource extends WidgetRemoteSource {
|
|
1421
1517
|
getModelSource(owner) {
|
|
1422
1518
|
return {
|
|
1423
1519
|
...super._getModelSource(owner),
|
|
@@ -1427,150 +1523,2119 @@ class WidgetTableSource extends WidgetBaseSource {
|
|
|
1427
1523
|
}
|
|
1428
1524
|
}
|
|
1429
1525
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
aggregationExp,
|
|
1435
|
-
aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
|
|
1436
|
-
sqlQuery,
|
|
1437
|
-
spatialDataColumn = 'h3',
|
|
1438
|
-
queryParameters,
|
|
1439
|
-
filters
|
|
1440
|
-
} = options;
|
|
1441
|
-
const urlParameters = {
|
|
1442
|
-
aggregationExp,
|
|
1443
|
-
spatialDataColumn,
|
|
1444
|
-
spatialDataType: 'h3',
|
|
1445
|
-
q: sqlQuery
|
|
1446
|
-
};
|
|
1447
|
-
if (aggregationResLevel) {
|
|
1448
|
-
urlParameters.aggregationResLevel = String(aggregationResLevel);
|
|
1449
|
-
}
|
|
1450
|
-
if (queryParameters) {
|
|
1451
|
-
urlParameters.queryParameters = queryParameters;
|
|
1526
|
+
function makeIntervalComplete(intervals) {
|
|
1527
|
+
return intervals.map(val => {
|
|
1528
|
+
if (val[0] === undefined || val[0] === null) {
|
|
1529
|
+
return [Number.MIN_SAFE_INTEGER, val[1]];
|
|
1452
1530
|
}
|
|
1453
|
-
if (
|
|
1454
|
-
|
|
1531
|
+
if (val[1] === undefined || val[1] === null) {
|
|
1532
|
+
return [val[0], Number.MAX_SAFE_INTEGER];
|
|
1455
1533
|
}
|
|
1456
|
-
return
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1534
|
+
return val;
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
const filterFunctions = {
|
|
1539
|
+
[exports.FilterType.IN]: filterIn,
|
|
1540
|
+
[exports.FilterType.BETWEEN]: filterBetween,
|
|
1541
|
+
[exports.FilterType.TIME]: filterTime,
|
|
1542
|
+
[exports.FilterType.CLOSED_OPEN]: filterClosedOpen,
|
|
1543
|
+
[exports.FilterType.STRING_SEARCH]: filterStringSearch
|
|
1463
1544
|
};
|
|
1545
|
+
function filterIn(filterValues, featureValue) {
|
|
1546
|
+
return filterValues.includes(featureValue);
|
|
1547
|
+
}
|
|
1548
|
+
// FilterTypes.BETWEEN
|
|
1549
|
+
function filterBetween(filterValues, featureValue) {
|
|
1550
|
+
const checkRange = range => {
|
|
1551
|
+
const [lowerBound, upperBound] = range;
|
|
1552
|
+
return featureValue >= lowerBound && featureValue <= upperBound;
|
|
1553
|
+
};
|
|
1554
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
1555
|
+
}
|
|
1556
|
+
function filterTime(filterValues, featureValue) {
|
|
1557
|
+
const featureValueAsTimestamp = new Date(featureValue).getTime();
|
|
1558
|
+
if (isFinite(featureValueAsTimestamp)) {
|
|
1559
|
+
return filterBetween(filterValues, featureValueAsTimestamp);
|
|
1560
|
+
} else {
|
|
1561
|
+
throw new Error(`Column used to filter by time isn't well formatted.`);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
// FilterTypes.CLOSED_OPEN
|
|
1565
|
+
function filterClosedOpen(filterValues, featureValue) {
|
|
1566
|
+
const checkRange = range => {
|
|
1567
|
+
const [lowerBound, upperBound] = range;
|
|
1568
|
+
return featureValue >= lowerBound && featureValue < upperBound;
|
|
1569
|
+
};
|
|
1570
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
1571
|
+
}
|
|
1572
|
+
// FilterTypes.STRING_SEARCH
|
|
1573
|
+
function filterStringSearch(filterValues, featureValue, params) {
|
|
1574
|
+
if (params === void 0) {
|
|
1575
|
+
params = {};
|
|
1576
|
+
}
|
|
1577
|
+
const normalizedFeatureValue = normalize(featureValue, params);
|
|
1578
|
+
const stringRegExp = params.useRegExp ? filterValues : filterValues.map(filterValue => {
|
|
1579
|
+
let stringRegExp = escapeRegExp(normalize(filterValue, params));
|
|
1580
|
+
if (params.mustStart) stringRegExp = `^${stringRegExp}`;
|
|
1581
|
+
if (params.mustEnd) stringRegExp = `${stringRegExp}$`;
|
|
1582
|
+
return stringRegExp;
|
|
1583
|
+
});
|
|
1584
|
+
const regex = new RegExp(stringRegExp.join('|'), params.caseSensitive ? 'g' : 'gi');
|
|
1585
|
+
return !!normalizedFeatureValue.match(regex);
|
|
1586
|
+
}
|
|
1587
|
+
// Aux
|
|
1588
|
+
const specialCharRegExp = /[.*+?^${}()|[\]\\]/g;
|
|
1589
|
+
const normalizeRegExp = /\p{Diacritic}/gu;
|
|
1590
|
+
function escapeRegExp(value) {
|
|
1591
|
+
return value.replace(specialCharRegExp, '\\$&');
|
|
1592
|
+
}
|
|
1593
|
+
function normalize(data, params) {
|
|
1594
|
+
let normalizedData = String(data);
|
|
1595
|
+
if (!params.keepSpecialCharacters) normalizedData = normalizedData.normalize('NFD').replace(normalizeRegExp, '');
|
|
1596
|
+
return normalizedData;
|
|
1597
|
+
}
|
|
1464
1598
|
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
aggregationExp,
|
|
1477
|
-
name: tableName,
|
|
1478
|
-
spatialDataColumn,
|
|
1479
|
-
spatialDataType: 'h3'
|
|
1480
|
-
};
|
|
1481
|
-
if (aggregationResLevel) {
|
|
1482
|
-
urlParameters.aggregationResLevel = String(aggregationResLevel);
|
|
1483
|
-
}
|
|
1484
|
-
if (filters) {
|
|
1485
|
-
urlParameters.filters = filters;
|
|
1599
|
+
const LOGICAL_OPERATOR_METHODS = {
|
|
1600
|
+
and: 'every',
|
|
1601
|
+
or: 'some'
|
|
1602
|
+
};
|
|
1603
|
+
function passesFilter(columns, filters, feature, filtersLogicalOperator) {
|
|
1604
|
+
const method = LOGICAL_OPERATOR_METHODS[filtersLogicalOperator];
|
|
1605
|
+
return columns[method](column => {
|
|
1606
|
+
const columnFilters = filters[column];
|
|
1607
|
+
const columnFilterTypes = Object.keys(columnFilters);
|
|
1608
|
+
if (!feature || feature[column] === null || feature[column] === undefined) {
|
|
1609
|
+
return false;
|
|
1486
1610
|
}
|
|
1487
|
-
return
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1611
|
+
return columnFilterTypes.every(filter => {
|
|
1612
|
+
const filterFunction = filterFunctions[filter];
|
|
1613
|
+
if (!filterFunction) {
|
|
1614
|
+
throw new Error(`"${filter}" filter is not implemented.`);
|
|
1615
|
+
}
|
|
1616
|
+
return filterFunction(columnFilters[filter].values, feature[column], columnFilters[filter].params);
|
|
1617
|
+
});
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
function buildFeatureFilter(_ref) {
|
|
1621
|
+
let {
|
|
1622
|
+
filters = {},
|
|
1623
|
+
type = 'boolean',
|
|
1624
|
+
filtersLogicalOperator = 'and'
|
|
1625
|
+
} = _ref;
|
|
1626
|
+
const columns = Object.keys(filters);
|
|
1627
|
+
if (!columns.length) {
|
|
1628
|
+
return () => type === 'number' ? 1 : true;
|
|
1493
1629
|
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1630
|
+
return feature => {
|
|
1631
|
+
const f = feature.properties || feature;
|
|
1632
|
+
const featurePassesFilter = passesFilter(columns, filters, f, filtersLogicalOperator);
|
|
1633
|
+
return type === 'number' ? Number(featurePassesFilter) : featurePassesFilter;
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
// Apply certain filters to a collection of features
|
|
1637
|
+
function applyFilters(features, filters, filtersLogicalOperator) {
|
|
1638
|
+
return Object.keys(filters).length ? features.filter(buildFeatureFilter({
|
|
1639
|
+
filters,
|
|
1640
|
+
filtersLogicalOperator
|
|
1641
|
+
})) : features;
|
|
1642
|
+
}
|
|
1643
|
+
// Binary
|
|
1644
|
+
function buildBinaryFeatureFilter(_ref2) {
|
|
1645
|
+
let {
|
|
1646
|
+
filters = {}
|
|
1647
|
+
} = _ref2;
|
|
1648
|
+
const columns = Object.keys(filters);
|
|
1649
|
+
if (!columns.length) {
|
|
1650
|
+
return () => 1;
|
|
1508
1651
|
}
|
|
1652
|
+
return (featureIdIdx, binaryData) => passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
|
|
1653
|
+
}
|
|
1654
|
+
function getValueFromNumericProps(featureIdIdx, binaryData, _ref3) {
|
|
1655
|
+
let {
|
|
1656
|
+
column
|
|
1657
|
+
} = _ref3;
|
|
1658
|
+
return binaryData.numericProps?.[column]?.value[featureIdIdx];
|
|
1659
|
+
}
|
|
1660
|
+
function getValueFromProperties(featureIdIdx, binaryData, _ref4) {
|
|
1661
|
+
let {
|
|
1662
|
+
column
|
|
1663
|
+
} = _ref4;
|
|
1664
|
+
const propertyIdx = binaryData.featureIds.value[featureIdIdx];
|
|
1665
|
+
return binaryData.properties[propertyIdx]?.[column];
|
|
1666
|
+
}
|
|
1667
|
+
const GET_VALUE_BY_BINARY_PROP = {
|
|
1668
|
+
properties: getValueFromProperties,
|
|
1669
|
+
numericProps: getValueFromNumericProps
|
|
1509
1670
|
};
|
|
1671
|
+
function getBinaryPropertyByFilterValues(filterValues) {
|
|
1672
|
+
return typeof filterValues.flat()[0] === 'string' ? 'properties' : 'numericProps';
|
|
1673
|
+
}
|
|
1674
|
+
function getFeatureValue(featureIdIdx, binaryData, filter) {
|
|
1675
|
+
const {
|
|
1676
|
+
column,
|
|
1677
|
+
values
|
|
1678
|
+
} = filter;
|
|
1679
|
+
const binaryProp = getBinaryPropertyByFilterValues(values);
|
|
1680
|
+
const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
|
|
1681
|
+
return getFeatureValueFn(featureIdIdx, binaryData, {
|
|
1682
|
+
column
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
function passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData) {
|
|
1686
|
+
return columns.every(column => {
|
|
1687
|
+
const columnFilters = filters[column];
|
|
1688
|
+
return Object.entries(columnFilters).every(_ref5 => {
|
|
1689
|
+
let [type, {
|
|
1690
|
+
values
|
|
1691
|
+
}] = _ref5;
|
|
1692
|
+
const filterFn = filterFunctions[type];
|
|
1693
|
+
if (!filterFn) {
|
|
1694
|
+
throw new Error(`"${type}" filter is not implemented.`);
|
|
1695
|
+
}
|
|
1696
|
+
if (!values) return 0;
|
|
1697
|
+
const featureValue = getFeatureValue(featureIdIdx, binaryData, {
|
|
1698
|
+
type: type,
|
|
1699
|
+
column,
|
|
1700
|
+
values
|
|
1701
|
+
});
|
|
1702
|
+
if (featureValue === undefined || featureValue === null) return 0;
|
|
1703
|
+
return filterFn(values, featureValue);
|
|
1704
|
+
});
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1510
1707
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1708
|
+
function geojsonFeatures(_ref) {
|
|
1709
|
+
let {
|
|
1710
|
+
geojson,
|
|
1711
|
+
spatialFilter,
|
|
1712
|
+
uniqueIdProperty
|
|
1713
|
+
} = _ref;
|
|
1714
|
+
let uniqueIdx = 0;
|
|
1715
|
+
const map = new Map();
|
|
1716
|
+
if (!spatialFilter) {
|
|
1717
|
+
return [];
|
|
1718
|
+
}
|
|
1719
|
+
for (const feature of geojson.features) {
|
|
1720
|
+
const uniqueId = uniqueIdProperty ? feature.properties[uniqueIdProperty] : ++uniqueIdx;
|
|
1721
|
+
if (!map.has(uniqueId) && intersects(spatialFilter, feature)) {
|
|
1722
|
+
map.set(uniqueId, feature.properties);
|
|
1523
1723
|
}
|
|
1524
|
-
return Promise.resolve(baseSource('raster', options, urlParameters));
|
|
1525
|
-
} catch (e) {
|
|
1526
|
-
return Promise.reject(e);
|
|
1527
1724
|
}
|
|
1528
|
-
|
|
1725
|
+
return Array.from(map.values());
|
|
1726
|
+
}
|
|
1529
1727
|
|
|
1530
|
-
//
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
if (aggregationResLevel) {
|
|
1548
|
-
urlParameters.aggregationResLevel = String(aggregationResLevel);
|
|
1549
|
-
}
|
|
1550
|
-
if (queryParameters) {
|
|
1551
|
-
urlParameters.queryParameters = queryParameters;
|
|
1552
|
-
}
|
|
1553
|
-
if (filters) {
|
|
1554
|
-
urlParameters.filters = filters;
|
|
1555
|
-
}
|
|
1556
|
-
return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
|
|
1557
|
-
...result,
|
|
1558
|
-
widgetSource: new WidgetQuerySource(options)
|
|
1559
|
-
})));
|
|
1560
|
-
} catch (e) {
|
|
1561
|
-
return Promise.reject(e);
|
|
1728
|
+
// math.gl
|
|
1729
|
+
// SPDX-License-Identifier: MIT
|
|
1730
|
+
// Copyright (c) vis.gl contributors
|
|
1731
|
+
const DEFAULT_CONFIG = {
|
|
1732
|
+
EPSILON: 1e-12,
|
|
1733
|
+
debug: false,
|
|
1734
|
+
precision: 4,
|
|
1735
|
+
printTypes: false,
|
|
1736
|
+
printDegrees: false,
|
|
1737
|
+
printRowMajor: true,
|
|
1738
|
+
_cartographicRadians: false
|
|
1739
|
+
};
|
|
1740
|
+
// Configuration is truly global as of v3.6 to ensure single config even if multiple copies of math.gl
|
|
1741
|
+
// Multiple copies of config can be quite tricky to debug...
|
|
1742
|
+
globalThis.mathgl = globalThis.mathgl || {
|
|
1743
|
+
config: {
|
|
1744
|
+
...DEFAULT_CONFIG
|
|
1562
1745
|
}
|
|
1563
1746
|
};
|
|
1747
|
+
/**
|
|
1748
|
+
* Check if value is an "array"
|
|
1749
|
+
* Returns `true` if value is either an array or a typed array
|
|
1750
|
+
* Note: returns `false` for `ArrayBuffer` and `DataView` instances
|
|
1751
|
+
* @note isTypedArray and isNumericArray are often more useful in TypeScript
|
|
1752
|
+
*/
|
|
1753
|
+
function isArray(value) {
|
|
1754
|
+
return Array.isArray(value) || ArrayBuffer.isView(value) && !(value instanceof DataView);
|
|
1755
|
+
}
|
|
1756
|
+
function lerp(a, b, t) {
|
|
1757
|
+
if (isArray(a)) {
|
|
1758
|
+
return a.map((ai, i) => lerp(ai, b[i], t));
|
|
1759
|
+
}
|
|
1760
|
+
return t * b + (1 - t) * a;
|
|
1761
|
+
}
|
|
1564
1762
|
|
|
1565
|
-
//
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1763
|
+
// Replacement for the external assert method to reduce bundle size
|
|
1764
|
+
// Note: We don't use the second "message" argument in calling code,
|
|
1765
|
+
// so no need to support it here
|
|
1766
|
+
function assert(condition, message) {
|
|
1767
|
+
if (!condition) {
|
|
1768
|
+
throw new Error(message || '@math.gl/web-mercator: assertion failed.');
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
// TODO - THE UTILITIES IN THIS FILE SHOULD BE IMPORTED FROM WEB-MERCATOR-VIEWPORT MODULE
|
|
1773
|
+
// CONSTANTS
|
|
1774
|
+
const PI = Math.PI;
|
|
1775
|
+
const PI_4 = PI / 4;
|
|
1776
|
+
const DEGREES_TO_RADIANS = PI / 180;
|
|
1777
|
+
const RADIANS_TO_DEGREES = 180 / PI;
|
|
1778
|
+
const TILE_SIZE = 512;
|
|
1779
|
+
/**
|
|
1780
|
+
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
|
|
1781
|
+
* Performs the nonlinear part of the web mercator projection.
|
|
1782
|
+
* Remaining projection is done with 4x4 matrices which also handles
|
|
1783
|
+
* perspective.
|
|
1784
|
+
*
|
|
1785
|
+
* @param lngLat - [lng, lat] coordinates
|
|
1786
|
+
* Specifies a point on the sphere to project onto the map.
|
|
1787
|
+
* @return [x,y] coordinates.
|
|
1788
|
+
*/
|
|
1789
|
+
function lngLatToWorld(lngLat) {
|
|
1790
|
+
const [lng, lat] = lngLat;
|
|
1791
|
+
assert(Number.isFinite(lng));
|
|
1792
|
+
assert(Number.isFinite(lat) && lat >= -90 && lat <= 90, 'invalid latitude');
|
|
1793
|
+
const lambda2 = lng * DEGREES_TO_RADIANS;
|
|
1794
|
+
const phi2 = lat * DEGREES_TO_RADIANS;
|
|
1795
|
+
const x = TILE_SIZE * (lambda2 + PI) / (2 * PI);
|
|
1796
|
+
const y = TILE_SIZE * (PI + Math.log(Math.tan(PI_4 + phi2 * 0.5))) / (2 * PI);
|
|
1797
|
+
return [x, y];
|
|
1798
|
+
}
|
|
1799
|
+
/**
|
|
1800
|
+
* Unproject world point [x,y] on map onto {lat, lon} on sphere
|
|
1801
|
+
*
|
|
1802
|
+
* @param xy - array with [x,y] members
|
|
1803
|
+
* representing point on projected map plane
|
|
1804
|
+
* @return - array with [x,y] of point on sphere.
|
|
1805
|
+
* Has toArray method if you need a GeoJSON Array.
|
|
1806
|
+
* Per cartographic tradition, lat and lon are specified as degrees.
|
|
1807
|
+
*/
|
|
1808
|
+
function worldToLngLat(xy) {
|
|
1809
|
+
const [x, y] = xy;
|
|
1810
|
+
const lambda2 = x / TILE_SIZE * (2 * PI) - PI;
|
|
1811
|
+
const phi2 = 2 * (Math.atan(Math.exp(y / TILE_SIZE * (2 * PI) - PI)) - PI_4);
|
|
1812
|
+
return [lambda2 * RADIANS_TO_DEGREES, phi2 * RADIANS_TO_DEGREES];
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
const TRANSFORM_FN$1 = {
|
|
1816
|
+
Point: transformPoint$1,
|
|
1817
|
+
MultiPoint: transformMultiPoint$1,
|
|
1818
|
+
LineString: transformLineString$1,
|
|
1819
|
+
MultiLineString: transformMultiLineString$1,
|
|
1820
|
+
Polygon: transformPolygon$1,
|
|
1821
|
+
MultiPolygon: transformMultiPolygon$1
|
|
1822
|
+
};
|
|
1823
|
+
/**
|
|
1824
|
+
* Transform WGS84 coordinates to tile coords.
|
|
1825
|
+
* It's the inverse of deck.gl coordinate-transform (https://github.com/visgl/deck.gl/blob/master/modules/geo-layers/src/mvt-layer/coordinate-transform.js)
|
|
1826
|
+
*
|
|
1827
|
+
* @param geometry - any valid geojson geometry
|
|
1828
|
+
* @param bbox - geojson bbox
|
|
1829
|
+
*/
|
|
1830
|
+
function transformToTileCoords(geometry, bbox) {
|
|
1831
|
+
const [west, south, east, north] = bbox;
|
|
1832
|
+
const nw = projectFlat([west, north]);
|
|
1833
|
+
const se = projectFlat([east, south]);
|
|
1834
|
+
const projectedBbox = [nw, se];
|
|
1835
|
+
if (geometry.type === 'GeometryCollection') {
|
|
1836
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
1837
|
+
}
|
|
1838
|
+
const transformFn = TRANSFORM_FN$1[geometry.type];
|
|
1839
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
1840
|
+
return {
|
|
1841
|
+
...geometry,
|
|
1842
|
+
coordinates
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
function transformPoint$1(_ref, _ref2) {
|
|
1846
|
+
let [pointX, pointY] = _ref;
|
|
1847
|
+
let [nw, se] = _ref2;
|
|
1848
|
+
const x = inverseLerp(nw[0], se[0], pointX);
|
|
1849
|
+
const y = inverseLerp(nw[1], se[1], pointY);
|
|
1850
|
+
return [x, y];
|
|
1851
|
+
}
|
|
1852
|
+
function getPoints$1(geometry, bbox) {
|
|
1853
|
+
return geometry.map(g => transformPoint$1(projectFlat(g), bbox));
|
|
1854
|
+
}
|
|
1855
|
+
function transformMultiPoint$1(multiPoint, bbox) {
|
|
1856
|
+
return getPoints$1(multiPoint, bbox);
|
|
1857
|
+
}
|
|
1858
|
+
function transformLineString$1(line, bbox) {
|
|
1859
|
+
return getPoints$1(line, bbox);
|
|
1860
|
+
}
|
|
1861
|
+
function transformMultiLineString$1(multiLineString, bbox) {
|
|
1862
|
+
return multiLineString.map(lineString => transformLineString$1(lineString, bbox));
|
|
1863
|
+
}
|
|
1864
|
+
function transformPolygon$1(polygon, bbox) {
|
|
1865
|
+
return polygon.map(polygonRing => getPoints$1(polygonRing, bbox));
|
|
1866
|
+
}
|
|
1867
|
+
function transformMultiPolygon$1(multiPolygon, bbox) {
|
|
1868
|
+
return multiPolygon.map(polygon => transformPolygon$1(polygon, bbox));
|
|
1869
|
+
}
|
|
1870
|
+
function projectFlat(xyz) {
|
|
1871
|
+
return lngLatToWorld(xyz);
|
|
1872
|
+
}
|
|
1873
|
+
function inverseLerp(a, b, x) {
|
|
1874
|
+
return (x - a) / (b - a);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
const TRANSFORM_FN = {
|
|
1878
|
+
Point: transformPoint,
|
|
1879
|
+
MultiPoint: transformMultiPoint,
|
|
1880
|
+
LineString: transformLineString,
|
|
1881
|
+
MultiLineString: transformMultiLineString,
|
|
1882
|
+
Polygon: transformPolygon,
|
|
1883
|
+
MultiPolygon: transformMultiPolygon
|
|
1884
|
+
};
|
|
1885
|
+
/**
|
|
1886
|
+
* Transform tile coords to WGS84 coordinates.
|
|
1887
|
+
*
|
|
1888
|
+
* @param geometry - any valid geojson geometry
|
|
1889
|
+
* @param bbox - geojson bbox
|
|
1890
|
+
*/
|
|
1891
|
+
function transformTileCoordsToWGS84(geometry, bbox) {
|
|
1892
|
+
const [west, south, east, north] = bbox;
|
|
1893
|
+
const nw = lngLatToWorld([west, north]);
|
|
1894
|
+
const se = lngLatToWorld([east, south]);
|
|
1895
|
+
const projectedBbox = [nw, se];
|
|
1896
|
+
if (geometry.type === 'GeometryCollection') {
|
|
1897
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
1898
|
+
}
|
|
1899
|
+
const transformFn = TRANSFORM_FN[geometry.type];
|
|
1900
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
1901
|
+
return {
|
|
1902
|
+
...geometry,
|
|
1903
|
+
coordinates
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
function transformPoint(_ref, _ref2) {
|
|
1907
|
+
let [pointX, pointY] = _ref;
|
|
1908
|
+
let [nw, se] = _ref2;
|
|
1909
|
+
const x = lerp(nw[0], se[0], pointX);
|
|
1910
|
+
const y = lerp(nw[1], se[1], pointY);
|
|
1911
|
+
return worldToLngLat([x, y]);
|
|
1912
|
+
}
|
|
1913
|
+
function getPoints(geometry, bbox) {
|
|
1914
|
+
return geometry.map(g => transformPoint(g, bbox));
|
|
1915
|
+
}
|
|
1916
|
+
function transformMultiPoint(multiPoint, bbox) {
|
|
1917
|
+
return getPoints(multiPoint, bbox);
|
|
1918
|
+
}
|
|
1919
|
+
function transformLineString(line, bbox) {
|
|
1920
|
+
return getPoints(line, bbox);
|
|
1921
|
+
}
|
|
1922
|
+
function transformMultiLineString(multiLineString, bbox) {
|
|
1923
|
+
return multiLineString.map(lineString => transformLineString(lineString, bbox));
|
|
1924
|
+
}
|
|
1925
|
+
function transformPolygon(polygon, bbox) {
|
|
1926
|
+
return polygon.map(polygonRing => getPoints(polygonRing, bbox));
|
|
1927
|
+
}
|
|
1928
|
+
function transformMultiPolygon(multiPolygon, bbox) {
|
|
1929
|
+
return multiPolygon.map(polygon => transformPolygon(polygon, bbox));
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
const FEATURE_GEOM_PROPERTY = '__geomValue';
|
|
1933
|
+
function tileFeaturesGeometries(_ref) {
|
|
1934
|
+
let {
|
|
1935
|
+
tiles,
|
|
1936
|
+
tileFormat,
|
|
1937
|
+
spatialFilter,
|
|
1938
|
+
uniqueIdProperty,
|
|
1939
|
+
options
|
|
1940
|
+
} = _ref;
|
|
1941
|
+
const map = new Map();
|
|
1942
|
+
for (const tile of tiles) {
|
|
1943
|
+
// Discard if it's not a visible tile (only check false value, not undefined)
|
|
1944
|
+
// or tile has not data
|
|
1945
|
+
if (tile.isVisible === false || !tile.data) {
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
const bbox = [tile.bbox.west, tile.bbox.south, tile.bbox.east, tile.bbox.north];
|
|
1949
|
+
const bboxToGeom = bboxPolygon(bbox);
|
|
1950
|
+
const tileIsFullyVisible = booleanWithin(bboxToGeom, spatialFilter);
|
|
1951
|
+
// Clip the geometry to intersect with the tile
|
|
1952
|
+
const spatialFilterFeature = {
|
|
1953
|
+
type: 'Feature',
|
|
1954
|
+
geometry: spatialFilter,
|
|
1955
|
+
properties: {}
|
|
1956
|
+
};
|
|
1957
|
+
const clippedGeometryToIntersect = intersect(helpers.featureCollection([bboxToGeom, spatialFilterFeature]));
|
|
1958
|
+
if (!clippedGeometryToIntersect) {
|
|
1959
|
+
continue;
|
|
1960
|
+
}
|
|
1961
|
+
// We assume that MVT tileFormat uses local coordinates so we transform the geometry to intersect to tile coordinates [0..1],
|
|
1962
|
+
// while in the case of 'geojson' or binary, the geometries are already in WGS84
|
|
1963
|
+
const transformedGeometryToIntersect = tileFormat === exports.TileFormat.MVT ? transformToTileCoords(clippedGeometryToIntersect.geometry, bbox) : clippedGeometryToIntersect.geometry;
|
|
1964
|
+
createIndicesForPoints(tile.data.points);
|
|
1965
|
+
calculateFeatures({
|
|
1966
|
+
map,
|
|
1967
|
+
tileIsFullyVisible,
|
|
1968
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
1969
|
+
data: tile.data.points,
|
|
1970
|
+
type: 'Point',
|
|
1971
|
+
bbox,
|
|
1972
|
+
tileFormat,
|
|
1973
|
+
uniqueIdProperty,
|
|
1974
|
+
options
|
|
1975
|
+
});
|
|
1976
|
+
calculateFeatures({
|
|
1977
|
+
map,
|
|
1978
|
+
tileIsFullyVisible,
|
|
1979
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
1980
|
+
data: tile.data.lines,
|
|
1981
|
+
type: 'LineString',
|
|
1982
|
+
bbox,
|
|
1983
|
+
tileFormat,
|
|
1984
|
+
uniqueIdProperty,
|
|
1985
|
+
options
|
|
1986
|
+
});
|
|
1987
|
+
calculateFeatures({
|
|
1988
|
+
map,
|
|
1989
|
+
tileIsFullyVisible,
|
|
1990
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
1991
|
+
data: tile.data.polygons,
|
|
1992
|
+
type: 'Polygon',
|
|
1993
|
+
bbox,
|
|
1994
|
+
tileFormat,
|
|
1995
|
+
uniqueIdProperty,
|
|
1996
|
+
options
|
|
1997
|
+
});
|
|
1998
|
+
}
|
|
1999
|
+
return Array.from(map.values());
|
|
2000
|
+
}
|
|
2001
|
+
function processTileFeatureProperties(_ref2) {
|
|
2002
|
+
let {
|
|
2003
|
+
map,
|
|
2004
|
+
data,
|
|
2005
|
+
startIndex,
|
|
2006
|
+
endIndex,
|
|
2007
|
+
type,
|
|
2008
|
+
bbox,
|
|
2009
|
+
tileFormat,
|
|
2010
|
+
uniqueIdProperty,
|
|
2011
|
+
storeGeometry,
|
|
2012
|
+
geometryIntersection
|
|
2013
|
+
} = _ref2;
|
|
2014
|
+
const tileProps = getPropertiesFromTile(data, startIndex);
|
|
2015
|
+
const uniquePropertyValue = getUniquePropertyValue(tileProps, uniqueIdProperty, map);
|
|
2016
|
+
if (!uniquePropertyValue || map.has(uniquePropertyValue)) {
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
let geometry = null;
|
|
2020
|
+
// Only calculate geometry if necessary
|
|
2021
|
+
if (storeGeometry || geometryIntersection) {
|
|
2022
|
+
const {
|
|
2023
|
+
positions
|
|
2024
|
+
} = data;
|
|
2025
|
+
const ringCoordinates = getRingCoordinatesFor(startIndex, endIndex, positions);
|
|
2026
|
+
geometry = getFeatureByType(ringCoordinates, type);
|
|
2027
|
+
}
|
|
2028
|
+
// If intersection is required, check before proceeding
|
|
2029
|
+
if (geometry && geometryIntersection && !intersects(geometry, geometryIntersection)) {
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
const properties = parseProperties(tileProps);
|
|
2033
|
+
// Only save geometry if necessary
|
|
2034
|
+
if (storeGeometry && geometry) {
|
|
2035
|
+
properties[FEATURE_GEOM_PROPERTY] = tileFormat === exports.TileFormat.MVT ? transformTileCoordsToWGS84(geometry, bbox) : geometry;
|
|
2036
|
+
}
|
|
2037
|
+
map.set(uniquePropertyValue, properties);
|
|
2038
|
+
}
|
|
2039
|
+
function addIntersectedFeaturesInTile(_ref3) {
|
|
2040
|
+
let {
|
|
2041
|
+
map,
|
|
2042
|
+
data,
|
|
2043
|
+
geometryIntersection,
|
|
2044
|
+
type,
|
|
2045
|
+
bbox,
|
|
2046
|
+
tileFormat,
|
|
2047
|
+
uniqueIdProperty,
|
|
2048
|
+
options
|
|
2049
|
+
} = _ref3;
|
|
2050
|
+
const indices = getIndices(data);
|
|
2051
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
2052
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
2053
|
+
const startIndex = indices[i];
|
|
2054
|
+
const endIndex = indices[i + 1];
|
|
2055
|
+
processTileFeatureProperties({
|
|
2056
|
+
map,
|
|
2057
|
+
data,
|
|
2058
|
+
startIndex,
|
|
2059
|
+
endIndex,
|
|
2060
|
+
type,
|
|
2061
|
+
bbox,
|
|
2062
|
+
tileFormat,
|
|
2063
|
+
uniqueIdProperty,
|
|
2064
|
+
storeGeometry,
|
|
2065
|
+
geometryIntersection
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
function getIndices(data) {
|
|
2070
|
+
let indices;
|
|
2071
|
+
switch (data.type) {
|
|
2072
|
+
case 'Point':
|
|
2073
|
+
// @ts-expect-error Missing or changed types?
|
|
2074
|
+
indices = data.pointIndices;
|
|
2075
|
+
break;
|
|
2076
|
+
case 'LineString':
|
|
2077
|
+
indices = data.pathIndices;
|
|
2078
|
+
break;
|
|
2079
|
+
case 'Polygon':
|
|
2080
|
+
indices = data.primitivePolygonIndices;
|
|
2081
|
+
break;
|
|
2082
|
+
default:
|
|
2083
|
+
throw new Error(`Unexpected type, "${data.type}"`);
|
|
2084
|
+
}
|
|
2085
|
+
return indices.value;
|
|
2086
|
+
}
|
|
2087
|
+
function getFeatureId(data, startIndex) {
|
|
2088
|
+
return data.featureIds.value[startIndex];
|
|
2089
|
+
}
|
|
2090
|
+
function getPropertiesFromTile(data, startIndex) {
|
|
2091
|
+
const featureId = getFeatureId(data, startIndex);
|
|
2092
|
+
const {
|
|
2093
|
+
properties,
|
|
2094
|
+
numericProps,
|
|
2095
|
+
fields
|
|
2096
|
+
} = data;
|
|
2097
|
+
const result = {
|
|
2098
|
+
uniqueId: fields?.[featureId]?.id,
|
|
2099
|
+
properties: properties[featureId],
|
|
2100
|
+
numericProps: {}
|
|
2101
|
+
};
|
|
2102
|
+
for (const key in numericProps) {
|
|
2103
|
+
result.numericProps[key] = numericProps[key].value[startIndex];
|
|
2104
|
+
}
|
|
2105
|
+
return result;
|
|
2106
|
+
}
|
|
2107
|
+
function parseProperties(tileProps) {
|
|
2108
|
+
const {
|
|
2109
|
+
properties,
|
|
2110
|
+
numericProps
|
|
2111
|
+
} = tileProps;
|
|
2112
|
+
return Object.assign({}, properties, numericProps);
|
|
2113
|
+
}
|
|
2114
|
+
function getUniquePropertyValue(tileProps, uniqueIdProperty, map) {
|
|
2115
|
+
if (uniqueIdProperty) {
|
|
2116
|
+
return getValueFromTileProps(tileProps, uniqueIdProperty);
|
|
2117
|
+
}
|
|
2118
|
+
if (tileProps.uniqueId) {
|
|
2119
|
+
return tileProps.uniqueId;
|
|
2120
|
+
}
|
|
2121
|
+
const artificialId = map.size + 1; // a counter, assumed as a valid new id
|
|
2122
|
+
return getValueFromTileProps(tileProps, 'cartodb_id') || getValueFromTileProps(tileProps, 'geoid') || artificialId;
|
|
2123
|
+
}
|
|
2124
|
+
function getValueFromTileProps(tileProps, propertyName) {
|
|
2125
|
+
const {
|
|
2126
|
+
properties,
|
|
2127
|
+
numericProps
|
|
2128
|
+
} = tileProps;
|
|
2129
|
+
return numericProps[propertyName] || properties[propertyName];
|
|
2130
|
+
}
|
|
2131
|
+
function getFeatureByType(coordinates, type) {
|
|
2132
|
+
switch (type) {
|
|
2133
|
+
case 'Polygon':
|
|
2134
|
+
return {
|
|
2135
|
+
type: 'Polygon',
|
|
2136
|
+
coordinates: [coordinates]
|
|
2137
|
+
};
|
|
2138
|
+
case 'LineString':
|
|
2139
|
+
return {
|
|
2140
|
+
type: 'LineString',
|
|
2141
|
+
coordinates
|
|
2142
|
+
};
|
|
2143
|
+
case 'Point':
|
|
2144
|
+
return {
|
|
2145
|
+
type: 'Point',
|
|
2146
|
+
coordinates: coordinates[0]
|
|
2147
|
+
};
|
|
2148
|
+
default:
|
|
2149
|
+
throw new Error('Invalid geometry type');
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
function getRingCoordinatesFor(startIndex, endIndex, positions) {
|
|
2153
|
+
const ringCoordinates = [];
|
|
2154
|
+
for (let j = startIndex; j < endIndex; j++) {
|
|
2155
|
+
ringCoordinates.push(Array.from(positions.value.subarray(j * positions.size, (j + 1) * positions.size)));
|
|
2156
|
+
}
|
|
2157
|
+
return ringCoordinates;
|
|
2158
|
+
}
|
|
2159
|
+
function calculateFeatures(_ref4) {
|
|
2160
|
+
let {
|
|
2161
|
+
map,
|
|
2162
|
+
tileIsFullyVisible,
|
|
2163
|
+
geometryIntersection,
|
|
2164
|
+
data,
|
|
2165
|
+
type,
|
|
2166
|
+
bbox,
|
|
2167
|
+
tileFormat,
|
|
2168
|
+
uniqueIdProperty,
|
|
2169
|
+
options
|
|
2170
|
+
} = _ref4;
|
|
2171
|
+
if (!data?.properties.length) {
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
if (tileIsFullyVisible) {
|
|
2175
|
+
addAllFeaturesInTile({
|
|
2176
|
+
map,
|
|
2177
|
+
data,
|
|
2178
|
+
type,
|
|
2179
|
+
bbox,
|
|
2180
|
+
tileFormat,
|
|
2181
|
+
uniqueIdProperty,
|
|
2182
|
+
options
|
|
2183
|
+
});
|
|
2184
|
+
} else {
|
|
2185
|
+
addIntersectedFeaturesInTile({
|
|
2186
|
+
map,
|
|
2187
|
+
data,
|
|
2188
|
+
geometryIntersection,
|
|
2189
|
+
type,
|
|
2190
|
+
bbox,
|
|
2191
|
+
tileFormat,
|
|
2192
|
+
uniqueIdProperty,
|
|
2193
|
+
options
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
function addAllFeaturesInTile(_ref5) {
|
|
2198
|
+
let {
|
|
2199
|
+
map,
|
|
2200
|
+
data,
|
|
2201
|
+
type,
|
|
2202
|
+
bbox,
|
|
2203
|
+
tileFormat,
|
|
2204
|
+
uniqueIdProperty,
|
|
2205
|
+
options
|
|
2206
|
+
} = _ref5;
|
|
2207
|
+
const indices = getIndices(data);
|
|
2208
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
2209
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
2210
|
+
const startIndex = indices[i];
|
|
2211
|
+
const endIndex = indices[i + 1];
|
|
2212
|
+
processTileFeatureProperties({
|
|
2213
|
+
map,
|
|
2214
|
+
data,
|
|
2215
|
+
startIndex,
|
|
2216
|
+
endIndex,
|
|
2217
|
+
type,
|
|
2218
|
+
bbox,
|
|
2219
|
+
tileFormat,
|
|
2220
|
+
uniqueIdProperty,
|
|
2221
|
+
storeGeometry
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function createIndicesForPoints(data) {
|
|
2226
|
+
const featureIds = data.featureIds.value;
|
|
2227
|
+
const lastFeatureId = featureIds[featureIds.length - 1];
|
|
2228
|
+
const PointIndicesArray = featureIds.constructor;
|
|
2229
|
+
const pointIndices = {
|
|
2230
|
+
value: new PointIndicesArray(featureIds.length + 1),
|
|
2231
|
+
size: 1
|
|
2232
|
+
};
|
|
2233
|
+
pointIndices.value.set(featureIds);
|
|
2234
|
+
pointIndices.value.set([lastFeatureId + 1], featureIds.length);
|
|
2235
|
+
// @ts-expect-error Missing or changed types?
|
|
2236
|
+
data.pointIndices = pointIndices;
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
// a tile is an array [x,y,z]
|
|
2240
|
+
var d2r = Math.PI / 180,
|
|
2241
|
+
r2d = 180 / Math.PI;
|
|
2242
|
+
function tileToBBOX(tile) {
|
|
2243
|
+
var e = tile2lon(tile[0] + 1, tile[2]);
|
|
2244
|
+
var w = tile2lon(tile[0], tile[2]);
|
|
2245
|
+
var s = tile2lat(tile[1] + 1, tile[2]);
|
|
2246
|
+
var n = tile2lat(tile[1], tile[2]);
|
|
2247
|
+
return [w, s, e, n];
|
|
2248
|
+
}
|
|
2249
|
+
function tileToGeoJSON(tile) {
|
|
2250
|
+
var bbox = tileToBBOX(tile);
|
|
2251
|
+
var poly = {
|
|
2252
|
+
type: 'Polygon',
|
|
2253
|
+
coordinates: [[[bbox[0], bbox[1]], [bbox[0], bbox[3]], [bbox[2], bbox[3]], [bbox[2], bbox[1]], [bbox[0], bbox[1]]]]
|
|
2254
|
+
};
|
|
2255
|
+
return poly;
|
|
2256
|
+
}
|
|
2257
|
+
function tile2lon(x, z) {
|
|
2258
|
+
return x / Math.pow(2, z) * 360 - 180;
|
|
2259
|
+
}
|
|
2260
|
+
function tile2lat(y, z) {
|
|
2261
|
+
var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
|
|
2262
|
+
return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
|
2263
|
+
}
|
|
2264
|
+
function pointToTile(lon, lat, z) {
|
|
2265
|
+
var tile = pointToTileFraction(lon, lat, z);
|
|
2266
|
+
tile[0] = Math.floor(tile[0]);
|
|
2267
|
+
tile[1] = Math.floor(tile[1]);
|
|
2268
|
+
return tile;
|
|
2269
|
+
}
|
|
2270
|
+
function getChildren(tile) {
|
|
2271
|
+
return [[tile[0] * 2, tile[1] * 2, tile[2] + 1], [tile[0] * 2 + 1, tile[1] * 2, tile[2] + 1], [tile[0] * 2 + 1, tile[1] * 2 + 1, tile[2] + 1], [tile[0] * 2, tile[1] * 2 + 1, tile[2] + 1]];
|
|
2272
|
+
}
|
|
2273
|
+
function getParent(tile) {
|
|
2274
|
+
// top left
|
|
2275
|
+
if (tile[0] % 2 === 0 && tile[1] % 2 === 0) {
|
|
2276
|
+
return [tile[0] / 2, tile[1] / 2, tile[2] - 1];
|
|
2277
|
+
}
|
|
2278
|
+
// bottom left
|
|
2279
|
+
else if (tile[0] % 2 === 0 && !tile[1] % 2 === 0) {
|
|
2280
|
+
return [tile[0] / 2, (tile[1] - 1) / 2, tile[2] - 1];
|
|
2281
|
+
}
|
|
2282
|
+
// top right
|
|
2283
|
+
else if (!tile[0] % 2 === 0 && tile[1] % 2 === 0) {
|
|
2284
|
+
return [(tile[0] - 1) / 2, tile[1] / 2, tile[2] - 1];
|
|
2285
|
+
}
|
|
2286
|
+
// bottom right
|
|
2287
|
+
else {
|
|
2288
|
+
return [(tile[0] - 1) / 2, (tile[1] - 1) / 2, tile[2] - 1];
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
function getSiblings(tile) {
|
|
2292
|
+
return getChildren(getParent(tile));
|
|
2293
|
+
}
|
|
2294
|
+
function hasSiblings(tile, tiles) {
|
|
2295
|
+
var siblings = getSiblings(tile);
|
|
2296
|
+
for (var i = 0; i < siblings.length; i++) {
|
|
2297
|
+
if (!hasTile(tiles, siblings[i])) return false;
|
|
2298
|
+
}
|
|
2299
|
+
return true;
|
|
2300
|
+
}
|
|
2301
|
+
function hasTile(tiles, tile) {
|
|
2302
|
+
for (var i = 0; i < tiles.length; i++) {
|
|
2303
|
+
if (tilesEqual(tiles[i], tile)) return true;
|
|
2304
|
+
}
|
|
2305
|
+
return false;
|
|
2306
|
+
}
|
|
2307
|
+
function tilesEqual(tile1, tile2) {
|
|
2308
|
+
return tile1[0] === tile2[0] && tile1[1] === tile2[1] && tile1[2] === tile2[2];
|
|
2309
|
+
}
|
|
2310
|
+
function tileToQuadkey(tile) {
|
|
2311
|
+
var index = '';
|
|
2312
|
+
for (var z = tile[2]; z > 0; z--) {
|
|
2313
|
+
var b = 0;
|
|
2314
|
+
var mask = 1 << z - 1;
|
|
2315
|
+
if ((tile[0] & mask) !== 0) b++;
|
|
2316
|
+
if ((tile[1] & mask) !== 0) b += 2;
|
|
2317
|
+
index += b.toString();
|
|
2318
|
+
}
|
|
2319
|
+
return index;
|
|
2320
|
+
}
|
|
2321
|
+
function quadkeyToTile(quadkey) {
|
|
2322
|
+
var x = 0;
|
|
2323
|
+
var y = 0;
|
|
2324
|
+
var z = quadkey.length;
|
|
2325
|
+
for (var i = z; i > 0; i--) {
|
|
2326
|
+
var mask = 1 << i - 1;
|
|
2327
|
+
switch (quadkey[z - i]) {
|
|
2328
|
+
case '0':
|
|
2329
|
+
break;
|
|
2330
|
+
case '1':
|
|
2331
|
+
x |= mask;
|
|
2332
|
+
break;
|
|
2333
|
+
case '2':
|
|
2334
|
+
y |= mask;
|
|
2335
|
+
break;
|
|
2336
|
+
case '3':
|
|
2337
|
+
x |= mask;
|
|
2338
|
+
y |= mask;
|
|
2339
|
+
break;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
return [x, y, z];
|
|
2343
|
+
}
|
|
2344
|
+
function bboxToTile(bboxCoords) {
|
|
2345
|
+
var min = pointToTile(bboxCoords[0], bboxCoords[1], 32);
|
|
2346
|
+
var max = pointToTile(bboxCoords[2], bboxCoords[3], 32);
|
|
2347
|
+
var bbox = [min[0], min[1], max[0], max[1]];
|
|
2348
|
+
var z = getBboxZoom(bbox);
|
|
2349
|
+
if (z === 0) return [0, 0, 0];
|
|
2350
|
+
var x = bbox[0] >>> 32 - z;
|
|
2351
|
+
var y = bbox[1] >>> 32 - z;
|
|
2352
|
+
return [x, y, z];
|
|
2353
|
+
}
|
|
2354
|
+
function getBboxZoom(bbox) {
|
|
2355
|
+
var MAX_ZOOM = 28;
|
|
2356
|
+
for (var z = 0; z < MAX_ZOOM; z++) {
|
|
2357
|
+
var mask = 1 << 32 - (z + 1);
|
|
2358
|
+
if ((bbox[0] & mask) != (bbox[2] & mask) || (bbox[1] & mask) != (bbox[3] & mask)) {
|
|
2359
|
+
return z;
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
return MAX_ZOOM;
|
|
2363
|
+
}
|
|
2364
|
+
function pointToTileFraction(lon, lat, z) {
|
|
2365
|
+
var sin = Math.sin(lat * d2r),
|
|
2366
|
+
z2 = Math.pow(2, z),
|
|
2367
|
+
x = z2 * (lon / 360 + 0.5),
|
|
2368
|
+
y = z2 * (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI);
|
|
2369
|
+
return [x, y, z];
|
|
2370
|
+
}
|
|
2371
|
+
var tilebelt = {
|
|
2372
|
+
tileToGeoJSON: tileToGeoJSON,
|
|
2373
|
+
tileToBBOX: tileToBBOX,
|
|
2374
|
+
getChildren: getChildren,
|
|
2375
|
+
getParent: getParent,
|
|
2376
|
+
getSiblings: getSiblings,
|
|
2377
|
+
hasTile: hasTile,
|
|
2378
|
+
hasSiblings: hasSiblings,
|
|
2379
|
+
tilesEqual: tilesEqual,
|
|
2380
|
+
tileToQuadkey: tileToQuadkey,
|
|
2381
|
+
quadkeyToTile: quadkeyToTile,
|
|
2382
|
+
pointToTile: pointToTile,
|
|
2383
|
+
bboxToTile: bboxToTile,
|
|
2384
|
+
pointToTileFraction: pointToTileFraction
|
|
2385
|
+
};
|
|
2386
|
+
|
|
2387
|
+
/**
|
|
2388
|
+
* Given a geometry, create cells and return them in their raw form,
|
|
2389
|
+
* as an array of cell identifiers.
|
|
2390
|
+
*
|
|
2391
|
+
* @alias tiles
|
|
2392
|
+
* @param {Object} geom GeoJSON geometry
|
|
2393
|
+
* @param {Object} limits an object with min_zoom and max_zoom properties
|
|
2394
|
+
* specifying the minimum and maximum level to be tiled.
|
|
2395
|
+
* @returns {Array<Array<number>>} An array of tiles given as [x, y, z] arrays
|
|
2396
|
+
*/
|
|
2397
|
+
var tiles = getTiles;
|
|
2398
|
+
function getTiles(geom, limits) {
|
|
2399
|
+
var i,
|
|
2400
|
+
tile,
|
|
2401
|
+
coords = geom.coordinates,
|
|
2402
|
+
maxZoom = limits.max_zoom,
|
|
2403
|
+
tileHash = {},
|
|
2404
|
+
tiles = [];
|
|
2405
|
+
if (geom.type === 'Point') {
|
|
2406
|
+
return [tilebelt.pointToTile(coords[0], coords[1], maxZoom)];
|
|
2407
|
+
} else if (geom.type === 'MultiPoint') {
|
|
2408
|
+
for (i = 0; i < coords.length; i++) {
|
|
2409
|
+
tile = tilebelt.pointToTile(coords[i][0], coords[i][1], maxZoom);
|
|
2410
|
+
tileHash[toID(tile[0], tile[1], tile[2])] = true;
|
|
2411
|
+
}
|
|
2412
|
+
} else if (geom.type === 'LineString') {
|
|
2413
|
+
lineCover(tileHash, coords, maxZoom);
|
|
2414
|
+
} else if (geom.type === 'MultiLineString') {
|
|
2415
|
+
for (i = 0; i < coords.length; i++) {
|
|
2416
|
+
lineCover(tileHash, coords[i], maxZoom);
|
|
2417
|
+
}
|
|
2418
|
+
} else if (geom.type === 'Polygon') {
|
|
2419
|
+
polygonCover(tileHash, tiles, coords, maxZoom);
|
|
2420
|
+
} else if (geom.type === 'MultiPolygon') {
|
|
2421
|
+
for (i = 0; i < coords.length; i++) {
|
|
2422
|
+
polygonCover(tileHash, tiles, coords[i], maxZoom);
|
|
2423
|
+
}
|
|
2424
|
+
} else {
|
|
2425
|
+
throw new Error('Geometry type not implemented');
|
|
2426
|
+
}
|
|
2427
|
+
if (limits.min_zoom !== maxZoom) {
|
|
2428
|
+
// sync tile hash and tile array so that both contain the same tiles
|
|
2429
|
+
var len = tiles.length;
|
|
2430
|
+
appendHashTiles(tileHash, tiles);
|
|
2431
|
+
for (i = 0; i < len; i++) {
|
|
2432
|
+
var t = tiles[i];
|
|
2433
|
+
tileHash[toID(t[0], t[1], t[2])] = true;
|
|
2434
|
+
}
|
|
2435
|
+
return mergeTiles(tileHash, tiles, limits);
|
|
2436
|
+
}
|
|
2437
|
+
appendHashTiles(tileHash, tiles);
|
|
2438
|
+
return tiles;
|
|
2439
|
+
}
|
|
2440
|
+
function mergeTiles(tileHash, tiles, limits) {
|
|
2441
|
+
var mergedTiles = [];
|
|
2442
|
+
for (var z = limits.max_zoom; z > limits.min_zoom; z--) {
|
|
2443
|
+
var parentTileHash = {};
|
|
2444
|
+
var parentTiles = [];
|
|
2445
|
+
for (var i = 0; i < tiles.length; i++) {
|
|
2446
|
+
var t = tiles[i];
|
|
2447
|
+
if (t[0] % 2 === 0 && t[1] % 2 === 0) {
|
|
2448
|
+
var id2 = toID(t[0] + 1, t[1], z),
|
|
2449
|
+
id3 = toID(t[0], t[1] + 1, z),
|
|
2450
|
+
id4 = toID(t[0] + 1, t[1] + 1, z);
|
|
2451
|
+
if (tileHash[id2] && tileHash[id3] && tileHash[id4]) {
|
|
2452
|
+
tileHash[toID(t[0], t[1], t[2])] = false;
|
|
2453
|
+
tileHash[id2] = false;
|
|
2454
|
+
tileHash[id3] = false;
|
|
2455
|
+
tileHash[id4] = false;
|
|
2456
|
+
var parentTile = [t[0] / 2, t[1] / 2, z - 1];
|
|
2457
|
+
if (z - 1 === limits.min_zoom) mergedTiles.push(parentTile);else {
|
|
2458
|
+
parentTileHash[toID(t[0] / 2, t[1] / 2, z - 1)] = true;
|
|
2459
|
+
parentTiles.push(parentTile);
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
for (i = 0; i < tiles.length; i++) {
|
|
2465
|
+
t = tiles[i];
|
|
2466
|
+
if (tileHash[toID(t[0], t[1], t[2])]) mergedTiles.push(t);
|
|
2467
|
+
}
|
|
2468
|
+
tileHash = parentTileHash;
|
|
2469
|
+
tiles = parentTiles;
|
|
2470
|
+
}
|
|
2471
|
+
return mergedTiles;
|
|
2472
|
+
}
|
|
2473
|
+
function polygonCover(tileHash, tileArray, geom, zoom) {
|
|
2474
|
+
var intersections = [];
|
|
2475
|
+
for (var i = 0; i < geom.length; i++) {
|
|
2476
|
+
var ring = [];
|
|
2477
|
+
lineCover(tileHash, geom[i], zoom, ring);
|
|
2478
|
+
for (var j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
|
|
2479
|
+
var m = (j + 1) % len;
|
|
2480
|
+
var y = ring[j][1];
|
|
2481
|
+
|
|
2482
|
+
// add interesction if it's not local extremum or duplicate
|
|
2483
|
+
if ((y > ring[k][1] || y > ring[m][1]) && (
|
|
2484
|
+
// not local minimum
|
|
2485
|
+
y < ring[k][1] || y < ring[m][1]) &&
|
|
2486
|
+
// not local maximum
|
|
2487
|
+
y !== ring[m][1]) intersections.push(ring[j]);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
intersections.sort(compareTiles); // sort by y, then x
|
|
2491
|
+
|
|
2492
|
+
for (i = 0; i < intersections.length; i += 2) {
|
|
2493
|
+
// fill tiles between pairs of intersections
|
|
2494
|
+
y = intersections[i][1];
|
|
2495
|
+
for (var x = intersections[i][0] + 1; x < intersections[i + 1][0]; x++) {
|
|
2496
|
+
var id = toID(x, y, zoom);
|
|
2497
|
+
if (!tileHash[id]) {
|
|
2498
|
+
tileArray.push([x, y, zoom]);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
function compareTiles(a, b) {
|
|
2504
|
+
return a[1] - b[1] || a[0] - b[0];
|
|
2505
|
+
}
|
|
2506
|
+
function lineCover(tileHash, coords, maxZoom, ring) {
|
|
2507
|
+
var prevX, prevY;
|
|
2508
|
+
for (var i = 0; i < coords.length - 1; i++) {
|
|
2509
|
+
var start = tilebelt.pointToTileFraction(coords[i][0], coords[i][1], maxZoom),
|
|
2510
|
+
stop = tilebelt.pointToTileFraction(coords[i + 1][0], coords[i + 1][1], maxZoom),
|
|
2511
|
+
x0 = start[0],
|
|
2512
|
+
y0 = start[1],
|
|
2513
|
+
x1 = stop[0],
|
|
2514
|
+
y1 = stop[1],
|
|
2515
|
+
dx = x1 - x0,
|
|
2516
|
+
dy = y1 - y0;
|
|
2517
|
+
if (dy === 0 && dx === 0) continue;
|
|
2518
|
+
var sx = dx > 0 ? 1 : -1,
|
|
2519
|
+
sy = dy > 0 ? 1 : -1,
|
|
2520
|
+
x = Math.floor(x0),
|
|
2521
|
+
y = Math.floor(y0),
|
|
2522
|
+
tMaxX = dx === 0 ? Infinity : Math.abs(((dx > 0 ? 1 : 0) + x - x0) / dx),
|
|
2523
|
+
tMaxY = dy === 0 ? Infinity : Math.abs(((dy > 0 ? 1 : 0) + y - y0) / dy),
|
|
2524
|
+
tdx = Math.abs(sx / dx),
|
|
2525
|
+
tdy = Math.abs(sy / dy);
|
|
2526
|
+
if (x !== prevX || y !== prevY) {
|
|
2527
|
+
tileHash[toID(x, y, maxZoom)] = true;
|
|
2528
|
+
if (ring && y !== prevY) ring.push([x, y]);
|
|
2529
|
+
prevX = x;
|
|
2530
|
+
prevY = y;
|
|
2531
|
+
}
|
|
2532
|
+
while (tMaxX < 1 || tMaxY < 1) {
|
|
2533
|
+
if (tMaxX < tMaxY) {
|
|
2534
|
+
tMaxX += tdx;
|
|
2535
|
+
x += sx;
|
|
2536
|
+
} else {
|
|
2537
|
+
tMaxY += tdy;
|
|
2538
|
+
y += sy;
|
|
2539
|
+
}
|
|
2540
|
+
tileHash[toID(x, y, maxZoom)] = true;
|
|
2541
|
+
if (ring && y !== prevY) ring.push([x, y]);
|
|
2542
|
+
prevX = x;
|
|
2543
|
+
prevY = y;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
if (ring && y === ring[0][1]) ring.pop();
|
|
2547
|
+
}
|
|
2548
|
+
function appendHashTiles(hash, tiles) {
|
|
2549
|
+
var keys = Object.keys(hash);
|
|
2550
|
+
for (var i = 0; i < keys.length; i++) {
|
|
2551
|
+
tiles.push(fromID(+keys[i]));
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
function toID(x, y, z) {
|
|
2555
|
+
var dim = 2 * (1 << z);
|
|
2556
|
+
return (dim * y + x) * 32 + z;
|
|
2557
|
+
}
|
|
2558
|
+
function fromID(id) {
|
|
2559
|
+
var z = id % 32,
|
|
2560
|
+
dim = 2 * (1 << z),
|
|
2561
|
+
xy = (id - z) / 32,
|
|
2562
|
+
x = xy % dim,
|
|
2563
|
+
y = (xy - x) / dim % dim;
|
|
2564
|
+
return [x, y, z];
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
const B = [0x5555555555555555n, 0x3333333333333333n, 0x0f0f0f0f0f0f0f0fn, 0x00ff00ff00ff00ffn, 0x0000ffff0000ffffn, 0x00000000ffffffffn];
|
|
2568
|
+
const S = [0n, 1n, 2n, 4n, 8n, 16n];
|
|
2569
|
+
function tileToCell(tile) {
|
|
2570
|
+
if (tile.z < 0 || tile.z > 26) {
|
|
2571
|
+
throw new Error('Wrong zoom');
|
|
2572
|
+
}
|
|
2573
|
+
const z = BigInt(tile.z);
|
|
2574
|
+
let x = BigInt(tile.x) << 32n - z;
|
|
2575
|
+
let y = BigInt(tile.y) << 32n - z;
|
|
2576
|
+
for (let i = 0; i < 5; i++) {
|
|
2577
|
+
const s = S[5 - i];
|
|
2578
|
+
const b = B[4 - i];
|
|
2579
|
+
x = (x | x << s) & b;
|
|
2580
|
+
y = (y | y << s) & b;
|
|
2581
|
+
}
|
|
2582
|
+
const quadbin = 0x4000000000000000n | 1n << 59n |
|
|
2583
|
+
// | (mode << 59) | (mode_dep << 57)
|
|
2584
|
+
z << 52n | (x | y << 1n) >> 12n | 0xfffffffffffffn >> z * 2n;
|
|
2585
|
+
return quadbin;
|
|
2586
|
+
}
|
|
2587
|
+
function getResolution$1(quadbin) {
|
|
2588
|
+
return quadbin >> 52n & 0x1fn;
|
|
2589
|
+
}
|
|
2590
|
+
function geometryToCells(geometry, resolution) {
|
|
2591
|
+
const zoom = Number(resolution);
|
|
2592
|
+
return tiles(geometry, {
|
|
2593
|
+
min_zoom: zoom,
|
|
2594
|
+
max_zoom: zoom
|
|
2595
|
+
}).map(([x, y, z]) => tileToCell({
|
|
2596
|
+
x,
|
|
2597
|
+
y,
|
|
2598
|
+
z
|
|
2599
|
+
}));
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
function tileFeaturesSpatialIndex(_ref) {
|
|
2603
|
+
let {
|
|
2604
|
+
tiles,
|
|
2605
|
+
spatialFilter,
|
|
2606
|
+
spatialDataColumn,
|
|
2607
|
+
spatialDataType
|
|
2608
|
+
} = _ref;
|
|
2609
|
+
const map = new Map();
|
|
2610
|
+
const spatialIndex = getSpatialIndex(spatialDataType);
|
|
2611
|
+
const resolution = getResolution(tiles, spatialIndex);
|
|
2612
|
+
const spatialIndexIDName = spatialDataColumn ? spatialDataColumn : spatialIndex;
|
|
2613
|
+
if (!resolution) {
|
|
2614
|
+
return [];
|
|
2615
|
+
}
|
|
2616
|
+
const cells = getCellsCoverGeometry(spatialFilter, spatialIndex, resolution);
|
|
2617
|
+
if (!cells?.length) {
|
|
2618
|
+
return [];
|
|
2619
|
+
}
|
|
2620
|
+
// We transform cells to Set to improve the performace
|
|
2621
|
+
const cellsSet = new Set(cells);
|
|
2622
|
+
for (const tile of tiles) {
|
|
2623
|
+
if (tile.isVisible === false || !tile.data) {
|
|
2624
|
+
continue;
|
|
2625
|
+
}
|
|
2626
|
+
tile.data.forEach(d => {
|
|
2627
|
+
if (cellsSet.has(d.id)) {
|
|
2628
|
+
map.set(d.id, {
|
|
2629
|
+
...d.properties,
|
|
2630
|
+
[spatialIndexIDName]: d.id
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
return Array.from(map.values());
|
|
2636
|
+
}
|
|
2637
|
+
function getResolution(tiles, spatialIndex) {
|
|
2638
|
+
const data = tiles.find(tile => tile.data?.length)?.data;
|
|
2639
|
+
if (!data) {
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
if (spatialIndex === exports.SpatialIndex.QUADBIN) {
|
|
2643
|
+
return Number(getResolution$1(data[0].id));
|
|
2644
|
+
}
|
|
2645
|
+
if (spatialIndex === exports.SpatialIndex.H3) {
|
|
2646
|
+
return h3Js.getResolution(data[0].id);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
const bboxWest = [-180, -90, 0, 90];
|
|
2650
|
+
const bboxEast = [0, -90, 180, 90];
|
|
2651
|
+
function getCellsCoverGeometry(geometry, spatialIndex, resolution) {
|
|
2652
|
+
if (spatialIndex === exports.SpatialIndex.QUADBIN) {
|
|
2653
|
+
// @ts-expect-error TODO: Probably ought to be stricter about number vs. bigint types in this file.
|
|
2654
|
+
return geometryToCells(geometry, resolution);
|
|
2655
|
+
}
|
|
2656
|
+
if (spatialIndex === exports.SpatialIndex.H3) {
|
|
2657
|
+
// The current H3 polyfill algorithm can't deal with polygon segments of greater than 180 degrees longitude
|
|
2658
|
+
// so we clip the geometry to be sure that none of them is greater than 180 degrees
|
|
2659
|
+
// https://github.com/uber/h3-js/issues/24#issuecomment-431893796
|
|
2660
|
+
return h3Js.polygonToCells(bboxClip(geometry, bboxWest).geometry.coordinates, resolution, true).concat(h3Js.polygonToCells(bboxClip(geometry, bboxEast).geometry.coordinates, resolution, true));
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
function getSpatialIndex(spatialDataType) {
|
|
2664
|
+
switch (spatialDataType) {
|
|
2665
|
+
case 'h3':
|
|
2666
|
+
return exports.SpatialIndex.H3;
|
|
2667
|
+
case 'quadbin':
|
|
2668
|
+
return exports.SpatialIndex.QUADBIN;
|
|
2669
|
+
default:
|
|
2670
|
+
throw new Error('Unexpected spatial data type');
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
2675
|
+
function tileFeatures(_ref) {
|
|
2676
|
+
let {
|
|
2677
|
+
tiles,
|
|
2678
|
+
spatialFilter,
|
|
2679
|
+
uniqueIdProperty,
|
|
2680
|
+
tileFormat,
|
|
2681
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
2682
|
+
spatialDataType,
|
|
2683
|
+
options = {}
|
|
2684
|
+
} = _ref;
|
|
2685
|
+
// TODO(cleanup): Is an empty response the expected result when spatialFilter
|
|
2686
|
+
// is omitted? Why not make the parameter required, or return the full input?
|
|
2687
|
+
if (!spatialFilter) {
|
|
2688
|
+
return [];
|
|
2689
|
+
}
|
|
2690
|
+
if (spatialDataType !== 'geo') {
|
|
2691
|
+
return tileFeaturesSpatialIndex({
|
|
2692
|
+
tiles: tiles,
|
|
2693
|
+
spatialFilter,
|
|
2694
|
+
spatialDataColumn,
|
|
2695
|
+
spatialDataType
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
return tileFeaturesGeometries({
|
|
2699
|
+
tiles,
|
|
2700
|
+
tileFormat,
|
|
2701
|
+
spatialFilter,
|
|
2702
|
+
uniqueIdProperty,
|
|
2703
|
+
options
|
|
2704
|
+
});
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
2708
|
+
const aggregationFunctions = {
|
|
2709
|
+
count: values => values.length,
|
|
2710
|
+
min: function () {
|
|
2711
|
+
return applyAggregationFunction(min, ...[].slice.call(arguments));
|
|
2712
|
+
},
|
|
2713
|
+
max: function () {
|
|
2714
|
+
return applyAggregationFunction(max, ...[].slice.call(arguments));
|
|
2715
|
+
},
|
|
2716
|
+
sum: function () {
|
|
2717
|
+
return applyAggregationFunction(sum, ...[].slice.call(arguments));
|
|
2718
|
+
},
|
|
2719
|
+
avg: function () {
|
|
2720
|
+
return applyAggregationFunction(avg, ...[].slice.call(arguments));
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
2724
|
+
function aggregate(feature, keys, operation) {
|
|
2725
|
+
if (!keys?.length) {
|
|
2726
|
+
throw new Error('Cannot aggregate a feature without having keys');
|
|
2727
|
+
} else if (keys.length === 1) {
|
|
2728
|
+
const value = feature[keys[0]];
|
|
2729
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
2730
|
+
}
|
|
2731
|
+
const aggregationFn = aggregationFunctions[operation];
|
|
2732
|
+
if (!aggregationFn) {
|
|
2733
|
+
throw new Error(`${operation} isn't a valid aggregation function`);
|
|
2734
|
+
}
|
|
2735
|
+
return aggregationFn(keys.map(column => {
|
|
2736
|
+
const value = feature[column];
|
|
2737
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
2738
|
+
}));
|
|
2739
|
+
}
|
|
2740
|
+
/*
|
|
2741
|
+
* Forced casting to Number (just of non empty strings) allows to work-around
|
|
2742
|
+
* some specific situations, where a big numeric field is transformed into a string when generating the tileset(eg.PG)
|
|
2743
|
+
*/
|
|
2744
|
+
function isPotentiallyValidNumber(value) {
|
|
2745
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
2746
|
+
}
|
|
2747
|
+
const applyAggregationFunction = (aggFn, values, keys, operation) => {
|
|
2748
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2749
|
+
const elements = (normalizedKeys?.length || 0) <= 1 ? filterFalsyElements(values, normalizedKeys || []) : values;
|
|
2750
|
+
return aggFn(elements, keys, operation);
|
|
2751
|
+
};
|
|
2752
|
+
function filterFalsyElements(values, keys) {
|
|
2753
|
+
const filterFn = value => value !== null && value !== undefined;
|
|
2754
|
+
if (!keys?.length) {
|
|
2755
|
+
return values.filter(filterFn);
|
|
2756
|
+
}
|
|
2757
|
+
return values.filter(v => filterFn(v[keys[0]]));
|
|
2758
|
+
}
|
|
2759
|
+
// Aggregation functions
|
|
2760
|
+
function avg(values, keys, joinOperation) {
|
|
2761
|
+
return sum(values, keys, joinOperation) / (values.length || 1);
|
|
2762
|
+
}
|
|
2763
|
+
function sum(values, keys, joinOperation) {
|
|
2764
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2765
|
+
if (normalizedKeys) {
|
|
2766
|
+
return values.reduce((a, b) => a + aggregate(b, normalizedKeys, joinOperation), 0);
|
|
2767
|
+
}
|
|
2768
|
+
return values.reduce((a, b) => a + b, 0);
|
|
2769
|
+
}
|
|
2770
|
+
function min(values, keys, joinOperation) {
|
|
2771
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2772
|
+
if (normalizedKeys) {
|
|
2773
|
+
return values.reduce((a, b) => Math.min(a, aggregate(b, normalizedKeys, joinOperation)), Infinity);
|
|
2774
|
+
}
|
|
2775
|
+
return Math.min(...values);
|
|
2776
|
+
}
|
|
2777
|
+
function max(values, keys, joinOperation) {
|
|
2778
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2779
|
+
if (normalizedKeys) {
|
|
2780
|
+
return values.reduce((a, b) => Math.max(a, aggregate(b, normalizedKeys, joinOperation)), -Infinity);
|
|
2781
|
+
}
|
|
2782
|
+
return Math.max(...values);
|
|
2783
|
+
}
|
|
2784
|
+
// Aux
|
|
2785
|
+
// Keys can come as a string (one column) or a strings array (multiple column)
|
|
2786
|
+
// Use always an array to make the code easier
|
|
2787
|
+
function normalizeKeys(keys) {
|
|
2788
|
+
return Array.isArray(keys) ? keys : typeof keys === 'string' ? [keys] : undefined;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
/***
|
|
2792
|
+
Copyright 2013 Teun Duynstee
|
|
2793
|
+
|
|
2794
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
2795
|
+
you may not use this file except in compliance with the License.
|
|
2796
|
+
You may obtain a copy of the License at
|
|
2797
|
+
|
|
2798
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
2799
|
+
|
|
2800
|
+
Unless required by applicable law or agreed to in writing, software
|
|
2801
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
2802
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2803
|
+
See the License for the specific language governing permissions and
|
|
2804
|
+
limitations under the License.
|
|
2805
|
+
*/
|
|
2806
|
+
var thenBy_module = function () {
|
|
2807
|
+
function identity(v) {
|
|
2808
|
+
return v;
|
|
2809
|
+
}
|
|
2810
|
+
function ignoreCase(v) {
|
|
2811
|
+
return typeof v === "string" ? v.toLowerCase() : v;
|
|
2812
|
+
}
|
|
2813
|
+
function makeCompareFunction(f, opt) {
|
|
2814
|
+
opt = typeof opt === "object" ? opt : {
|
|
2815
|
+
direction: opt
|
|
2816
|
+
};
|
|
2817
|
+
if (typeof f != "function") {
|
|
2818
|
+
var prop = f;
|
|
2819
|
+
// make unary function
|
|
2820
|
+
f = function (v1) {
|
|
2821
|
+
return !!v1[prop] ? v1[prop] : "";
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
if (f.length === 1) {
|
|
2825
|
+
// f is a unary function mapping a single item to its sort score
|
|
2826
|
+
var uf = f;
|
|
2827
|
+
var preprocess = opt.ignoreCase ? ignoreCase : identity;
|
|
2828
|
+
var cmp = opt.cmp || function (v1, v2) {
|
|
2829
|
+
return v1 < v2 ? -1 : v1 > v2 ? 1 : 0;
|
|
2830
|
+
};
|
|
2831
|
+
f = function (v1, v2) {
|
|
2832
|
+
return cmp(preprocess(uf(v1)), preprocess(uf(v2)));
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
const descTokens = {
|
|
2836
|
+
"-1": '',
|
|
2837
|
+
desc: ''
|
|
2838
|
+
};
|
|
2839
|
+
if (opt.direction in descTokens) return function (v1, v2) {
|
|
2840
|
+
return -f(v1, v2);
|
|
2841
|
+
};
|
|
2842
|
+
return f;
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
/* adds a secondary compare function to the target function (`this` context)
|
|
2846
|
+
which is applied in case the first one returns 0 (equal)
|
|
2847
|
+
returns a new compare function, which has a `thenBy` method as well */
|
|
2848
|
+
function tb(func, opt) {
|
|
2849
|
+
/* should get value false for the first call. This can be done by calling the
|
|
2850
|
+
exported function, or the firstBy property on it (for es6 module compatibility)
|
|
2851
|
+
*/
|
|
2852
|
+
var x = typeof this == "function" && !this.firstBy ? this : false;
|
|
2853
|
+
var y = makeCompareFunction(func, opt);
|
|
2854
|
+
var f = x ? function (a, b) {
|
|
2855
|
+
return x(a, b) || y(a, b);
|
|
2856
|
+
} : y;
|
|
2857
|
+
f.thenBy = tb;
|
|
2858
|
+
return f;
|
|
2859
|
+
}
|
|
2860
|
+
tb.firstBy = tb;
|
|
2861
|
+
return tb;
|
|
2862
|
+
}();
|
|
2863
|
+
|
|
2864
|
+
/**
|
|
2865
|
+
* Apply sort structure to a collection of features
|
|
2866
|
+
* @param features
|
|
2867
|
+
* @param [sortOptions]
|
|
2868
|
+
* @param [sortOptions.sortBy] - One or more columns to sort by
|
|
2869
|
+
* @param [sortOptions.sortByDirection] - Direction by the columns will be sorted
|
|
2870
|
+
* @param [sortOptions.sortByColumnType] - Column type
|
|
2871
|
+
* @internal
|
|
2872
|
+
* @internalRemarks Source: @carto/react-core
|
|
2873
|
+
*/
|
|
2874
|
+
function applySorting(features, _temp) {
|
|
2875
|
+
let {
|
|
2876
|
+
sortBy,
|
|
2877
|
+
sortByDirection = 'asc',
|
|
2878
|
+
sortByColumnType = 'string'
|
|
2879
|
+
} = _temp === void 0 ? {} : _temp;
|
|
2880
|
+
// If sortBy is undefined, pass all features
|
|
2881
|
+
if (sortBy === undefined) {
|
|
2882
|
+
return features;
|
|
2883
|
+
}
|
|
2884
|
+
// sortOptions exists, but are bad formatted
|
|
2885
|
+
const isValidSortBy = Array.isArray(sortBy) && sortBy.length ||
|
|
2886
|
+
// sortBy can be an array of columns
|
|
2887
|
+
typeof sortBy === 'string'; // or just one column
|
|
2888
|
+
if (!isValidSortBy) {
|
|
2889
|
+
throw new Error('Sorting options are bad formatted');
|
|
2890
|
+
}
|
|
2891
|
+
const sortFn = createSortFn({
|
|
2892
|
+
sortBy,
|
|
2893
|
+
sortByDirection,
|
|
2894
|
+
sortByColumnType: sortByColumnType || 'string'
|
|
2895
|
+
});
|
|
2896
|
+
return features.sort(sortFn);
|
|
2897
|
+
}
|
|
2898
|
+
// Aux
|
|
2899
|
+
function createSortFn(_ref) {
|
|
2900
|
+
let {
|
|
2901
|
+
sortBy,
|
|
2902
|
+
sortByDirection,
|
|
2903
|
+
sortByColumnType
|
|
2904
|
+
} = _ref;
|
|
2905
|
+
const [firstSortOption, ...othersSortOptions] = normalizeSortByOptions({
|
|
2906
|
+
sortBy,
|
|
2907
|
+
sortByDirection,
|
|
2908
|
+
sortByColumnType
|
|
2909
|
+
});
|
|
2910
|
+
let sortFn = thenBy_module.firstBy(...firstSortOption);
|
|
2911
|
+
for (let sortOptions of othersSortOptions) {
|
|
2912
|
+
sortFn = sortFn.thenBy(...sortOptions);
|
|
2913
|
+
}
|
|
2914
|
+
return sortFn;
|
|
2915
|
+
}
|
|
2916
|
+
function normalizeSortByOptions(_ref2) {
|
|
2917
|
+
let {
|
|
2918
|
+
sortBy,
|
|
2919
|
+
sortByDirection,
|
|
2920
|
+
sortByColumnType
|
|
2921
|
+
} = _ref2;
|
|
2922
|
+
const numberFormat = sortByColumnType === 'number' && {
|
|
2923
|
+
cmp: (a, b) => a - b
|
|
2924
|
+
};
|
|
2925
|
+
if (!Array.isArray(sortBy)) {
|
|
2926
|
+
sortBy = [sortBy];
|
|
2927
|
+
}
|
|
2928
|
+
return sortBy.map(sortByEl => {
|
|
2929
|
+
// sortByEl is 'column'
|
|
2930
|
+
if (typeof sortByEl === 'string') {
|
|
2931
|
+
return [sortByEl, {
|
|
2932
|
+
direction: sortByDirection,
|
|
2933
|
+
...numberFormat
|
|
2934
|
+
}];
|
|
2935
|
+
}
|
|
2936
|
+
if (Array.isArray(sortByEl)) {
|
|
2937
|
+
// sortBy is ['column']
|
|
2938
|
+
if (sortByEl[1] === undefined) {
|
|
2939
|
+
return [sortByEl, {
|
|
2940
|
+
direction: sortByDirection,
|
|
2941
|
+
...numberFormat
|
|
2942
|
+
}];
|
|
2943
|
+
}
|
|
2944
|
+
// sortBy is ['column', { ... }]
|
|
2945
|
+
if (typeof sortByEl[1] === 'object') {
|
|
2946
|
+
const othersSortOptions = numberFormat ? {
|
|
2947
|
+
...numberFormat,
|
|
2948
|
+
...sortByEl[1]
|
|
2949
|
+
} : sortByEl[1];
|
|
2950
|
+
return [sortByEl[0], {
|
|
2951
|
+
direction: sortByDirection,
|
|
2952
|
+
...othersSortOptions
|
|
2953
|
+
}];
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
return sortByEl;
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
2961
|
+
function groupValuesByColumn(_ref) {
|
|
2962
|
+
let {
|
|
2963
|
+
data,
|
|
2964
|
+
valuesColumns,
|
|
2965
|
+
joinOperation,
|
|
2966
|
+
keysColumn,
|
|
2967
|
+
operation
|
|
2968
|
+
} = _ref;
|
|
2969
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
2970
|
+
return null;
|
|
2971
|
+
}
|
|
2972
|
+
const groups = data.reduce((accumulator, item) => {
|
|
2973
|
+
const group = item[keysColumn];
|
|
2974
|
+
const values = accumulator.get(group) || [];
|
|
2975
|
+
accumulator.set(group, values);
|
|
2976
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
2977
|
+
const isValid = (operation === 'count' ? true : aggregatedValue !== null) && aggregatedValue !== undefined;
|
|
2978
|
+
if (isValid) {
|
|
2979
|
+
values.push(aggregatedValue);
|
|
2980
|
+
accumulator.set(group, values);
|
|
2981
|
+
}
|
|
2982
|
+
return accumulator;
|
|
2983
|
+
}, new Map()); // We use a map to be able to maintain the type in the key value
|
|
2984
|
+
const targetOperation = aggregationFunctions[operation];
|
|
2985
|
+
if (targetOperation) {
|
|
2986
|
+
return Array.from(groups).map(_ref2 => {
|
|
2987
|
+
let [name, value] = _ref2;
|
|
2988
|
+
return {
|
|
2989
|
+
name,
|
|
2990
|
+
value: targetOperation(value)
|
|
2991
|
+
};
|
|
2992
|
+
});
|
|
2993
|
+
}
|
|
2994
|
+
return [];
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
/**
|
|
2998
|
+
* Returns midnight (local time) on the Monday preceeding a given date, in
|
|
2999
|
+
* milliseconds since the UNIX epoch.
|
|
3000
|
+
*/
|
|
3001
|
+
/**
|
|
3002
|
+
* Returns midnight (UTC) on the Monday preceeding a given date, in
|
|
3003
|
+
* milliseconds since the UNIX epoch.
|
|
3004
|
+
*/
|
|
3005
|
+
function getUTCMonday(date) {
|
|
3006
|
+
const dateCp = new Date(date);
|
|
3007
|
+
const day = dateCp.getUTCDay();
|
|
3008
|
+
const diff = dateCp.getUTCDate() - day + (day ? 1 : -6); // adjust when day is sunday
|
|
3009
|
+
dateCp.setUTCDate(diff);
|
|
3010
|
+
return Date.UTC(dateCp.getUTCFullYear(), dateCp.getUTCMonth(), dateCp.getUTCDate());
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
const GROUP_KEY_FN_MAPPING = {
|
|
3014
|
+
year: date => Date.UTC(date.getUTCFullYear()),
|
|
3015
|
+
month: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth()),
|
|
3016
|
+
week: date => getUTCMonday(date),
|
|
3017
|
+
day: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
|
|
3018
|
+
hour: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours()),
|
|
3019
|
+
minute: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes()),
|
|
3020
|
+
second: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds())
|
|
3021
|
+
};
|
|
3022
|
+
/** @internalRemarks Source: @carto/react-core */
|
|
3023
|
+
function groupValuesByDateColumn(_ref) {
|
|
3024
|
+
let {
|
|
3025
|
+
data,
|
|
3026
|
+
valuesColumns,
|
|
3027
|
+
joinOperation,
|
|
3028
|
+
keysColumn,
|
|
3029
|
+
groupType,
|
|
3030
|
+
operation
|
|
3031
|
+
} = _ref;
|
|
3032
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
const groupKeyFn = GROUP_KEY_FN_MAPPING[groupType];
|
|
3036
|
+
if (!groupKeyFn) {
|
|
3037
|
+
return null;
|
|
3038
|
+
}
|
|
3039
|
+
const groups = data.reduce((acc, item) => {
|
|
3040
|
+
const value = item[keysColumn];
|
|
3041
|
+
const formattedValue = new Date(value);
|
|
3042
|
+
const groupKey = groupKeyFn(formattedValue);
|
|
3043
|
+
if (!isNaN(groupKey)) {
|
|
3044
|
+
let groupedValues = acc.get(groupKey);
|
|
3045
|
+
if (!groupedValues) {
|
|
3046
|
+
groupedValues = [];
|
|
3047
|
+
acc.set(groupKey, groupedValues);
|
|
3048
|
+
}
|
|
3049
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
3050
|
+
const isValid = aggregatedValue !== null && aggregatedValue !== undefined;
|
|
3051
|
+
if (isValid) {
|
|
3052
|
+
groupedValues.push(aggregatedValue);
|
|
3053
|
+
acc.set(groupKey, groupedValues);
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
return acc;
|
|
3057
|
+
}, new Map());
|
|
3058
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3059
|
+
return [...groups.entries()].map(_ref2 => {
|
|
3060
|
+
let [name, value] = _ref2;
|
|
3061
|
+
return {
|
|
3062
|
+
name,
|
|
3063
|
+
value: targetOperation(value)
|
|
3064
|
+
};
|
|
3065
|
+
}).sort((a, b) => a.name - b.name);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
/**
|
|
3069
|
+
* Histogram computation.
|
|
3070
|
+
* @internalRemarks Source: @carto/react-core
|
|
3071
|
+
*/
|
|
3072
|
+
function histogram(_ref) {
|
|
3073
|
+
let {
|
|
3074
|
+
data,
|
|
3075
|
+
valuesColumns,
|
|
3076
|
+
joinOperation,
|
|
3077
|
+
ticks,
|
|
3078
|
+
operation
|
|
3079
|
+
} = _ref;
|
|
3080
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
3081
|
+
return [];
|
|
3082
|
+
}
|
|
3083
|
+
const binsContainer = [Number.MIN_SAFE_INTEGER, ...ticks].map((tick, index, arr) => ({
|
|
3084
|
+
bin: index,
|
|
3085
|
+
start: tick,
|
|
3086
|
+
end: index === arr.length - 1 ? Number.MAX_SAFE_INTEGER : arr[index + 1],
|
|
3087
|
+
values: []
|
|
3088
|
+
}));
|
|
3089
|
+
data.forEach(feature => {
|
|
3090
|
+
const featureValue = aggregate(feature, valuesColumns, joinOperation);
|
|
3091
|
+
const isValid = featureValue !== null && featureValue !== undefined;
|
|
3092
|
+
if (!isValid) {
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
const binContainer = binsContainer.find(bin => bin.start <= featureValue && bin.end > featureValue);
|
|
3096
|
+
if (!binContainer) {
|
|
3097
|
+
return;
|
|
3098
|
+
}
|
|
3099
|
+
binContainer.values.push(featureValue);
|
|
3100
|
+
});
|
|
3101
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3102
|
+
const transformedBins = binsContainer.map(binContainer => binContainer.values);
|
|
3103
|
+
return transformedBins.map(values => values.length ? targetOperation(values) : 0);
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
/**
|
|
3107
|
+
* Filters invalid features and formats data.
|
|
3108
|
+
* @internalRemarks Source: @carto/react-core
|
|
3109
|
+
*/
|
|
3110
|
+
function scatterPlot(_ref) {
|
|
3111
|
+
let {
|
|
3112
|
+
data,
|
|
3113
|
+
xAxisColumns,
|
|
3114
|
+
xAxisJoinOperation,
|
|
3115
|
+
yAxisColumns,
|
|
3116
|
+
yAxisJoinOperation
|
|
3117
|
+
} = _ref;
|
|
3118
|
+
return data.reduce((acc, feature) => {
|
|
3119
|
+
const xValue = aggregate(feature, xAxisColumns, xAxisJoinOperation);
|
|
3120
|
+
const xIsValid = xValue !== null && xValue !== undefined;
|
|
3121
|
+
const yValue = aggregate(feature, yAxisColumns, yAxisJoinOperation);
|
|
3122
|
+
const yIsValid = yValue !== null && yValue !== undefined;
|
|
3123
|
+
if (xIsValid && yIsValid) {
|
|
3124
|
+
acc.push([xValue, yValue]);
|
|
3125
|
+
}
|
|
3126
|
+
return acc;
|
|
3127
|
+
}, []);
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
/**
|
|
3131
|
+
* Source for Widget API requests on a data source defined by a tileset.
|
|
3132
|
+
*
|
|
3133
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
3134
|
+
* {@link vectorTilesetSource}, {@link h3TilesetSource}, or {@link quadbinTilesetSource},
|
|
3135
|
+
* which can be shared with map layers. Sources contain a `widgetSource`
|
|
3136
|
+
* property, for use by widget implementations.
|
|
3137
|
+
*
|
|
3138
|
+
* Example:
|
|
3139
|
+
*
|
|
3140
|
+
* ```javascript
|
|
3141
|
+
* import { vectorTilesetSource } from '@carto/api-client';
|
|
3142
|
+
*
|
|
3143
|
+
* const data = vectorTilesetSource({
|
|
3144
|
+
* accessToken: '••••',
|
|
3145
|
+
* connectionName: 'carto_dw',
|
|
3146
|
+
* tableName: 'carto-demo-data.demo_rasters.my_tileset_source'
|
|
3147
|
+
* });
|
|
3148
|
+
*
|
|
3149
|
+
* const { widgetSource } = await data;
|
|
3150
|
+
* ```
|
|
3151
|
+
*/
|
|
3152
|
+
class WidgetTilesetSource extends WidgetSource {
|
|
3153
|
+
constructor() {
|
|
3154
|
+
super(...arguments);
|
|
3155
|
+
this._features = [];
|
|
3156
|
+
}
|
|
3157
|
+
getModelSource(owner) {
|
|
3158
|
+
return {
|
|
3159
|
+
...super._getModelSource(owner),
|
|
3160
|
+
type: 'tileset',
|
|
3161
|
+
data: this.props.tableName
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
/** Loads features as a list of tiles (typically provided by deck.gl). */
|
|
3165
|
+
loadTiles(_ref) {
|
|
3166
|
+
let {
|
|
3167
|
+
tiles,
|
|
3168
|
+
spatialFilter,
|
|
3169
|
+
uniqueIdProperty,
|
|
3170
|
+
options
|
|
3171
|
+
} = _ref;
|
|
3172
|
+
this._features = tileFeatures({
|
|
3173
|
+
tiles,
|
|
3174
|
+
options,
|
|
3175
|
+
spatialFilter,
|
|
3176
|
+
uniqueIdProperty,
|
|
3177
|
+
tileFormat: this.props.tileFormat,
|
|
3178
|
+
spatialDataColumn: this.props.spatialDataColumn,
|
|
3179
|
+
spatialDataType: this.props.spatialDataType
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
/** Loads features as GeoJSON (used for testing). */
|
|
3183
|
+
loadGeoJSON(_ref2) {
|
|
3184
|
+
let {
|
|
3185
|
+
geojson,
|
|
3186
|
+
spatialFilter,
|
|
3187
|
+
uniqueIdProperty
|
|
3188
|
+
} = _ref2;
|
|
3189
|
+
this._features = geojsonFeatures({
|
|
3190
|
+
geojson,
|
|
3191
|
+
spatialFilter,
|
|
3192
|
+
uniqueIdProperty
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
getFeatures(options) {
|
|
3196
|
+
try {
|
|
3197
|
+
throw new Error('getFeatures not supported for tilesets');
|
|
3198
|
+
return Promise.resolve();
|
|
3199
|
+
} catch (e) {
|
|
3200
|
+
return Promise.reject(e);
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
getFormula(_ref3) {
|
|
3204
|
+
let {
|
|
3205
|
+
column = '*',
|
|
3206
|
+
operation = 'count',
|
|
3207
|
+
joinOperation,
|
|
3208
|
+
filterOwner
|
|
3209
|
+
} = _ref3;
|
|
3210
|
+
try {
|
|
3211
|
+
const _this = this;
|
|
3212
|
+
if (operation === 'custom') {
|
|
3213
|
+
throw new Error('Custom aggregation not supported for tilesets');
|
|
3214
|
+
}
|
|
3215
|
+
if (!_this._features.length) {
|
|
3216
|
+
return Promise.resolve({
|
|
3217
|
+
value: null
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
// Column is required except when operation is 'count'.
|
|
3221
|
+
if (column && column !== '*' || operation !== 'count') {
|
|
3222
|
+
assertColumn(_this._features, column);
|
|
3223
|
+
}
|
|
3224
|
+
const filteredFeatures = _this._getFilteredFeatures(filterOwner);
|
|
3225
|
+
if (filteredFeatures.length === 0 && operation !== 'count') {
|
|
3226
|
+
return Promise.resolve({
|
|
3227
|
+
value: null
|
|
3228
|
+
});
|
|
3229
|
+
}
|
|
3230
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3231
|
+
return Promise.resolve({
|
|
3232
|
+
value: targetOperation(filteredFeatures, column, joinOperation)
|
|
3233
|
+
});
|
|
3234
|
+
} catch (e) {
|
|
3235
|
+
return Promise.reject(e);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
getHistogram(_ref4) {
|
|
3239
|
+
let {
|
|
3240
|
+
operation = 'count',
|
|
3241
|
+
ticks,
|
|
3242
|
+
column,
|
|
3243
|
+
joinOperation,
|
|
3244
|
+
filterOwner
|
|
3245
|
+
} = _ref4;
|
|
3246
|
+
try {
|
|
3247
|
+
const _this2 = this;
|
|
3248
|
+
if (!_this2._features.length) {
|
|
3249
|
+
return Promise.resolve([]);
|
|
3250
|
+
}
|
|
3251
|
+
const filteredFeatures = _this2._getFilteredFeatures(filterOwner);
|
|
3252
|
+
assertColumn(_this2._features, column);
|
|
3253
|
+
return Promise.resolve(histogram({
|
|
3254
|
+
data: filteredFeatures,
|
|
3255
|
+
valuesColumns: normalizeColumns(column),
|
|
3256
|
+
joinOperation,
|
|
3257
|
+
ticks,
|
|
3258
|
+
operation
|
|
3259
|
+
}));
|
|
3260
|
+
} catch (e) {
|
|
3261
|
+
return Promise.reject(e);
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
getCategories(_ref5) {
|
|
3265
|
+
let {
|
|
3266
|
+
column,
|
|
3267
|
+
operation = 'count',
|
|
3268
|
+
operationColumn,
|
|
3269
|
+
joinOperation,
|
|
3270
|
+
filterOwner
|
|
3271
|
+
} = _ref5;
|
|
3272
|
+
try {
|
|
3273
|
+
const _this3 = this;
|
|
3274
|
+
if (!_this3._features.length) {
|
|
3275
|
+
return Promise.resolve([]);
|
|
3276
|
+
}
|
|
3277
|
+
const filteredFeatures = _this3._getFilteredFeatures(filterOwner);
|
|
3278
|
+
assertColumn(_this3._features, column, operationColumn);
|
|
3279
|
+
const groups = groupValuesByColumn({
|
|
3280
|
+
data: filteredFeatures,
|
|
3281
|
+
valuesColumns: normalizeColumns(operationColumn || column),
|
|
3282
|
+
joinOperation,
|
|
3283
|
+
keysColumn: column,
|
|
3284
|
+
operation
|
|
3285
|
+
});
|
|
3286
|
+
return Promise.resolve(groups || []);
|
|
3287
|
+
} catch (e) {
|
|
3288
|
+
return Promise.reject(e);
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
getScatter(_ref6) {
|
|
3292
|
+
let {
|
|
3293
|
+
xAxisColumn,
|
|
3294
|
+
yAxisColumn,
|
|
3295
|
+
xAxisJoinOperation,
|
|
3296
|
+
yAxisJoinOperation,
|
|
3297
|
+
filterOwner
|
|
3298
|
+
} = _ref6;
|
|
3299
|
+
try {
|
|
3300
|
+
const _this4 = this;
|
|
3301
|
+
if (!_this4._features.length) {
|
|
3302
|
+
return Promise.resolve([]);
|
|
3303
|
+
}
|
|
3304
|
+
const filteredFeatures = _this4._getFilteredFeatures(filterOwner);
|
|
3305
|
+
assertColumn(_this4._features, xAxisColumn, yAxisColumn);
|
|
3306
|
+
return Promise.resolve(scatterPlot({
|
|
3307
|
+
data: filteredFeatures,
|
|
3308
|
+
xAxisColumns: normalizeColumns(xAxisColumn),
|
|
3309
|
+
xAxisJoinOperation,
|
|
3310
|
+
yAxisColumns: normalizeColumns(yAxisColumn),
|
|
3311
|
+
yAxisJoinOperation
|
|
3312
|
+
}));
|
|
3313
|
+
} catch (e) {
|
|
3314
|
+
return Promise.reject(e);
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
getTable(options) {
|
|
3318
|
+
try {
|
|
3319
|
+
const _this5 = this;
|
|
3320
|
+
const {
|
|
3321
|
+
filterOwner,
|
|
3322
|
+
spatialFilter,
|
|
3323
|
+
abortController,
|
|
3324
|
+
...params
|
|
3325
|
+
} = options;
|
|
3326
|
+
const {
|
|
3327
|
+
columns,
|
|
3328
|
+
searchFilterColumn,
|
|
3329
|
+
searchFilterText,
|
|
3330
|
+
sortBy,
|
|
3331
|
+
sortDirection,
|
|
3332
|
+
sortByColumnType,
|
|
3333
|
+
offset = 0,
|
|
3334
|
+
limit = 10
|
|
3335
|
+
} = params;
|
|
3336
|
+
if (!_this5._features.length) {
|
|
3337
|
+
return Promise.resolve({
|
|
3338
|
+
rows: [],
|
|
3339
|
+
totalCount: 0
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
// Filter.
|
|
3343
|
+
let filteredFeatures = _this5._getFilteredFeatures(filterOwner);
|
|
3344
|
+
// Search.
|
|
3345
|
+
// TODO: Could we get the same behavior by applying filters in loadTiles()?
|
|
3346
|
+
if (searchFilterColumn && searchFilterText) {
|
|
3347
|
+
filteredFeatures = filteredFeatures.filter(row => row[searchFilterColumn] && String(row[searchFilterColumn]).toLowerCase().includes(String(searchFilterText).toLowerCase()));
|
|
3348
|
+
}
|
|
3349
|
+
// Sort.
|
|
3350
|
+
let rows = applySorting(filteredFeatures, {
|
|
3351
|
+
sortBy,
|
|
3352
|
+
sortByDirection: sortDirection,
|
|
3353
|
+
sortByColumnType
|
|
3354
|
+
});
|
|
3355
|
+
const totalCount = rows.length;
|
|
3356
|
+
// Offset and limit.
|
|
3357
|
+
rows = rows.slice(Math.min(offset, totalCount), Math.min(offset + limit, totalCount));
|
|
3358
|
+
// Select columns.
|
|
3359
|
+
rows = rows.map(srcRow => {
|
|
3360
|
+
const dstRow = {};
|
|
3361
|
+
for (const column of columns) {
|
|
3362
|
+
dstRow[column] = srcRow[column];
|
|
3363
|
+
}
|
|
3364
|
+
return dstRow;
|
|
3365
|
+
});
|
|
3366
|
+
return Promise.resolve({
|
|
3367
|
+
rows,
|
|
3368
|
+
totalCount
|
|
3369
|
+
});
|
|
3370
|
+
} catch (e) {
|
|
3371
|
+
return Promise.reject(e);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
getTimeSeries(_ref7) {
|
|
3375
|
+
let {
|
|
3376
|
+
column,
|
|
3377
|
+
stepSize,
|
|
3378
|
+
operation,
|
|
3379
|
+
operationColumn,
|
|
3380
|
+
joinOperation,
|
|
3381
|
+
filterOwner
|
|
3382
|
+
} = _ref7;
|
|
3383
|
+
try {
|
|
3384
|
+
const _this6 = this;
|
|
3385
|
+
if (!_this6._features.length) {
|
|
3386
|
+
return Promise.resolve({
|
|
3387
|
+
rows: []
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
const filteredFeatures = _this6._getFilteredFeatures(filterOwner);
|
|
3391
|
+
assertColumn(_this6._features, column, operationColumn);
|
|
3392
|
+
const rows = groupValuesByDateColumn({
|
|
3393
|
+
data: filteredFeatures,
|
|
3394
|
+
valuesColumns: normalizeColumns(operationColumn || column),
|
|
3395
|
+
keysColumn: column,
|
|
3396
|
+
groupType: stepSize,
|
|
3397
|
+
operation,
|
|
3398
|
+
joinOperation
|
|
3399
|
+
}) || [];
|
|
3400
|
+
return Promise.resolve({
|
|
3401
|
+
rows
|
|
3402
|
+
});
|
|
3403
|
+
} catch (e) {
|
|
3404
|
+
return Promise.reject(e);
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
getRange(_ref8) {
|
|
3408
|
+
let {
|
|
3409
|
+
column,
|
|
3410
|
+
filterOwner
|
|
3411
|
+
} = _ref8;
|
|
3412
|
+
try {
|
|
3413
|
+
const _this7 = this;
|
|
3414
|
+
if (!_this7._features.length) {
|
|
3415
|
+
// TODO: Is this the only nullable response in the Widgets API? If so,
|
|
3416
|
+
// can we do something more consistent?
|
|
3417
|
+
return Promise.resolve(null);
|
|
3418
|
+
}
|
|
3419
|
+
assertColumn(_this7._features, column);
|
|
3420
|
+
const filteredFeatures = _this7._getFilteredFeatures(filterOwner);
|
|
3421
|
+
return Promise.resolve({
|
|
3422
|
+
min: aggregationFunctions.min(filteredFeatures, column),
|
|
3423
|
+
max: aggregationFunctions.max(filteredFeatures, column)
|
|
3424
|
+
});
|
|
3425
|
+
} catch (e) {
|
|
3426
|
+
return Promise.reject(e);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
/****************************************************************************
|
|
3430
|
+
* INTERNAL
|
|
3431
|
+
*/
|
|
3432
|
+
_getFilteredFeatures(filterOwner) {
|
|
3433
|
+
return applyFilters(this._features, getApplicableFilters(filterOwner, this.props.filters), this.props.filtersLogicalOperator || 'and');
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
function assertColumn(features) {
|
|
3437
|
+
// TODO(cleanup): Can drop support for multiple column shapes here?
|
|
3438
|
+
// Due to the multiple column shape, we normalise it as an array with normalizeColumns
|
|
3439
|
+
const columns = Array.from(new Set([].slice.call(arguments, 1).map(normalizeColumns).flat()));
|
|
3440
|
+
const featureKeys = Object.keys(features[0]);
|
|
3441
|
+
const invalidColumns = columns.filter(column => !featureKeys.includes(column));
|
|
3442
|
+
if (invalidColumns.length) {
|
|
3443
|
+
throw new InvalidColumnError(`Missing column(s): ${invalidColumns.join(', ')}`);
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
function normalizeColumns(columns) {
|
|
3447
|
+
return Array.isArray(columns) ? columns : typeof columns === 'string' ? [columns] : [];
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
// deck.gl
|
|
3451
|
+
// SPDX-License-Identifier: MIT
|
|
3452
|
+
// Copyright (c) vis.gl contributors
|
|
3453
|
+
/* eslint-disable camelcase */
|
|
3454
|
+
const h3QuerySource = function (options) {
|
|
3455
|
+
try {
|
|
3456
|
+
const {
|
|
3457
|
+
aggregationExp,
|
|
3458
|
+
aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
|
|
3459
|
+
sqlQuery,
|
|
3460
|
+
spatialDataColumn = 'h3',
|
|
3461
|
+
queryParameters,
|
|
3462
|
+
filters
|
|
3463
|
+
} = options;
|
|
3464
|
+
const urlParameters = {
|
|
3465
|
+
aggregationExp,
|
|
3466
|
+
spatialDataColumn,
|
|
3467
|
+
spatialDataType: 'h3',
|
|
3468
|
+
q: sqlQuery
|
|
3469
|
+
};
|
|
3470
|
+
if (aggregationResLevel) {
|
|
3471
|
+
urlParameters.aggregationResLevel = String(aggregationResLevel);
|
|
3472
|
+
}
|
|
3473
|
+
if (queryParameters) {
|
|
3474
|
+
urlParameters.queryParameters = queryParameters;
|
|
3475
|
+
}
|
|
3476
|
+
if (filters) {
|
|
3477
|
+
urlParameters.filters = filters;
|
|
3478
|
+
}
|
|
3479
|
+
return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
|
|
3480
|
+
...result,
|
|
3481
|
+
widgetSource: new WidgetQuerySource({
|
|
3482
|
+
...options,
|
|
3483
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
|
|
3484
|
+
spatialDataColumn,
|
|
3485
|
+
spatialDataType: 'h3'
|
|
3486
|
+
})
|
|
3487
|
+
})));
|
|
3488
|
+
} catch (e) {
|
|
3489
|
+
return Promise.reject(e);
|
|
3490
|
+
}
|
|
3491
|
+
};
|
|
3492
|
+
|
|
3493
|
+
// deck.gl
|
|
3494
|
+
// SPDX-License-Identifier: MIT
|
|
3495
|
+
// Copyright (c) vis.gl contributors
|
|
3496
|
+
/* eslint-disable camelcase */
|
|
3497
|
+
const h3TableSource = function (options) {
|
|
3498
|
+
try {
|
|
3499
|
+
const {
|
|
3500
|
+
aggregationExp,
|
|
3501
|
+
aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
|
|
3502
|
+
spatialDataColumn = 'h3',
|
|
3503
|
+
tableName,
|
|
3504
|
+
filters
|
|
3505
|
+
} = options;
|
|
3506
|
+
const urlParameters = {
|
|
3507
|
+
aggregationExp,
|
|
3508
|
+
name: tableName,
|
|
3509
|
+
spatialDataColumn,
|
|
3510
|
+
spatialDataType: 'h3'
|
|
3511
|
+
};
|
|
3512
|
+
if (aggregationResLevel) {
|
|
3513
|
+
urlParameters.aggregationResLevel = String(aggregationResLevel);
|
|
3514
|
+
}
|
|
3515
|
+
if (filters) {
|
|
3516
|
+
urlParameters.filters = filters;
|
|
3517
|
+
}
|
|
3518
|
+
return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
|
|
3519
|
+
...result,
|
|
3520
|
+
widgetSource: new WidgetTableSource({
|
|
3521
|
+
...options,
|
|
3522
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
|
|
3523
|
+
spatialDataColumn,
|
|
3524
|
+
spatialDataType: 'h3'
|
|
3525
|
+
})
|
|
3526
|
+
})));
|
|
3527
|
+
} catch (e) {
|
|
3528
|
+
return Promise.reject(e);
|
|
3529
|
+
}
|
|
3530
|
+
};
|
|
3531
|
+
|
|
3532
|
+
function getTileFormat(tilejson) {
|
|
3533
|
+
const tileParams = new URL(tilejson.tiles[0]).searchParams;
|
|
3534
|
+
return tileParams.get('formatTiles') === 'mvt' ? exports.TileFormat.MVT : exports.TileFormat.BINARY;
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
// deck.gl
|
|
3538
|
+
// SPDX-License-Identifier: MIT
|
|
3539
|
+
// Copyright (c) vis.gl contributors
|
|
3540
|
+
const h3TilesetSource = function (options) {
|
|
3541
|
+
try {
|
|
3542
|
+
const {
|
|
3543
|
+
tableName,
|
|
3544
|
+
spatialDataColumn = 'h3'
|
|
3545
|
+
} = options;
|
|
3546
|
+
const urlParameters = {
|
|
3547
|
+
name: tableName
|
|
3548
|
+
};
|
|
3549
|
+
return Promise.resolve(baseSource('tileset', options, urlParameters).then(result => ({
|
|
3550
|
+
...result,
|
|
3551
|
+
widgetSource: new WidgetTilesetSource({
|
|
3552
|
+
...options,
|
|
3553
|
+
tileFormat: getTileFormat(result),
|
|
3554
|
+
spatialDataColumn,
|
|
3555
|
+
spatialDataType: 'h3'
|
|
3556
|
+
})
|
|
3557
|
+
})));
|
|
3558
|
+
} catch (e) {
|
|
3559
|
+
return Promise.reject(e);
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
|
|
3563
|
+
// deck.gl
|
|
3564
|
+
// SPDX-License-Identifier: MIT
|
|
3565
|
+
// Copyright (c) vis.gl contributors
|
|
3566
|
+
const rasterSource = function (options) {
|
|
3567
|
+
try {
|
|
3568
|
+
const {
|
|
3569
|
+
tableName,
|
|
3570
|
+
filters
|
|
3571
|
+
} = options;
|
|
3572
|
+
const urlParameters = {
|
|
3573
|
+
name: tableName
|
|
3574
|
+
};
|
|
3575
|
+
if (filters) {
|
|
3576
|
+
urlParameters.filters = filters;
|
|
3577
|
+
}
|
|
3578
|
+
return Promise.resolve(baseSource('raster', options, urlParameters));
|
|
3579
|
+
} catch (e) {
|
|
3580
|
+
return Promise.reject(e);
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
|
|
3584
|
+
// deck.gl
|
|
3585
|
+
// SPDX-License-Identifier: MIT
|
|
3586
|
+
// Copyright (c) vis.gl contributors
|
|
3587
|
+
/* eslint-disable camelcase */
|
|
3588
|
+
const quadbinQuerySource = function (options) {
|
|
3589
|
+
try {
|
|
3590
|
+
const {
|
|
3591
|
+
aggregationExp,
|
|
3592
|
+
aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
|
|
3593
|
+
sqlQuery,
|
|
3594
|
+
spatialDataColumn = 'quadbin',
|
|
3595
|
+
queryParameters,
|
|
3596
|
+
filters
|
|
3597
|
+
} = options;
|
|
3598
|
+
const urlParameters = {
|
|
3599
|
+
aggregationExp,
|
|
3600
|
+
q: sqlQuery,
|
|
3601
|
+
spatialDataColumn,
|
|
3602
|
+
spatialDataType: 'quadbin'
|
|
3603
|
+
};
|
|
3604
|
+
if (aggregationResLevel) {
|
|
3605
|
+
urlParameters.aggregationResLevel = String(aggregationResLevel);
|
|
3606
|
+
}
|
|
3607
|
+
if (queryParameters) {
|
|
3608
|
+
urlParameters.queryParameters = queryParameters;
|
|
3609
|
+
}
|
|
3610
|
+
if (filters) {
|
|
3611
|
+
urlParameters.filters = filters;
|
|
3612
|
+
}
|
|
3613
|
+
return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
|
|
3614
|
+
...result,
|
|
3615
|
+
widgetSource: new WidgetQuerySource({
|
|
3616
|
+
...options,
|
|
3617
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
|
|
3618
|
+
spatialDataColumn,
|
|
3619
|
+
spatialDataType: 'quadbin'
|
|
3620
|
+
})
|
|
3621
|
+
})));
|
|
3622
|
+
} catch (e) {
|
|
3623
|
+
return Promise.reject(e);
|
|
3624
|
+
}
|
|
3625
|
+
};
|
|
3626
|
+
|
|
3627
|
+
// deck.gl
|
|
3628
|
+
// SPDX-License-Identifier: MIT
|
|
3629
|
+
// Copyright (c) vis.gl contributors
|
|
3630
|
+
/* eslint-disable camelcase */
|
|
3631
|
+
const quadbinTableSource = function (options) {
|
|
3632
|
+
try {
|
|
3633
|
+
const {
|
|
3634
|
+
aggregationExp,
|
|
3635
|
+
aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
|
|
3636
|
+
spatialDataColumn = 'quadbin',
|
|
3637
|
+
tableName,
|
|
3638
|
+
filters
|
|
1574
3639
|
} = options;
|
|
1575
3640
|
const urlParameters = {
|
|
1576
3641
|
aggregationExp,
|
|
@@ -1586,7 +3651,12 @@ const quadbinTableSource = function (options) {
|
|
|
1586
3651
|
}
|
|
1587
3652
|
return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
|
|
1588
3653
|
...result,
|
|
1589
|
-
widgetSource: new WidgetTableSource(
|
|
3654
|
+
widgetSource: new WidgetTableSource({
|
|
3655
|
+
...options,
|
|
3656
|
+
// NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
|
|
3657
|
+
spatialDataColumn,
|
|
3658
|
+
spatialDataType: 'quadbin'
|
|
3659
|
+
})
|
|
1590
3660
|
})));
|
|
1591
3661
|
} catch (e) {
|
|
1592
3662
|
return Promise.reject(e);
|
|
@@ -1594,27 +3664,41 @@ const quadbinTableSource = function (options) {
|
|
|
1594
3664
|
};
|
|
1595
3665
|
|
|
1596
3666
|
// deck.gl
|
|
3667
|
+
// SPDX-License-Identifier: MIT
|
|
3668
|
+
// Copyright (c) vis.gl contributors
|
|
1597
3669
|
const quadbinTilesetSource = function (options) {
|
|
1598
3670
|
try {
|
|
1599
3671
|
const {
|
|
1600
|
-
tableName
|
|
3672
|
+
tableName,
|
|
3673
|
+
spatialDataColumn = 'quadbin'
|
|
1601
3674
|
} = options;
|
|
1602
3675
|
const urlParameters = {
|
|
1603
3676
|
name: tableName
|
|
1604
3677
|
};
|
|
1605
|
-
return Promise.resolve(baseSource('tileset', options, urlParameters)
|
|
3678
|
+
return Promise.resolve(baseSource('tileset', options, urlParameters).then(result => ({
|
|
3679
|
+
...result,
|
|
3680
|
+
widgetSource: new WidgetTilesetSource({
|
|
3681
|
+
...options,
|
|
3682
|
+
tileFormat: getTileFormat(result),
|
|
3683
|
+
spatialDataColumn,
|
|
3684
|
+
spatialDataType: 'quadbin'
|
|
3685
|
+
})
|
|
3686
|
+
})));
|
|
1606
3687
|
} catch (e) {
|
|
1607
3688
|
return Promise.reject(e);
|
|
1608
3689
|
}
|
|
1609
3690
|
};
|
|
1610
3691
|
|
|
1611
3692
|
// deck.gl
|
|
3693
|
+
// SPDX-License-Identifier: MIT
|
|
3694
|
+
// Copyright (c) vis.gl contributors
|
|
3695
|
+
/* eslint-disable camelcase */
|
|
1612
3696
|
const vectorQuerySource = function (options) {
|
|
1613
3697
|
try {
|
|
1614
3698
|
const {
|
|
1615
3699
|
columns,
|
|
1616
3700
|
filters,
|
|
1617
|
-
spatialDataColumn =
|
|
3701
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1618
3702
|
sqlQuery,
|
|
1619
3703
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1620
3704
|
queryParameters,
|
|
@@ -1640,7 +3724,11 @@ const vectorQuerySource = function (options) {
|
|
|
1640
3724
|
}
|
|
1641
3725
|
return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
|
|
1642
3726
|
...result,
|
|
1643
|
-
widgetSource: new WidgetQuerySource(
|
|
3727
|
+
widgetSource: new WidgetQuerySource({
|
|
3728
|
+
...options,
|
|
3729
|
+
spatialDataColumn,
|
|
3730
|
+
spatialDataType: 'geo'
|
|
3731
|
+
})
|
|
1644
3732
|
})));
|
|
1645
3733
|
} catch (e) {
|
|
1646
3734
|
return Promise.reject(e);
|
|
@@ -1648,12 +3736,15 @@ const vectorQuerySource = function (options) {
|
|
|
1648
3736
|
};
|
|
1649
3737
|
|
|
1650
3738
|
// deck.gl
|
|
3739
|
+
// SPDX-License-Identifier: MIT
|
|
3740
|
+
// Copyright (c) vis.gl contributors
|
|
3741
|
+
/* eslint-disable camelcase */
|
|
1651
3742
|
const vectorTableSource = function (options) {
|
|
1652
3743
|
try {
|
|
1653
3744
|
const {
|
|
1654
3745
|
columns,
|
|
1655
3746
|
filters,
|
|
1656
|
-
spatialDataColumn =
|
|
3747
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1657
3748
|
tableName,
|
|
1658
3749
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1659
3750
|
aggregationExp
|
|
@@ -1675,7 +3766,11 @@ const vectorTableSource = function (options) {
|
|
|
1675
3766
|
}
|
|
1676
3767
|
return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
|
|
1677
3768
|
...result,
|
|
1678
|
-
widgetSource: new WidgetTableSource(
|
|
3769
|
+
widgetSource: new WidgetTableSource({
|
|
3770
|
+
...options,
|
|
3771
|
+
spatialDataColumn,
|
|
3772
|
+
spatialDataType: 'geo'
|
|
3773
|
+
})
|
|
1679
3774
|
})));
|
|
1680
3775
|
} catch (e) {
|
|
1681
3776
|
return Promise.reject(e);
|
|
@@ -1683,21 +3778,34 @@ const vectorTableSource = function (options) {
|
|
|
1683
3778
|
};
|
|
1684
3779
|
|
|
1685
3780
|
// deck.gl
|
|
3781
|
+
// SPDX-License-Identifier: MIT
|
|
3782
|
+
// Copyright (c) vis.gl contributors
|
|
1686
3783
|
const vectorTilesetSource = function (options) {
|
|
1687
3784
|
try {
|
|
1688
3785
|
const {
|
|
1689
|
-
tableName
|
|
3786
|
+
tableName,
|
|
3787
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN
|
|
1690
3788
|
} = options;
|
|
1691
3789
|
const urlParameters = {
|
|
1692
3790
|
name: tableName
|
|
1693
3791
|
};
|
|
1694
|
-
return Promise.resolve(baseSource('tileset', options, urlParameters)
|
|
3792
|
+
return Promise.resolve(baseSource('tileset', options, urlParameters).then(result => ({
|
|
3793
|
+
...result,
|
|
3794
|
+
widgetSource: new WidgetTilesetSource({
|
|
3795
|
+
...options,
|
|
3796
|
+
tileFormat: getTileFormat(result),
|
|
3797
|
+
spatialDataColumn,
|
|
3798
|
+
spatialDataType: 'geo'
|
|
3799
|
+
})
|
|
3800
|
+
})));
|
|
1695
3801
|
} catch (e) {
|
|
1696
3802
|
return Promise.reject(e);
|
|
1697
3803
|
}
|
|
1698
3804
|
};
|
|
1699
3805
|
|
|
1700
3806
|
// deck.gl
|
|
3807
|
+
// SPDX-License-Identifier: MIT
|
|
3808
|
+
// Copyright (c) vis.gl contributors
|
|
1701
3809
|
const query = function (options) {
|
|
1702
3810
|
try {
|
|
1703
3811
|
const {
|
|
@@ -1748,24 +3856,38 @@ const query = function (options) {
|
|
|
1748
3856
|
|
|
1749
3857
|
exports.CartoAPIError = CartoAPIError;
|
|
1750
3858
|
exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
|
|
3859
|
+
exports.FEATURE_GEOM_PROPERTY = FEATURE_GEOM_PROPERTY;
|
|
1751
3860
|
exports.SOURCE_DEFAULTS = SOURCE_DEFAULTS;
|
|
1752
|
-
exports.WidgetBaseSource = WidgetBaseSource;
|
|
1753
3861
|
exports.WidgetQuerySource = WidgetQuerySource;
|
|
3862
|
+
exports.WidgetRemoteSource = WidgetRemoteSource;
|
|
1754
3863
|
exports.WidgetTableSource = WidgetTableSource;
|
|
3864
|
+
exports.WidgetTilesetSource = WidgetTilesetSource;
|
|
1755
3865
|
exports.addFilter = addFilter;
|
|
3866
|
+
exports.aggregate = aggregate;
|
|
3867
|
+
exports.aggregationFunctions = aggregationFunctions;
|
|
3868
|
+
exports.applyFilters = applyFilters;
|
|
3869
|
+
exports.applySorting = applySorting;
|
|
1756
3870
|
exports.boundaryQuerySource = boundaryQuerySource;
|
|
1757
3871
|
exports.boundaryTableSource = boundaryTableSource;
|
|
3872
|
+
exports.buildBinaryFeatureFilter = buildBinaryFeatureFilter;
|
|
3873
|
+
exports.buildFeatureFilter = buildFeatureFilter;
|
|
1758
3874
|
exports.buildPublicMapUrl = buildPublicMapUrl;
|
|
1759
3875
|
exports.buildStatsUrl = buildStatsUrl;
|
|
1760
3876
|
exports.clearFilters = clearFilters;
|
|
1761
3877
|
exports.createPolygonSpatialFilter = createPolygonSpatialFilter;
|
|
1762
3878
|
exports.createViewportSpatialFilter = createViewportSpatialFilter;
|
|
3879
|
+
exports.filterFunctions = filterFunctions;
|
|
3880
|
+
exports.geojsonFeatures = geojsonFeatures;
|
|
1763
3881
|
exports.getClient = getClient;
|
|
1764
3882
|
exports.getFilter = getFilter;
|
|
3883
|
+
exports.groupValuesByColumn = groupValuesByColumn;
|
|
3884
|
+
exports.groupValuesByDateColumn = groupValuesByDateColumn;
|
|
1765
3885
|
exports.h3QuerySource = h3QuerySource;
|
|
1766
3886
|
exports.h3TableSource = h3TableSource;
|
|
1767
3887
|
exports.h3TilesetSource = h3TilesetSource;
|
|
1768
3888
|
exports.hasFilter = hasFilter;
|
|
3889
|
+
exports.histogram = histogram;
|
|
3890
|
+
exports.makeIntervalComplete = makeIntervalComplete;
|
|
1769
3891
|
exports.quadbinQuerySource = quadbinQuerySource;
|
|
1770
3892
|
exports.quadbinTableSource = quadbinTableSource;
|
|
1771
3893
|
exports.quadbinTilesetSource = quadbinTilesetSource;
|
|
@@ -1773,7 +3895,12 @@ exports.query = query;
|
|
|
1773
3895
|
exports.rasterSource = rasterSource;
|
|
1774
3896
|
exports.removeFilter = removeFilter;
|
|
1775
3897
|
exports.requestWithParameters = requestWithParameters;
|
|
3898
|
+
exports.scatterPlot = scatterPlot;
|
|
1776
3899
|
exports.setClient = setClient;
|
|
3900
|
+
exports.tileFeatures = tileFeatures;
|
|
3901
|
+
exports.tileFeaturesGeometries = tileFeaturesGeometries;
|
|
3902
|
+
exports.tileFeaturesSpatialIndex = tileFeaturesSpatialIndex;
|
|
3903
|
+
exports.transformToTileCoords = transformToTileCoords;
|
|
1777
3904
|
exports.vectorQuerySource = vectorQuerySource;
|
|
1778
3905
|
exports.vectorTableSource = vectorTableSource;
|
|
1779
3906
|
exports.vectorTilesetSource = vectorTilesetSource;
|