@carto/api-client 0.4.6 → 0.4.7-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/build/api/carto-api-error.d.ts +1 -1
- package/build/api/query.d.ts +1 -1
- package/build/api/request-with-parameters.d.ts +2 -2
- package/build/api-client.cjs +2365 -279
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +2274 -298
- package/build/api-client.modern.js.map +1 -1
- package/build/client.d.ts +2 -2
- package/build/constants-internal.d.ts +5 -5
- package/build/constants.d.ts +25 -3
- package/build/deck/get-data-filter-extension-props.d.ts +28 -0
- package/build/deck/index.d.ts +1 -0
- package/build/filters/Filter.d.ts +25 -0
- package/build/filters/FilterTypes.d.ts +3 -0
- package/build/filters/geosjonFeatures.d.ts +8 -0
- package/build/filters/index.d.ts +6 -0
- package/build/filters/tileFeatures.d.ts +20 -0
- package/build/filters/tileFeaturesGeometries.d.ts +13 -0
- package/build/filters/tileFeaturesSpatialIndex.d.ts +10 -0
- package/build/filters.d.ts +2 -2
- package/build/geo.d.ts +1 -1
- package/build/index.d.ts +5 -0
- package/build/models/common.d.ts +5 -4
- package/build/models/index.d.ts +1 -1
- package/build/models/model.d.ts +2 -2
- package/build/operations/aggregation.d.ts +8 -0
- package/build/operations/applySorting.d.ts +20 -0
- package/build/operations/groupBy.d.ts +15 -0
- package/build/operations/groupByDate.d.ts +11 -0
- package/build/operations/histogram.d.ts +13 -0
- package/build/operations/index.d.ts +6 -0
- package/build/operations/scatterPlot.d.ts +14 -0
- package/build/sources/base-source.d.ts +2 -2
- package/build/sources/boundary-query-source.d.ts +1 -1
- package/build/sources/boundary-table-source.d.ts +1 -1
- package/build/sources/h3-query-source.d.ts +2 -2
- package/build/sources/h3-table-source.d.ts +2 -2
- package/build/sources/h3-tileset-source.d.ts +1 -1
- package/build/sources/index.d.ts +26 -26
- package/build/sources/quadbin-query-source.d.ts +2 -2
- package/build/sources/quadbin-table-source.d.ts +2 -2
- package/build/sources/quadbin-tileset-source.d.ts +1 -1
- package/build/sources/raster-source.d.ts +1 -1
- package/build/sources/types.d.ts +3 -3
- package/build/sources/vector-query-source.d.ts +1 -1
- package/build/sources/vector-table-source.d.ts +1 -1
- package/build/sources/vector-tileset-source.d.ts +1 -1
- package/build/spatial-index.d.ts +3 -3
- package/build/types-internal.d.ts +9 -5
- package/build/types.d.ts +74 -14
- 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 +3 -3
- package/build/widget-sources/index.d.ts +3 -1
- package/build/widget-sources/types.d.ts +38 -25
- package/build/widget-sources/widget-query-source.d.ts +4 -3
- 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 +4 -3
- package/build/widget-sources/widget-tileset-source.d.ts +75 -0
- package/package.json +46 -29
- package/src/api/carto-api-error.ts +1 -1
- package/src/api/query.ts +5 -5
- package/src/api/request-with-parameters.ts +6 -6
- package/src/client.ts +3 -3
- package/src/constants-internal.ts +5 -5
- package/src/constants.ts +28 -3
- package/src/deck/get-data-filter-extension-props.ts +164 -0
- package/src/deck/index.ts +1 -0
- package/src/filters/Filter.ts +179 -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 +50 -0
- package/src/filters/tileFeaturesGeometries.ts +444 -0
- package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
- package/src/filters.ts +4 -4
- package/src/geo.ts +12 -14
- package/src/index.ts +7 -0
- package/src/models/common.ts +11 -9
- package/src/models/index.ts +1 -1
- package/src/models/model.ts +3 -4
- 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/base-source.ts +8 -8
- package/src/sources/boundary-query-source.ts +2 -2
- package/src/sources/boundary-table-source.ts +2 -2
- package/src/sources/h3-query-source.ts +7 -5
- package/src/sources/h3-table-source.ts +7 -5
- package/src/sources/h3-tileset-source.ts +2 -2
- package/src/sources/index.ts +26 -26
- package/src/sources/quadbin-query-source.ts +7 -5
- package/src/sources/quadbin-table-source.ts +7 -5
- package/src/sources/quadbin-tileset-source.ts +2 -2
- package/src/sources/raster-source.ts +3 -2
- package/src/sources/types.ts +3 -3
- package/src/sources/vector-query-source.ts +7 -5
- package/src/sources/vector-table-source.ts +7 -5
- package/src/sources/vector-tileset-source.ts +2 -2
- package/src/spatial-index.ts +4 -5
- package/src/types-internal.ts +11 -5
- package/src/types.ts +73 -15
- 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 +3 -3
- package/src/widget-sources/index.ts +3 -1
- package/src/widget-sources/types.ts +39 -25
- package/src/widget-sources/widget-query-source.ts +12 -5
- package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +51 -171
- package/src/widget-sources/widget-source.ts +173 -0
- package/src/widget-sources/widget-table-source.ts +12 -5
- package/src/widget-sources/widget-tileset-source.ts +456 -0
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import intersects from '@turf/boolean-intersects';
|
|
2
2
|
import bboxPolygon from '@turf/bbox-polygon';
|
|
3
|
+
import booleanWithin from '@turf/boolean-within';
|
|
4
|
+
import intersect from '@turf/intersect';
|
|
5
|
+
import { featureCollection, feature, polygon, multiPolygon } from '@turf/helpers';
|
|
6
|
+
import bboxClip from '@turf/bbox-clip';
|
|
7
|
+
import { getResolution as getResolution$2, polygonToCells } from 'h3-js';
|
|
3
8
|
import union from '@turf/union';
|
|
4
9
|
import { getType } from '@turf/invariant';
|
|
5
|
-
import {
|
|
10
|
+
import { booleanEqual } from '@turf/boolean-equal';
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* @internal
|
|
9
|
-
* @
|
|
14
|
+
* @privateRemarks Source: @carto/react-core, @carto/constants, @deck.gl/carto
|
|
10
15
|
*/
|
|
11
16
|
let client = 'deck-gl-carto';
|
|
12
17
|
/**
|
|
13
18
|
* Returns current client ID, used to categorize API requests. For internal use only.
|
|
14
19
|
*
|
|
15
20
|
* @internal
|
|
16
|
-
* @
|
|
21
|
+
* @privateRemarks Source: @carto/react-core
|
|
17
22
|
*/
|
|
18
23
|
function getClient() {
|
|
19
24
|
return client;
|
|
@@ -22,7 +27,7 @@ function getClient() {
|
|
|
22
27
|
* Sets current client ID, used to categorize API requests. For internal use only.
|
|
23
28
|
*
|
|
24
29
|
* @internal
|
|
25
|
-
* @
|
|
30
|
+
* @privateRemarks Source: @carto/react-core
|
|
26
31
|
*/
|
|
27
32
|
function setClient(c) {
|
|
28
33
|
client = c;
|
|
@@ -33,34 +38,1389 @@ function setClient(c) {
|
|
|
33
38
|
*
|
|
34
39
|
* Example:
|
|
35
40
|
*
|
|
36
|
-
* ```javascript
|
|
37
|
-
* import { FilterType } from '@carto/api-client';
|
|
38
|
-
* const filters = {
|
|
39
|
-
* column_name: { [FilterType.IN]: { values: ['a', 'b', 'c'] } }
|
|
40
|
-
* };
|
|
41
|
+
* ```javascript
|
|
42
|
+
* import { FilterType } from '@carto/api-client';
|
|
43
|
+
* const filters = {
|
|
44
|
+
* column_name: { [FilterType.IN]: { values: ['a', 'b', 'c'] } }
|
|
45
|
+
* };
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @privateRemarks Source: @carto/react-api, @deck.gl/carto
|
|
49
|
+
*/
|
|
50
|
+
var FilterType;
|
|
51
|
+
(function (FilterType) {
|
|
52
|
+
FilterType["IN"] = "in";
|
|
53
|
+
/** [a, b] both are included. */
|
|
54
|
+
FilterType["BETWEEN"] = "between";
|
|
55
|
+
/** [a, b) a is included, b is not. */
|
|
56
|
+
FilterType["CLOSED_OPEN"] = "closed_open";
|
|
57
|
+
FilterType["TIME"] = "time";
|
|
58
|
+
FilterType["STRING_SEARCH"] = "stringSearch";
|
|
59
|
+
})(FilterType || (FilterType = {}));
|
|
60
|
+
/** @privateRemarks Source: @carto/constants */
|
|
61
|
+
var ApiVersion;
|
|
62
|
+
(function (ApiVersion) {
|
|
63
|
+
ApiVersion["V1"] = "v1";
|
|
64
|
+
ApiVersion["V2"] = "v2";
|
|
65
|
+
ApiVersion["V3"] = "v3";
|
|
66
|
+
})(ApiVersion || (ApiVersion = {}));
|
|
67
|
+
/** @privateRemarks Source: @carto/constants, @deck.gl/carto */
|
|
68
|
+
const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
|
|
69
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
70
|
+
var TileFormat;
|
|
71
|
+
(function (TileFormat) {
|
|
72
|
+
TileFormat["MVT"] = "mvt";
|
|
73
|
+
TileFormat["JSON"] = "json";
|
|
74
|
+
TileFormat["GEOJSON"] = "geojson";
|
|
75
|
+
TileFormat["BINARY"] = "binary";
|
|
76
|
+
})(TileFormat || (TileFormat = {}));
|
|
77
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
78
|
+
var SpatialIndex;
|
|
79
|
+
(function (SpatialIndex) {
|
|
80
|
+
SpatialIndex["H3"] = "h3";
|
|
81
|
+
SpatialIndex["S2"] = "s2";
|
|
82
|
+
SpatialIndex["QUADBIN"] = "quadbin";
|
|
83
|
+
})(SpatialIndex || (SpatialIndex = {}));
|
|
84
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
85
|
+
var Provider;
|
|
86
|
+
(function (Provider) {
|
|
87
|
+
Provider["BIGQUERY"] = "bigquery";
|
|
88
|
+
Provider["REDSHIFT"] = "redshift";
|
|
89
|
+
Provider["POSTGRES"] = "postgres";
|
|
90
|
+
Provider["SNOWFLAKE"] = "snowflake";
|
|
91
|
+
Provider["DATABRICKS"] = "databricks";
|
|
92
|
+
Provider["DATABRICKS_REST"] = "databricksRest";
|
|
93
|
+
})(Provider || (Provider = {}));
|
|
94
|
+
|
|
95
|
+
function _extends() {
|
|
96
|
+
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
97
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
98
|
+
var t = arguments[e];
|
|
99
|
+
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
100
|
+
}
|
|
101
|
+
return n;
|
|
102
|
+
}, _extends.apply(null, arguments);
|
|
103
|
+
}
|
|
104
|
+
function _objectWithoutPropertiesLoose(r, e) {
|
|
105
|
+
if (null == r) return {};
|
|
106
|
+
var t = {};
|
|
107
|
+
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
108
|
+
if (e.includes(n)) continue;
|
|
109
|
+
t[n] = r[n];
|
|
110
|
+
}
|
|
111
|
+
return t;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function makeIntervalComplete(intervals) {
|
|
115
|
+
return intervals.map(val => {
|
|
116
|
+
if (val[0] === undefined || val[0] === null) {
|
|
117
|
+
return [Number.MIN_SAFE_INTEGER, val[1]];
|
|
118
|
+
}
|
|
119
|
+
if (val[1] === undefined || val[1] === null) {
|
|
120
|
+
return [val[0], Number.MAX_SAFE_INTEGER];
|
|
121
|
+
}
|
|
122
|
+
return val;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const filterFunctions = {
|
|
127
|
+
[FilterType.IN]: filterIn,
|
|
128
|
+
[FilterType.BETWEEN]: filterBetween,
|
|
129
|
+
[FilterType.TIME]: filterTime,
|
|
130
|
+
[FilterType.CLOSED_OPEN]: filterClosedOpen,
|
|
131
|
+
[FilterType.STRING_SEARCH]: filterStringSearch
|
|
132
|
+
};
|
|
133
|
+
function filterIn(filterValues, featureValue) {
|
|
134
|
+
return filterValues.includes(featureValue);
|
|
135
|
+
}
|
|
136
|
+
// FilterTypes.BETWEEN
|
|
137
|
+
function filterBetween(filterValues, featureValue) {
|
|
138
|
+
const checkRange = range => {
|
|
139
|
+
const [lowerBound, upperBound] = range;
|
|
140
|
+
return featureValue >= lowerBound && featureValue <= upperBound;
|
|
141
|
+
};
|
|
142
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
143
|
+
}
|
|
144
|
+
function filterTime(filterValues, featureValue) {
|
|
145
|
+
const featureValueAsTimestamp = new Date(featureValue).getTime();
|
|
146
|
+
if (isFinite(featureValueAsTimestamp)) {
|
|
147
|
+
return filterBetween(filterValues, featureValueAsTimestamp);
|
|
148
|
+
} else {
|
|
149
|
+
throw new Error(`Column used to filter by time isn't well formatted.`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// FilterTypes.CLOSED_OPEN
|
|
153
|
+
function filterClosedOpen(filterValues, featureValue) {
|
|
154
|
+
const checkRange = range => {
|
|
155
|
+
const [lowerBound, upperBound] = range;
|
|
156
|
+
return featureValue >= lowerBound && featureValue < upperBound;
|
|
157
|
+
};
|
|
158
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
159
|
+
}
|
|
160
|
+
// FilterTypes.STRING_SEARCH
|
|
161
|
+
function filterStringSearch(filterValues, featureValue, params = {}) {
|
|
162
|
+
const normalizedFeatureValue = normalize(featureValue, params);
|
|
163
|
+
const stringRegExp = params.useRegExp ? filterValues : filterValues.map(filterValue => {
|
|
164
|
+
let stringRegExp = escapeRegExp(normalize(filterValue, params));
|
|
165
|
+
if (params.mustStart) stringRegExp = `^${stringRegExp}`;
|
|
166
|
+
if (params.mustEnd) stringRegExp = `${stringRegExp}$`;
|
|
167
|
+
return stringRegExp;
|
|
168
|
+
});
|
|
169
|
+
const regex = new RegExp(stringRegExp.join('|'), params.caseSensitive ? 'g' : 'gi');
|
|
170
|
+
return !!normalizedFeatureValue.match(regex);
|
|
171
|
+
}
|
|
172
|
+
// Aux
|
|
173
|
+
const specialCharRegExp = /[.*+?^${}()|[\]\\]/g;
|
|
174
|
+
const normalizeRegExp = /(?:[\^`\xA8\xAF\xB4\xB7\xB8\u02B0-\u034E\u0350-\u0357\u035D-\u0362\u0374\u0375\u037A\u0384\u0385\u0483-\u0487\u0559\u0591-\u05A1\u05A3-\u05BD\u05BF\u05C1\u05C2\u05C4\u064B-\u0652\u0657\u0658\u06DF\u06E0\u06E5\u06E6\u06EA-\u06EC\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F5\u0818\u0819\u0898-\u089F\u08C9-\u08D2\u08E3-\u08FE\u093C\u094D\u0951-\u0954\u0971\u09BC\u09CD\u0A3C\u0A4D\u0ABC\u0ACD\u0AFD-\u0AFF\u0B3C\u0B4D\u0B55\u0BCD\u0C3C\u0C4D\u0CBC\u0CCD\u0D3B\u0D3C\u0D4D\u0DCA\u0E3A\u0E47-\u0E4C\u0E4E\u0EBA\u0EC8-\u0ECC\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F82-\u0F84\u0F86\u0F87\u0FC6\u1037\u1039\u103A\u1063\u1064\u1069-\u106D\u1087-\u108D\u108F\u109A\u109B\u135D-\u135F\u1714\u1715\u1734\u17C9-\u17D3\u17DD\u1939-\u193B\u1A60\u1A75-\u1A7C\u1A7F\u1AB0-\u1ABE\u1AC1-\u1ACB\u1B34\u1B44\u1B6B-\u1B73\u1BAA\u1BAB\u1BE6\u1BF2\u1BF3\u1C36\u1C37\u1C78-\u1C7D\u1CD0-\u1CE8\u1CED\u1CF4\u1CF7-\u1CF9\u1D2C-\u1D6A\u1DC4-\u1DCF\u1DF5-\u1DFF\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2CEF-\u2CF1\u2E2F\u302A-\u302F\u3099-\u309C\u30FC\uA66F\uA67C\uA67D\uA67F\uA69C\uA69D\uA6F0\uA6F1\uA700-\uA721\uA788-\uA78A\uA7F8\uA7F9\uA806\uA82C\uA8C4\uA8E0-\uA8F1\uA92B-\uA92E\uA953\uA9B3\uA9C0\uA9E5\uAA7B-\uAA7D\uAABF-\uAAC2\uAAF6\uAB5B-\uAB5F\uAB69-\uAB6B\uABEC\uABED\uFB1E\uFE20-\uFE2F\uFF3E\uFF40\uFF70\uFF9E\uFF9F\uFFE3]|\uD800\uDEE0|\uD801[\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD803[\uDD22-\uDD27\uDD4E\uDD69-\uDD6D\uDEFD-\uDEFF\uDF46-\uDF50\uDF82-\uDF85]|\uD804[\uDC46\uDC70\uDCB9\uDCBA\uDD33\uDD34\uDD73\uDDC0\uDDCA-\uDDCC\uDE35\uDE36\uDEE9\uDEEA\uDF3B\uDF3C\uDF4D\uDF66-\uDF6C\uDF70-\uDF74\uDFCE-\uDFD0\uDFD2\uDFD3\uDFE1\uDFE2]|\uD805[\uDC42\uDC46\uDCC2\uDCC3\uDDBF\uDDC0\uDE3F\uDEB6\uDEB7\uDF2B]|\uD806[\uDC39\uDC3A\uDD3D\uDD3E\uDD43\uDDE0\uDE34\uDE47\uDE99]|\uD807[\uDC3F\uDD42\uDD44\uDD45\uDD97\uDF41\uDF42\uDF5A]|\uD80D[\uDC47-\uDC55]|\uD818\uDD2F|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDD6B\uDD6C\uDF8F-\uDF9F\uDFF0\uDFF1]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD833[\uDF00-\uDF2D\uDF30-\uDF46]|\uD834[\uDD67-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD]|\uD838[\uDC30-\uDC6D\uDD30-\uDD36\uDEAE\uDEEC-\uDEEF]|\uD839[\uDDEE\uDDEF]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD46\uDD48-\uDD4A])/g;
|
|
175
|
+
function escapeRegExp(value) {
|
|
176
|
+
return value.replace(specialCharRegExp, '\\$&');
|
|
177
|
+
}
|
|
178
|
+
function normalize(data, params) {
|
|
179
|
+
let normalizedData = String(data);
|
|
180
|
+
if (!params.keepSpecialCharacters) normalizedData = normalizedData.normalize('NFD').replace(normalizeRegExp, '');
|
|
181
|
+
return normalizedData;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const LOGICAL_OPERATOR_METHODS = {
|
|
185
|
+
and: 'every',
|
|
186
|
+
or: 'some'
|
|
187
|
+
};
|
|
188
|
+
function passesFilter(columns, filters, feature, filtersLogicalOperator) {
|
|
189
|
+
const method = LOGICAL_OPERATOR_METHODS[filtersLogicalOperator];
|
|
190
|
+
return columns[method](column => {
|
|
191
|
+
const columnFilters = filters[column];
|
|
192
|
+
const columnFilterTypes = Object.keys(columnFilters);
|
|
193
|
+
if (!feature || feature[column] === null || feature[column] === undefined) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return columnFilterTypes.every(filter => {
|
|
197
|
+
const filterFunction = filterFunctions[filter];
|
|
198
|
+
if (!filterFunction) {
|
|
199
|
+
throw new Error(`"${filter}" filter is not implemented.`);
|
|
200
|
+
}
|
|
201
|
+
return filterFunction(columnFilters[filter].values, feature[column], columnFilters[filter].params);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* @internal
|
|
207
|
+
* @privateRemarks Exported for use in @deck.gl/carto's getDataFilterExtensionProps.
|
|
208
|
+
*/
|
|
209
|
+
function _buildFeatureFilter({
|
|
210
|
+
filters = {},
|
|
211
|
+
type = 'boolean',
|
|
212
|
+
filtersLogicalOperator = 'and'
|
|
213
|
+
}) {
|
|
214
|
+
const columns = Object.keys(filters);
|
|
215
|
+
if (!columns.length) {
|
|
216
|
+
return () => type === 'number' ? 1 : true;
|
|
217
|
+
}
|
|
218
|
+
return feature => {
|
|
219
|
+
const f = feature.properties || feature;
|
|
220
|
+
const featurePassesFilter = passesFilter(columns, filters, f, filtersLogicalOperator);
|
|
221
|
+
return type === 'number' ? Number(featurePassesFilter) : featurePassesFilter;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Apply certain filters to a collection of features.
|
|
226
|
+
* @internal
|
|
227
|
+
*/
|
|
228
|
+
function applyFilters(features, filters, filtersLogicalOperator) {
|
|
229
|
+
return Object.keys(filters).length ? features.filter(_buildFeatureFilter({
|
|
230
|
+
filters,
|
|
231
|
+
filtersLogicalOperator
|
|
232
|
+
})) : features;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Binary.
|
|
236
|
+
* @internal
|
|
237
|
+
*/
|
|
238
|
+
function buildBinaryFeatureFilter({
|
|
239
|
+
filters = {}
|
|
240
|
+
}) {
|
|
241
|
+
const columns = Object.keys(filters);
|
|
242
|
+
if (!columns.length) {
|
|
243
|
+
return () => 1;
|
|
244
|
+
}
|
|
245
|
+
return (featureIdIdx, binaryData) => passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
|
|
246
|
+
}
|
|
247
|
+
function getValueFromNumericProps(featureIdIdx, binaryData, {
|
|
248
|
+
column
|
|
249
|
+
}) {
|
|
250
|
+
var _binaryData$numericPr;
|
|
251
|
+
return (_binaryData$numericPr = binaryData.numericProps) == null || (_binaryData$numericPr = _binaryData$numericPr[column]) == null ? void 0 : _binaryData$numericPr.value[featureIdIdx];
|
|
252
|
+
}
|
|
253
|
+
function getValueFromProperties(featureIdIdx, binaryData, {
|
|
254
|
+
column
|
|
255
|
+
}) {
|
|
256
|
+
var _binaryData$propertie;
|
|
257
|
+
const propertyIdx = binaryData.featureIds.value[featureIdIdx];
|
|
258
|
+
return (_binaryData$propertie = binaryData.properties[propertyIdx]) == null ? void 0 : _binaryData$propertie[column];
|
|
259
|
+
}
|
|
260
|
+
const GET_VALUE_BY_BINARY_PROP = {
|
|
261
|
+
properties: getValueFromProperties,
|
|
262
|
+
numericProps: getValueFromNumericProps
|
|
263
|
+
};
|
|
264
|
+
function getBinaryPropertyByFilterValues(filterValues) {
|
|
265
|
+
return typeof filterValues.flat()[0] === 'string' ? 'properties' : 'numericProps';
|
|
266
|
+
}
|
|
267
|
+
function getFeatureValue(featureIdIdx, binaryData, filter) {
|
|
268
|
+
const {
|
|
269
|
+
column,
|
|
270
|
+
values
|
|
271
|
+
} = filter;
|
|
272
|
+
const binaryProp = getBinaryPropertyByFilterValues(values);
|
|
273
|
+
const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
|
|
274
|
+
return getFeatureValueFn(featureIdIdx, binaryData, {
|
|
275
|
+
column
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData) {
|
|
279
|
+
return columns.every(column => {
|
|
280
|
+
const columnFilters = filters[column];
|
|
281
|
+
return Object.entries(columnFilters).every(([type, {
|
|
282
|
+
values
|
|
283
|
+
}]) => {
|
|
284
|
+
const filterFn = filterFunctions[type];
|
|
285
|
+
if (!filterFn) {
|
|
286
|
+
throw new Error(`"${type}" filter is not implemented.`);
|
|
287
|
+
}
|
|
288
|
+
if (!values) return 0;
|
|
289
|
+
const featureValue = getFeatureValue(featureIdIdx, binaryData, {
|
|
290
|
+
type: type,
|
|
291
|
+
column,
|
|
292
|
+
values
|
|
293
|
+
});
|
|
294
|
+
if (featureValue === undefined || featureValue === null) return 0;
|
|
295
|
+
return filterFn(values, featureValue);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function geojsonFeatures({
|
|
301
|
+
geojson,
|
|
302
|
+
spatialFilter,
|
|
303
|
+
uniqueIdProperty
|
|
304
|
+
}) {
|
|
305
|
+
let uniqueIdx = 0;
|
|
306
|
+
const map = new Map();
|
|
307
|
+
if (!spatialFilter) {
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
for (const feature of geojson.features) {
|
|
311
|
+
const uniqueId = uniqueIdProperty ? feature.properties[uniqueIdProperty] : ++uniqueIdx;
|
|
312
|
+
if (!map.has(uniqueId) && intersects(spatialFilter, feature)) {
|
|
313
|
+
map.set(uniqueId, feature.properties);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return Array.from(map.values());
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// math.gl
|
|
320
|
+
// SPDX-License-Identifier: MIT
|
|
321
|
+
// Copyright (c) vis.gl contributors
|
|
322
|
+
const DEFAULT_CONFIG = {
|
|
323
|
+
EPSILON: 1e-12,
|
|
324
|
+
debug: false,
|
|
325
|
+
precision: 4,
|
|
326
|
+
printTypes: false,
|
|
327
|
+
printDegrees: false,
|
|
328
|
+
printRowMajor: true,
|
|
329
|
+
_cartographicRadians: false
|
|
330
|
+
};
|
|
331
|
+
// Configuration is truly global as of v3.6 to ensure single config even if multiple copies of math.gl
|
|
332
|
+
// Multiple copies of config can be quite tricky to debug...
|
|
333
|
+
globalThis.mathgl = globalThis.mathgl || {
|
|
334
|
+
config: {
|
|
335
|
+
...DEFAULT_CONFIG
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* Check if value is an "array"
|
|
340
|
+
* Returns `true` if value is either an array or a typed array
|
|
341
|
+
* Note: returns `false` for `ArrayBuffer` and `DataView` instances
|
|
342
|
+
* @note isTypedArray and isNumericArray are often more useful in TypeScript
|
|
343
|
+
*/
|
|
344
|
+
function isArray(value) {
|
|
345
|
+
return Array.isArray(value) || ArrayBuffer.isView(value) && !(value instanceof DataView);
|
|
346
|
+
}
|
|
347
|
+
function lerp(a, b, t) {
|
|
348
|
+
if (isArray(a)) {
|
|
349
|
+
return a.map((ai, i) => lerp(ai, b[i], t));
|
|
350
|
+
}
|
|
351
|
+
return t * b + (1 - t) * a;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Replacement for the external assert method to reduce bundle size
|
|
355
|
+
// Note: We don't use the second "message" argument in calling code,
|
|
356
|
+
// so no need to support it here
|
|
357
|
+
function assert$1(condition, message) {
|
|
358
|
+
if (!condition) {
|
|
359
|
+
throw new Error(message || '@math.gl/web-mercator: assertion failed.');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// TODO - THE UTILITIES IN THIS FILE SHOULD BE IMPORTED FROM WEB-MERCATOR-VIEWPORT MODULE
|
|
364
|
+
// CONSTANTS
|
|
365
|
+
const PI = Math.PI;
|
|
366
|
+
const PI_4 = PI / 4;
|
|
367
|
+
const DEGREES_TO_RADIANS = PI / 180;
|
|
368
|
+
const RADIANS_TO_DEGREES = 180 / PI;
|
|
369
|
+
const TILE_SIZE = 512;
|
|
370
|
+
/**
|
|
371
|
+
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
|
|
372
|
+
* Performs the nonlinear part of the web mercator projection.
|
|
373
|
+
* Remaining projection is done with 4x4 matrices which also handles
|
|
374
|
+
* perspective.
|
|
375
|
+
*
|
|
376
|
+
* @param lngLat - [lng, lat] coordinates
|
|
377
|
+
* Specifies a point on the sphere to project onto the map.
|
|
378
|
+
* @return [x,y] coordinates.
|
|
379
|
+
*/
|
|
380
|
+
function lngLatToWorld(lngLat) {
|
|
381
|
+
const [lng, lat] = lngLat;
|
|
382
|
+
assert$1(Number.isFinite(lng));
|
|
383
|
+
assert$1(Number.isFinite(lat) && lat >= -90 && lat <= 90, 'invalid latitude');
|
|
384
|
+
const lambda2 = lng * DEGREES_TO_RADIANS;
|
|
385
|
+
const phi2 = lat * DEGREES_TO_RADIANS;
|
|
386
|
+
const x = TILE_SIZE * (lambda2 + PI) / (2 * PI);
|
|
387
|
+
const y = TILE_SIZE * (PI + Math.log(Math.tan(PI_4 + phi2 * 0.5))) / (2 * PI);
|
|
388
|
+
return [x, y];
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Unproject world point [x,y] on map onto {lat, lon} on sphere
|
|
392
|
+
*
|
|
393
|
+
* @param xy - array with [x,y] members
|
|
394
|
+
* representing point on projected map plane
|
|
395
|
+
* @return - array with [x,y] of point on sphere.
|
|
396
|
+
* Has toArray method if you need a GeoJSON Array.
|
|
397
|
+
* Per cartographic tradition, lat and lon are specified as degrees.
|
|
398
|
+
*/
|
|
399
|
+
function worldToLngLat(xy) {
|
|
400
|
+
const [x, y] = xy;
|
|
401
|
+
const lambda2 = x / TILE_SIZE * (2 * PI) - PI;
|
|
402
|
+
const phi2 = 2 * (Math.atan(Math.exp(y / TILE_SIZE * (2 * PI) - PI)) - PI_4);
|
|
403
|
+
return [lambda2 * RADIANS_TO_DEGREES, phi2 * RADIANS_TO_DEGREES];
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const TRANSFORM_FN$1 = {
|
|
407
|
+
Point: transformPoint$1,
|
|
408
|
+
MultiPoint: transformMultiPoint$1,
|
|
409
|
+
LineString: transformLineString$1,
|
|
410
|
+
MultiLineString: transformMultiLineString$1,
|
|
411
|
+
Polygon: transformPolygon$1,
|
|
412
|
+
MultiPolygon: transformMultiPolygon$1
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Transform WGS84 coordinates to tile coords.
|
|
416
|
+
* 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)
|
|
417
|
+
*
|
|
418
|
+
* @param geometry - any valid geojson geometry
|
|
419
|
+
* @param bbox - geojson bbox
|
|
420
|
+
*/
|
|
421
|
+
function transformToTileCoords(geometry, bbox) {
|
|
422
|
+
const [west, south, east, north] = bbox;
|
|
423
|
+
const nw = projectFlat([west, north]);
|
|
424
|
+
const se = projectFlat([east, south]);
|
|
425
|
+
const projectedBbox = [nw, se];
|
|
426
|
+
if (geometry.type === 'GeometryCollection') {
|
|
427
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
428
|
+
}
|
|
429
|
+
const transformFn = TRANSFORM_FN$1[geometry.type];
|
|
430
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
431
|
+
return _extends({}, geometry, {
|
|
432
|
+
coordinates
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
function transformPoint$1([pointX, pointY], [nw, se]) {
|
|
436
|
+
const x = inverseLerp(nw[0], se[0], pointX);
|
|
437
|
+
const y = inverseLerp(nw[1], se[1], pointY);
|
|
438
|
+
return [x, y];
|
|
439
|
+
}
|
|
440
|
+
function getPoints$1(geometry, bbox) {
|
|
441
|
+
return geometry.map(g => transformPoint$1(projectFlat(g), bbox));
|
|
442
|
+
}
|
|
443
|
+
function transformMultiPoint$1(multiPoint, bbox) {
|
|
444
|
+
return getPoints$1(multiPoint, bbox);
|
|
445
|
+
}
|
|
446
|
+
function transformLineString$1(line, bbox) {
|
|
447
|
+
return getPoints$1(line, bbox);
|
|
448
|
+
}
|
|
449
|
+
function transformMultiLineString$1(multiLineString, bbox) {
|
|
450
|
+
return multiLineString.map(lineString => transformLineString$1(lineString, bbox));
|
|
451
|
+
}
|
|
452
|
+
function transformPolygon$1(polygon, bbox) {
|
|
453
|
+
return polygon.map(polygonRing => getPoints$1(polygonRing, bbox));
|
|
454
|
+
}
|
|
455
|
+
function transformMultiPolygon$1(multiPolygon, bbox) {
|
|
456
|
+
return multiPolygon.map(polygon => transformPolygon$1(polygon, bbox));
|
|
457
|
+
}
|
|
458
|
+
function projectFlat(xyz) {
|
|
459
|
+
return lngLatToWorld(xyz);
|
|
460
|
+
}
|
|
461
|
+
function inverseLerp(a, b, x) {
|
|
462
|
+
return (x - a) / (b - a);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const TRANSFORM_FN = {
|
|
466
|
+
Point: transformPoint,
|
|
467
|
+
MultiPoint: transformMultiPoint,
|
|
468
|
+
LineString: transformLineString,
|
|
469
|
+
MultiLineString: transformMultiLineString,
|
|
470
|
+
Polygon: transformPolygon,
|
|
471
|
+
MultiPolygon: transformMultiPolygon
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* Transform tile coords to WGS84 coordinates.
|
|
475
|
+
*
|
|
476
|
+
* @param geometry - any valid geojson geometry
|
|
477
|
+
* @param bbox - geojson bbox
|
|
478
|
+
*/
|
|
479
|
+
function transformTileCoordsToWGS84(geometry, bbox) {
|
|
480
|
+
const [west, south, east, north] = bbox;
|
|
481
|
+
const nw = lngLatToWorld([west, north]);
|
|
482
|
+
const se = lngLatToWorld([east, south]);
|
|
483
|
+
const projectedBbox = [nw, se];
|
|
484
|
+
if (geometry.type === 'GeometryCollection') {
|
|
485
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
486
|
+
}
|
|
487
|
+
const transformFn = TRANSFORM_FN[geometry.type];
|
|
488
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
489
|
+
return _extends({}, geometry, {
|
|
490
|
+
coordinates
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
function transformPoint([pointX, pointY], [nw, se]) {
|
|
494
|
+
const x = lerp(nw[0], se[0], pointX);
|
|
495
|
+
const y = lerp(nw[1], se[1], pointY);
|
|
496
|
+
return worldToLngLat([x, y]);
|
|
497
|
+
}
|
|
498
|
+
function getPoints(geometry, bbox) {
|
|
499
|
+
return geometry.map(g => transformPoint(g, bbox));
|
|
500
|
+
}
|
|
501
|
+
function transformMultiPoint(multiPoint, bbox) {
|
|
502
|
+
return getPoints(multiPoint, bbox);
|
|
503
|
+
}
|
|
504
|
+
function transformLineString(line, bbox) {
|
|
505
|
+
return getPoints(line, bbox);
|
|
506
|
+
}
|
|
507
|
+
function transformMultiLineString(multiLineString, bbox) {
|
|
508
|
+
return multiLineString.map(lineString => transformLineString(lineString, bbox));
|
|
509
|
+
}
|
|
510
|
+
function transformPolygon(polygon, bbox) {
|
|
511
|
+
return polygon.map(polygonRing => getPoints(polygonRing, bbox));
|
|
512
|
+
}
|
|
513
|
+
function transformMultiPolygon(multiPolygon, bbox) {
|
|
514
|
+
return multiPolygon.map(polygon => transformPolygon(polygon, bbox));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const FEATURE_GEOM_PROPERTY = '__geomValue';
|
|
518
|
+
function tileFeaturesGeometries({
|
|
519
|
+
tiles,
|
|
520
|
+
tileFormat,
|
|
521
|
+
spatialFilter,
|
|
522
|
+
uniqueIdProperty,
|
|
523
|
+
options
|
|
524
|
+
}) {
|
|
525
|
+
const map = new Map();
|
|
526
|
+
for (const tile of tiles) {
|
|
527
|
+
// Discard if it's not a visible tile (only check false value, not undefined)
|
|
528
|
+
// or tile has not data
|
|
529
|
+
if (tile.isVisible === false || !tile.data) {
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
const bbox = [tile.bbox.west, tile.bbox.south, tile.bbox.east, tile.bbox.north];
|
|
533
|
+
const bboxToGeom = bboxPolygon(bbox);
|
|
534
|
+
const tileIsFullyVisible = booleanWithin(bboxToGeom, spatialFilter);
|
|
535
|
+
// Clip the geometry to intersect with the tile
|
|
536
|
+
const spatialFilterFeature = {
|
|
537
|
+
type: 'Feature',
|
|
538
|
+
geometry: spatialFilter,
|
|
539
|
+
properties: {}
|
|
540
|
+
};
|
|
541
|
+
const clippedGeometryToIntersect = intersect(featureCollection([bboxToGeom, spatialFilterFeature]));
|
|
542
|
+
if (!clippedGeometryToIntersect) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
// We assume that MVT tileFormat uses local coordinates so we transform the geometry to intersect to tile coordinates [0..1],
|
|
546
|
+
// while in the case of 'geojson' or binary, the geometries are already in WGS84
|
|
547
|
+
const transformedGeometryToIntersect = tileFormat === TileFormat.MVT ? transformToTileCoords(clippedGeometryToIntersect.geometry, bbox) : clippedGeometryToIntersect.geometry;
|
|
548
|
+
createIndicesForPoints(tile.data.points);
|
|
549
|
+
calculateFeatures({
|
|
550
|
+
map,
|
|
551
|
+
tileIsFullyVisible,
|
|
552
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
553
|
+
data: tile.data.points,
|
|
554
|
+
type: 'Point',
|
|
555
|
+
bbox,
|
|
556
|
+
tileFormat,
|
|
557
|
+
uniqueIdProperty,
|
|
558
|
+
options
|
|
559
|
+
});
|
|
560
|
+
calculateFeatures({
|
|
561
|
+
map,
|
|
562
|
+
tileIsFullyVisible,
|
|
563
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
564
|
+
data: tile.data.lines,
|
|
565
|
+
type: 'LineString',
|
|
566
|
+
bbox,
|
|
567
|
+
tileFormat,
|
|
568
|
+
uniqueIdProperty,
|
|
569
|
+
options
|
|
570
|
+
});
|
|
571
|
+
calculateFeatures({
|
|
572
|
+
map,
|
|
573
|
+
tileIsFullyVisible,
|
|
574
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
575
|
+
data: tile.data.polygons,
|
|
576
|
+
type: 'Polygon',
|
|
577
|
+
bbox,
|
|
578
|
+
tileFormat,
|
|
579
|
+
uniqueIdProperty,
|
|
580
|
+
options
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
return Array.from(map.values());
|
|
584
|
+
}
|
|
585
|
+
function processTileFeatureProperties({
|
|
586
|
+
map,
|
|
587
|
+
data,
|
|
588
|
+
startIndex,
|
|
589
|
+
endIndex,
|
|
590
|
+
type,
|
|
591
|
+
bbox,
|
|
592
|
+
tileFormat,
|
|
593
|
+
uniqueIdProperty,
|
|
594
|
+
storeGeometry,
|
|
595
|
+
geometryIntersection
|
|
596
|
+
}) {
|
|
597
|
+
const tileProps = getPropertiesFromTile(data, startIndex);
|
|
598
|
+
const uniquePropertyValue = getUniquePropertyValue(tileProps, uniqueIdProperty, map);
|
|
599
|
+
if (!uniquePropertyValue || map.has(uniquePropertyValue)) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
let geometry = null;
|
|
603
|
+
// Only calculate geometry if necessary
|
|
604
|
+
if (storeGeometry || geometryIntersection) {
|
|
605
|
+
const {
|
|
606
|
+
positions
|
|
607
|
+
} = data;
|
|
608
|
+
const ringCoordinates = getRingCoordinatesFor(startIndex, endIndex, positions);
|
|
609
|
+
geometry = getFeatureByType(ringCoordinates, type);
|
|
610
|
+
}
|
|
611
|
+
// If intersection is required, check before proceeding
|
|
612
|
+
if (geometry && geometryIntersection && !intersects(geometry, geometryIntersection)) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const properties = parseProperties(tileProps);
|
|
616
|
+
// Only save geometry if necessary
|
|
617
|
+
if (storeGeometry && geometry) {
|
|
618
|
+
properties[FEATURE_GEOM_PROPERTY] = tileFormat === TileFormat.MVT ? transformTileCoordsToWGS84(geometry, bbox) : geometry;
|
|
619
|
+
}
|
|
620
|
+
map.set(uniquePropertyValue, properties);
|
|
621
|
+
}
|
|
622
|
+
function addIntersectedFeaturesInTile({
|
|
623
|
+
map,
|
|
624
|
+
data,
|
|
625
|
+
geometryIntersection,
|
|
626
|
+
type,
|
|
627
|
+
bbox,
|
|
628
|
+
tileFormat,
|
|
629
|
+
uniqueIdProperty,
|
|
630
|
+
options
|
|
631
|
+
}) {
|
|
632
|
+
const indices = getIndices(data);
|
|
633
|
+
const storeGeometry = (options == null ? void 0 : options.storeGeometry) || false;
|
|
634
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
635
|
+
const startIndex = indices[i];
|
|
636
|
+
const endIndex = indices[i + 1];
|
|
637
|
+
processTileFeatureProperties({
|
|
638
|
+
map,
|
|
639
|
+
data,
|
|
640
|
+
startIndex,
|
|
641
|
+
endIndex,
|
|
642
|
+
type,
|
|
643
|
+
bbox,
|
|
644
|
+
tileFormat,
|
|
645
|
+
uniqueIdProperty,
|
|
646
|
+
storeGeometry,
|
|
647
|
+
geometryIntersection
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function getIndices(data) {
|
|
652
|
+
let indices;
|
|
653
|
+
switch (data.type) {
|
|
654
|
+
case 'Point':
|
|
655
|
+
// @ts-expect-error Missing or changed types?
|
|
656
|
+
indices = data.pointIndices;
|
|
657
|
+
break;
|
|
658
|
+
case 'LineString':
|
|
659
|
+
indices = data.pathIndices;
|
|
660
|
+
break;
|
|
661
|
+
case 'Polygon':
|
|
662
|
+
indices = data.primitivePolygonIndices;
|
|
663
|
+
break;
|
|
664
|
+
default:
|
|
665
|
+
throw new Error(`Unexpected type, "${data.type}"`);
|
|
666
|
+
}
|
|
667
|
+
return indices.value;
|
|
668
|
+
}
|
|
669
|
+
function getFeatureId(data, startIndex) {
|
|
670
|
+
return data.featureIds.value[startIndex];
|
|
671
|
+
}
|
|
672
|
+
function getPropertiesFromTile(data, startIndex) {
|
|
673
|
+
var _fields$featureId;
|
|
674
|
+
const featureId = getFeatureId(data, startIndex);
|
|
675
|
+
const {
|
|
676
|
+
properties,
|
|
677
|
+
numericProps,
|
|
678
|
+
fields
|
|
679
|
+
} = data;
|
|
680
|
+
const result = {
|
|
681
|
+
uniqueId: fields == null || (_fields$featureId = fields[featureId]) == null ? void 0 : _fields$featureId.id,
|
|
682
|
+
properties: properties[featureId],
|
|
683
|
+
numericProps: {}
|
|
684
|
+
};
|
|
685
|
+
for (const key in numericProps) {
|
|
686
|
+
result.numericProps[key] = numericProps[key].value[startIndex];
|
|
687
|
+
}
|
|
688
|
+
return result;
|
|
689
|
+
}
|
|
690
|
+
function parseProperties(tileProps) {
|
|
691
|
+
const {
|
|
692
|
+
properties,
|
|
693
|
+
numericProps
|
|
694
|
+
} = tileProps;
|
|
695
|
+
return Object.assign({}, properties, numericProps);
|
|
696
|
+
}
|
|
697
|
+
function getUniquePropertyValue(tileProps, uniqueIdProperty, map) {
|
|
698
|
+
if (uniqueIdProperty) {
|
|
699
|
+
return getValueFromTileProps(tileProps, uniqueIdProperty);
|
|
700
|
+
}
|
|
701
|
+
if (tileProps.uniqueId) {
|
|
702
|
+
return tileProps.uniqueId;
|
|
703
|
+
}
|
|
704
|
+
const artificialId = map.size + 1; // a counter, assumed as a valid new id
|
|
705
|
+
return getValueFromTileProps(tileProps, 'cartodb_id') || getValueFromTileProps(tileProps, 'geoid') || artificialId;
|
|
706
|
+
}
|
|
707
|
+
function getValueFromTileProps(tileProps, propertyName) {
|
|
708
|
+
const {
|
|
709
|
+
properties,
|
|
710
|
+
numericProps
|
|
711
|
+
} = tileProps;
|
|
712
|
+
return numericProps[propertyName] || properties[propertyName];
|
|
713
|
+
}
|
|
714
|
+
function getFeatureByType(coordinates, type) {
|
|
715
|
+
switch (type) {
|
|
716
|
+
case 'Polygon':
|
|
717
|
+
return {
|
|
718
|
+
type: 'Polygon',
|
|
719
|
+
coordinates: [coordinates]
|
|
720
|
+
};
|
|
721
|
+
case 'LineString':
|
|
722
|
+
return {
|
|
723
|
+
type: 'LineString',
|
|
724
|
+
coordinates
|
|
725
|
+
};
|
|
726
|
+
case 'Point':
|
|
727
|
+
return {
|
|
728
|
+
type: 'Point',
|
|
729
|
+
coordinates: coordinates[0]
|
|
730
|
+
};
|
|
731
|
+
default:
|
|
732
|
+
throw new Error('Invalid geometry type');
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function getRingCoordinatesFor(startIndex, endIndex, positions) {
|
|
736
|
+
const ringCoordinates = [];
|
|
737
|
+
for (let j = startIndex; j < endIndex; j++) {
|
|
738
|
+
ringCoordinates.push(Array.from(positions.value.subarray(j * positions.size, (j + 1) * positions.size)));
|
|
739
|
+
}
|
|
740
|
+
return ringCoordinates;
|
|
741
|
+
}
|
|
742
|
+
function calculateFeatures({
|
|
743
|
+
map,
|
|
744
|
+
tileIsFullyVisible,
|
|
745
|
+
geometryIntersection,
|
|
746
|
+
data,
|
|
747
|
+
type,
|
|
748
|
+
bbox,
|
|
749
|
+
tileFormat,
|
|
750
|
+
uniqueIdProperty,
|
|
751
|
+
options
|
|
752
|
+
}) {
|
|
753
|
+
if (!(data != null && data.properties.length)) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
if (tileIsFullyVisible) {
|
|
757
|
+
addAllFeaturesInTile({
|
|
758
|
+
map,
|
|
759
|
+
data,
|
|
760
|
+
type,
|
|
761
|
+
bbox,
|
|
762
|
+
tileFormat,
|
|
763
|
+
uniqueIdProperty,
|
|
764
|
+
options
|
|
765
|
+
});
|
|
766
|
+
} else {
|
|
767
|
+
addIntersectedFeaturesInTile({
|
|
768
|
+
map,
|
|
769
|
+
data,
|
|
770
|
+
geometryIntersection,
|
|
771
|
+
type,
|
|
772
|
+
bbox,
|
|
773
|
+
tileFormat,
|
|
774
|
+
uniqueIdProperty,
|
|
775
|
+
options
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function addAllFeaturesInTile({
|
|
780
|
+
map,
|
|
781
|
+
data,
|
|
782
|
+
type,
|
|
783
|
+
bbox,
|
|
784
|
+
tileFormat,
|
|
785
|
+
uniqueIdProperty,
|
|
786
|
+
options
|
|
787
|
+
}) {
|
|
788
|
+
const indices = getIndices(data);
|
|
789
|
+
const storeGeometry = (options == null ? void 0 : options.storeGeometry) || false;
|
|
790
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
791
|
+
const startIndex = indices[i];
|
|
792
|
+
const endIndex = indices[i + 1];
|
|
793
|
+
processTileFeatureProperties({
|
|
794
|
+
map,
|
|
795
|
+
data,
|
|
796
|
+
startIndex,
|
|
797
|
+
endIndex,
|
|
798
|
+
type,
|
|
799
|
+
bbox,
|
|
800
|
+
tileFormat,
|
|
801
|
+
uniqueIdProperty,
|
|
802
|
+
storeGeometry
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
function createIndicesForPoints(data) {
|
|
807
|
+
const featureIds = data.featureIds.value;
|
|
808
|
+
const lastFeatureId = featureIds[featureIds.length - 1];
|
|
809
|
+
const PointIndicesArray = featureIds.constructor;
|
|
810
|
+
const pointIndices = {
|
|
811
|
+
value: new PointIndicesArray(featureIds.length + 1),
|
|
812
|
+
size: 1
|
|
813
|
+
};
|
|
814
|
+
pointIndices.value.set(featureIds);
|
|
815
|
+
pointIndices.value.set([lastFeatureId + 1], featureIds.length);
|
|
816
|
+
// @ts-expect-error Missing or changed types?
|
|
817
|
+
data.pointIndices = pointIndices;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// a tile is an array [x,y,z]
|
|
821
|
+
var d2r = Math.PI / 180,
|
|
822
|
+
r2d = 180 / Math.PI;
|
|
823
|
+
function tileToBBOX(tile) {
|
|
824
|
+
var e = tile2lon(tile[0] + 1, tile[2]);
|
|
825
|
+
var w = tile2lon(tile[0], tile[2]);
|
|
826
|
+
var s = tile2lat(tile[1] + 1, tile[2]);
|
|
827
|
+
var n = tile2lat(tile[1], tile[2]);
|
|
828
|
+
return [w, s, e, n];
|
|
829
|
+
}
|
|
830
|
+
function tileToGeoJSON(tile) {
|
|
831
|
+
var bbox = tileToBBOX(tile);
|
|
832
|
+
var poly = {
|
|
833
|
+
type: 'Polygon',
|
|
834
|
+
coordinates: [[[bbox[0], bbox[1]], [bbox[0], bbox[3]], [bbox[2], bbox[3]], [bbox[2], bbox[1]], [bbox[0], bbox[1]]]]
|
|
835
|
+
};
|
|
836
|
+
return poly;
|
|
837
|
+
}
|
|
838
|
+
function tile2lon(x, z) {
|
|
839
|
+
return x / Math.pow(2, z) * 360 - 180;
|
|
840
|
+
}
|
|
841
|
+
function tile2lat(y, z) {
|
|
842
|
+
var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
|
|
843
|
+
return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
|
844
|
+
}
|
|
845
|
+
function pointToTile(lon, lat, z) {
|
|
846
|
+
var tile = pointToTileFraction(lon, lat, z);
|
|
847
|
+
tile[0] = Math.floor(tile[0]);
|
|
848
|
+
tile[1] = Math.floor(tile[1]);
|
|
849
|
+
return tile;
|
|
850
|
+
}
|
|
851
|
+
function getChildren(tile) {
|
|
852
|
+
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]];
|
|
853
|
+
}
|
|
854
|
+
function getParent(tile) {
|
|
855
|
+
// top left
|
|
856
|
+
if (tile[0] % 2 === 0 && tile[1] % 2 === 0) {
|
|
857
|
+
return [tile[0] / 2, tile[1] / 2, tile[2] - 1];
|
|
858
|
+
}
|
|
859
|
+
// bottom left
|
|
860
|
+
else if (tile[0] % 2 === 0 && !tile[1] % 2 === 0) {
|
|
861
|
+
return [tile[0] / 2, (tile[1] - 1) / 2, tile[2] - 1];
|
|
862
|
+
}
|
|
863
|
+
// top right
|
|
864
|
+
else if (!tile[0] % 2 === 0 && tile[1] % 2 === 0) {
|
|
865
|
+
return [(tile[0] - 1) / 2, tile[1] / 2, tile[2] - 1];
|
|
866
|
+
}
|
|
867
|
+
// bottom right
|
|
868
|
+
else {
|
|
869
|
+
return [(tile[0] - 1) / 2, (tile[1] - 1) / 2, tile[2] - 1];
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
function getSiblings(tile) {
|
|
873
|
+
return getChildren(getParent(tile));
|
|
874
|
+
}
|
|
875
|
+
function hasSiblings(tile, tiles) {
|
|
876
|
+
var siblings = getSiblings(tile);
|
|
877
|
+
for (var i = 0; i < siblings.length; i++) {
|
|
878
|
+
if (!hasTile(tiles, siblings[i])) return false;
|
|
879
|
+
}
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
function hasTile(tiles, tile) {
|
|
883
|
+
for (var i = 0; i < tiles.length; i++) {
|
|
884
|
+
if (tilesEqual(tiles[i], tile)) return true;
|
|
885
|
+
}
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
function tilesEqual(tile1, tile2) {
|
|
889
|
+
return tile1[0] === tile2[0] && tile1[1] === tile2[1] && tile1[2] === tile2[2];
|
|
890
|
+
}
|
|
891
|
+
function tileToQuadkey(tile) {
|
|
892
|
+
var index = '';
|
|
893
|
+
for (var z = tile[2]; z > 0; z--) {
|
|
894
|
+
var b = 0;
|
|
895
|
+
var mask = 1 << z - 1;
|
|
896
|
+
if ((tile[0] & mask) !== 0) b++;
|
|
897
|
+
if ((tile[1] & mask) !== 0) b += 2;
|
|
898
|
+
index += b.toString();
|
|
899
|
+
}
|
|
900
|
+
return index;
|
|
901
|
+
}
|
|
902
|
+
function quadkeyToTile(quadkey) {
|
|
903
|
+
var x = 0;
|
|
904
|
+
var y = 0;
|
|
905
|
+
var z = quadkey.length;
|
|
906
|
+
for (var i = z; i > 0; i--) {
|
|
907
|
+
var mask = 1 << i - 1;
|
|
908
|
+
switch (quadkey[z - i]) {
|
|
909
|
+
case '0':
|
|
910
|
+
break;
|
|
911
|
+
case '1':
|
|
912
|
+
x |= mask;
|
|
913
|
+
break;
|
|
914
|
+
case '2':
|
|
915
|
+
y |= mask;
|
|
916
|
+
break;
|
|
917
|
+
case '3':
|
|
918
|
+
x |= mask;
|
|
919
|
+
y |= mask;
|
|
920
|
+
break;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return [x, y, z];
|
|
924
|
+
}
|
|
925
|
+
function bboxToTile(bboxCoords) {
|
|
926
|
+
var min = pointToTile(bboxCoords[0], bboxCoords[1], 32);
|
|
927
|
+
var max = pointToTile(bboxCoords[2], bboxCoords[3], 32);
|
|
928
|
+
var bbox = [min[0], min[1], max[0], max[1]];
|
|
929
|
+
var z = getBboxZoom(bbox);
|
|
930
|
+
if (z === 0) return [0, 0, 0];
|
|
931
|
+
var x = bbox[0] >>> 32 - z;
|
|
932
|
+
var y = bbox[1] >>> 32 - z;
|
|
933
|
+
return [x, y, z];
|
|
934
|
+
}
|
|
935
|
+
function getBboxZoom(bbox) {
|
|
936
|
+
var MAX_ZOOM = 28;
|
|
937
|
+
for (var z = 0; z < MAX_ZOOM; z++) {
|
|
938
|
+
var mask = 1 << 32 - (z + 1);
|
|
939
|
+
if ((bbox[0] & mask) != (bbox[2] & mask) || (bbox[1] & mask) != (bbox[3] & mask)) {
|
|
940
|
+
return z;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return MAX_ZOOM;
|
|
944
|
+
}
|
|
945
|
+
function pointToTileFraction(lon, lat, z) {
|
|
946
|
+
var sin = Math.sin(lat * d2r),
|
|
947
|
+
z2 = Math.pow(2, z),
|
|
948
|
+
x = z2 * (lon / 360 + 0.5),
|
|
949
|
+
y = z2 * (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI);
|
|
950
|
+
return [x, y, z];
|
|
951
|
+
}
|
|
952
|
+
var tilebelt = {
|
|
953
|
+
tileToGeoJSON: tileToGeoJSON,
|
|
954
|
+
tileToBBOX: tileToBBOX,
|
|
955
|
+
getChildren: getChildren,
|
|
956
|
+
getParent: getParent,
|
|
957
|
+
getSiblings: getSiblings,
|
|
958
|
+
hasTile: hasTile,
|
|
959
|
+
hasSiblings: hasSiblings,
|
|
960
|
+
tilesEqual: tilesEqual,
|
|
961
|
+
tileToQuadkey: tileToQuadkey,
|
|
962
|
+
quadkeyToTile: quadkeyToTile,
|
|
963
|
+
pointToTile: pointToTile,
|
|
964
|
+
bboxToTile: bboxToTile,
|
|
965
|
+
pointToTileFraction: pointToTileFraction
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Given a geometry, create cells and return them in their raw form,
|
|
970
|
+
* as an array of cell identifiers.
|
|
971
|
+
*
|
|
972
|
+
* @alias tiles
|
|
973
|
+
* @param {Object} geom GeoJSON geometry
|
|
974
|
+
* @param {Object} limits an object with min_zoom and max_zoom properties
|
|
975
|
+
* specifying the minimum and maximum level to be tiled.
|
|
976
|
+
* @returns {Array<Array<number>>} An array of tiles given as [x, y, z] arrays
|
|
977
|
+
*/
|
|
978
|
+
var tiles = getTiles;
|
|
979
|
+
function getTiles(geom, limits) {
|
|
980
|
+
var i,
|
|
981
|
+
tile,
|
|
982
|
+
coords = geom.coordinates,
|
|
983
|
+
maxZoom = limits.max_zoom,
|
|
984
|
+
tileHash = {},
|
|
985
|
+
tiles = [];
|
|
986
|
+
if (geom.type === 'Point') {
|
|
987
|
+
return [tilebelt.pointToTile(coords[0], coords[1], maxZoom)];
|
|
988
|
+
} else if (geom.type === 'MultiPoint') {
|
|
989
|
+
for (i = 0; i < coords.length; i++) {
|
|
990
|
+
tile = tilebelt.pointToTile(coords[i][0], coords[i][1], maxZoom);
|
|
991
|
+
tileHash[toID(tile[0], tile[1], tile[2])] = true;
|
|
992
|
+
}
|
|
993
|
+
} else if (geom.type === 'LineString') {
|
|
994
|
+
lineCover(tileHash, coords, maxZoom);
|
|
995
|
+
} else if (geom.type === 'MultiLineString') {
|
|
996
|
+
for (i = 0; i < coords.length; i++) {
|
|
997
|
+
lineCover(tileHash, coords[i], maxZoom);
|
|
998
|
+
}
|
|
999
|
+
} else if (geom.type === 'Polygon') {
|
|
1000
|
+
polygonCover(tileHash, tiles, coords, maxZoom);
|
|
1001
|
+
} else if (geom.type === 'MultiPolygon') {
|
|
1002
|
+
for (i = 0; i < coords.length; i++) {
|
|
1003
|
+
polygonCover(tileHash, tiles, coords[i], maxZoom);
|
|
1004
|
+
}
|
|
1005
|
+
} else {
|
|
1006
|
+
throw new Error('Geometry type not implemented');
|
|
1007
|
+
}
|
|
1008
|
+
if (limits.min_zoom !== maxZoom) {
|
|
1009
|
+
// sync tile hash and tile array so that both contain the same tiles
|
|
1010
|
+
var len = tiles.length;
|
|
1011
|
+
appendHashTiles(tileHash, tiles);
|
|
1012
|
+
for (i = 0; i < len; i++) {
|
|
1013
|
+
var t = tiles[i];
|
|
1014
|
+
tileHash[toID(t[0], t[1], t[2])] = true;
|
|
1015
|
+
}
|
|
1016
|
+
return mergeTiles(tileHash, tiles, limits);
|
|
1017
|
+
}
|
|
1018
|
+
appendHashTiles(tileHash, tiles);
|
|
1019
|
+
return tiles;
|
|
1020
|
+
}
|
|
1021
|
+
function mergeTiles(tileHash, tiles, limits) {
|
|
1022
|
+
var mergedTiles = [];
|
|
1023
|
+
for (var z = limits.max_zoom; z > limits.min_zoom; z--) {
|
|
1024
|
+
var parentTileHash = {};
|
|
1025
|
+
var parentTiles = [];
|
|
1026
|
+
for (var i = 0; i < tiles.length; i++) {
|
|
1027
|
+
var t = tiles[i];
|
|
1028
|
+
if (t[0] % 2 === 0 && t[1] % 2 === 0) {
|
|
1029
|
+
var id2 = toID(t[0] + 1, t[1], z),
|
|
1030
|
+
id3 = toID(t[0], t[1] + 1, z),
|
|
1031
|
+
id4 = toID(t[0] + 1, t[1] + 1, z);
|
|
1032
|
+
if (tileHash[id2] && tileHash[id3] && tileHash[id4]) {
|
|
1033
|
+
tileHash[toID(t[0], t[1], t[2])] = false;
|
|
1034
|
+
tileHash[id2] = false;
|
|
1035
|
+
tileHash[id3] = false;
|
|
1036
|
+
tileHash[id4] = false;
|
|
1037
|
+
var parentTile = [t[0] / 2, t[1] / 2, z - 1];
|
|
1038
|
+
if (z - 1 === limits.min_zoom) mergedTiles.push(parentTile);else {
|
|
1039
|
+
parentTileHash[toID(t[0] / 2, t[1] / 2, z - 1)] = true;
|
|
1040
|
+
parentTiles.push(parentTile);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
for (i = 0; i < tiles.length; i++) {
|
|
1046
|
+
t = tiles[i];
|
|
1047
|
+
if (tileHash[toID(t[0], t[1], t[2])]) mergedTiles.push(t);
|
|
1048
|
+
}
|
|
1049
|
+
tileHash = parentTileHash;
|
|
1050
|
+
tiles = parentTiles;
|
|
1051
|
+
}
|
|
1052
|
+
return mergedTiles;
|
|
1053
|
+
}
|
|
1054
|
+
function polygonCover(tileHash, tileArray, geom, zoom) {
|
|
1055
|
+
var intersections = [];
|
|
1056
|
+
for (var i = 0; i < geom.length; i++) {
|
|
1057
|
+
var ring = [];
|
|
1058
|
+
lineCover(tileHash, geom[i], zoom, ring);
|
|
1059
|
+
for (var j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
|
|
1060
|
+
var m = (j + 1) % len;
|
|
1061
|
+
var y = ring[j][1];
|
|
1062
|
+
|
|
1063
|
+
// add interesction if it's not local extremum or duplicate
|
|
1064
|
+
if ((y > ring[k][1] || y > ring[m][1]) && (
|
|
1065
|
+
// not local minimum
|
|
1066
|
+
y < ring[k][1] || y < ring[m][1]) &&
|
|
1067
|
+
// not local maximum
|
|
1068
|
+
y !== ring[m][1]) intersections.push(ring[j]);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
intersections.sort(compareTiles); // sort by y, then x
|
|
1072
|
+
|
|
1073
|
+
for (i = 0; i < intersections.length; i += 2) {
|
|
1074
|
+
// fill tiles between pairs of intersections
|
|
1075
|
+
y = intersections[i][1];
|
|
1076
|
+
for (var x = intersections[i][0] + 1; x < intersections[i + 1][0]; x++) {
|
|
1077
|
+
var id = toID(x, y, zoom);
|
|
1078
|
+
if (!tileHash[id]) {
|
|
1079
|
+
tileArray.push([x, y, zoom]);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function compareTiles(a, b) {
|
|
1085
|
+
return a[1] - b[1] || a[0] - b[0];
|
|
1086
|
+
}
|
|
1087
|
+
function lineCover(tileHash, coords, maxZoom, ring) {
|
|
1088
|
+
var prevX, prevY;
|
|
1089
|
+
for (var i = 0; i < coords.length - 1; i++) {
|
|
1090
|
+
var start = tilebelt.pointToTileFraction(coords[i][0], coords[i][1], maxZoom),
|
|
1091
|
+
stop = tilebelt.pointToTileFraction(coords[i + 1][0], coords[i + 1][1], maxZoom),
|
|
1092
|
+
x0 = start[0],
|
|
1093
|
+
y0 = start[1],
|
|
1094
|
+
x1 = stop[0],
|
|
1095
|
+
y1 = stop[1],
|
|
1096
|
+
dx = x1 - x0,
|
|
1097
|
+
dy = y1 - y0;
|
|
1098
|
+
if (dy === 0 && dx === 0) continue;
|
|
1099
|
+
var sx = dx > 0 ? 1 : -1,
|
|
1100
|
+
sy = dy > 0 ? 1 : -1,
|
|
1101
|
+
x = Math.floor(x0),
|
|
1102
|
+
y = Math.floor(y0),
|
|
1103
|
+
tMaxX = dx === 0 ? Infinity : Math.abs(((dx > 0 ? 1 : 0) + x - x0) / dx),
|
|
1104
|
+
tMaxY = dy === 0 ? Infinity : Math.abs(((dy > 0 ? 1 : 0) + y - y0) / dy),
|
|
1105
|
+
tdx = Math.abs(sx / dx),
|
|
1106
|
+
tdy = Math.abs(sy / dy);
|
|
1107
|
+
if (x !== prevX || y !== prevY) {
|
|
1108
|
+
tileHash[toID(x, y, maxZoom)] = true;
|
|
1109
|
+
if (ring && y !== prevY) ring.push([x, y]);
|
|
1110
|
+
prevX = x;
|
|
1111
|
+
prevY = y;
|
|
1112
|
+
}
|
|
1113
|
+
while (tMaxX < 1 || tMaxY < 1) {
|
|
1114
|
+
if (tMaxX < tMaxY) {
|
|
1115
|
+
tMaxX += tdx;
|
|
1116
|
+
x += sx;
|
|
1117
|
+
} else {
|
|
1118
|
+
tMaxY += tdy;
|
|
1119
|
+
y += sy;
|
|
1120
|
+
}
|
|
1121
|
+
tileHash[toID(x, y, maxZoom)] = true;
|
|
1122
|
+
if (ring && y !== prevY) ring.push([x, y]);
|
|
1123
|
+
prevX = x;
|
|
1124
|
+
prevY = y;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
if (ring && y === ring[0][1]) ring.pop();
|
|
1128
|
+
}
|
|
1129
|
+
function appendHashTiles(hash, tiles) {
|
|
1130
|
+
var keys = Object.keys(hash);
|
|
1131
|
+
for (var i = 0; i < keys.length; i++) {
|
|
1132
|
+
tiles.push(fromID(+keys[i]));
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
function toID(x, y, z) {
|
|
1136
|
+
var dim = 2 * (1 << z);
|
|
1137
|
+
return (dim * y + x) * 32 + z;
|
|
1138
|
+
}
|
|
1139
|
+
function fromID(id) {
|
|
1140
|
+
var z = id % 32,
|
|
1141
|
+
dim = 2 * (1 << z),
|
|
1142
|
+
xy = (id - z) / 32,
|
|
1143
|
+
x = xy % dim,
|
|
1144
|
+
y = (xy - x) / dim % dim;
|
|
1145
|
+
return [x, y, z];
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
const B = [0x5555555555555555n, 0x3333333333333333n, 0x0f0f0f0f0f0f0f0fn, 0x00ff00ff00ff00ffn, 0x0000ffff0000ffffn, 0x00000000ffffffffn];
|
|
1149
|
+
const S = [0n, 1n, 2n, 4n, 8n, 16n];
|
|
1150
|
+
function tileToCell(tile) {
|
|
1151
|
+
if (tile.z < 0 || tile.z > 26) {
|
|
1152
|
+
throw new Error('Wrong zoom');
|
|
1153
|
+
}
|
|
1154
|
+
const z = BigInt(tile.z);
|
|
1155
|
+
let x = BigInt(tile.x) << 32n - z;
|
|
1156
|
+
let y = BigInt(tile.y) << 32n - z;
|
|
1157
|
+
for (let i = 0; i < 5; i++) {
|
|
1158
|
+
const s = S[5 - i];
|
|
1159
|
+
const b = B[4 - i];
|
|
1160
|
+
x = (x | x << s) & b;
|
|
1161
|
+
y = (y | y << s) & b;
|
|
1162
|
+
}
|
|
1163
|
+
const quadbin = 0x4000000000000000n | 1n << 59n |
|
|
1164
|
+
// | (mode << 59) | (mode_dep << 57)
|
|
1165
|
+
z << 52n | (x | y << 1n) >> 12n | 0xfffffffffffffn >> z * 2n;
|
|
1166
|
+
return quadbin;
|
|
1167
|
+
}
|
|
1168
|
+
function getResolution$1(quadbin) {
|
|
1169
|
+
return quadbin >> 52n & 0x1fn;
|
|
1170
|
+
}
|
|
1171
|
+
function geometryToCells(geometry, resolution) {
|
|
1172
|
+
const zoom = Number(resolution);
|
|
1173
|
+
return tiles(geometry, {
|
|
1174
|
+
min_zoom: zoom,
|
|
1175
|
+
max_zoom: zoom
|
|
1176
|
+
}).map(([x, y, z]) => tileToCell({
|
|
1177
|
+
x,
|
|
1178
|
+
y,
|
|
1179
|
+
z
|
|
1180
|
+
}));
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
function tileFeaturesSpatialIndex({
|
|
1184
|
+
tiles,
|
|
1185
|
+
spatialFilter,
|
|
1186
|
+
spatialDataColumn,
|
|
1187
|
+
spatialDataType
|
|
1188
|
+
}) {
|
|
1189
|
+
const map = new Map();
|
|
1190
|
+
const spatialIndex = getSpatialIndex(spatialDataType);
|
|
1191
|
+
const resolution = getResolution(tiles, spatialIndex);
|
|
1192
|
+
const spatialIndexIDName = spatialDataColumn ? spatialDataColumn : spatialIndex;
|
|
1193
|
+
if (!resolution) {
|
|
1194
|
+
return [];
|
|
1195
|
+
}
|
|
1196
|
+
const cells = getCellsCoverGeometry(spatialFilter, spatialIndex, resolution);
|
|
1197
|
+
if (!(cells != null && cells.length)) {
|
|
1198
|
+
return [];
|
|
1199
|
+
}
|
|
1200
|
+
// We transform cells to Set to improve the performace
|
|
1201
|
+
const cellsSet = new Set(cells);
|
|
1202
|
+
for (const tile of tiles) {
|
|
1203
|
+
if (tile.isVisible === false || !tile.data) {
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
tile.data.forEach(d => {
|
|
1207
|
+
if (cellsSet.has(d.id)) {
|
|
1208
|
+
map.set(d.id, _extends({}, d.properties, {
|
|
1209
|
+
[spatialIndexIDName]: d.id
|
|
1210
|
+
}));
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
return Array.from(map.values());
|
|
1215
|
+
}
|
|
1216
|
+
function getResolution(tiles, spatialIndex) {
|
|
1217
|
+
var _tiles$find;
|
|
1218
|
+
const data = (_tiles$find = tiles.find(tile => {
|
|
1219
|
+
var _tile$data;
|
|
1220
|
+
return (_tile$data = tile.data) == null ? void 0 : _tile$data.length;
|
|
1221
|
+
})) == null ? void 0 : _tiles$find.data;
|
|
1222
|
+
if (!data) {
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
if (spatialIndex === SpatialIndex.QUADBIN) {
|
|
1226
|
+
return Number(getResolution$1(data[0].id));
|
|
1227
|
+
}
|
|
1228
|
+
if (spatialIndex === SpatialIndex.H3) {
|
|
1229
|
+
return getResolution$2(data[0].id);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const bboxWest = [-180, -90, 0, 90];
|
|
1233
|
+
const bboxEast = [0, -90, 180, 90];
|
|
1234
|
+
function getCellsCoverGeometry(geometry, spatialIndex, resolution) {
|
|
1235
|
+
if (spatialIndex === SpatialIndex.QUADBIN) {
|
|
1236
|
+
// @ts-expect-error TODO: Probably ought to be stricter about number vs. bigint types in this file.
|
|
1237
|
+
return geometryToCells(geometry, resolution);
|
|
1238
|
+
}
|
|
1239
|
+
if (spatialIndex === SpatialIndex.H3) {
|
|
1240
|
+
// The current H3 polyfill algorithm can't deal with polygon segments of greater than 180 degrees longitude
|
|
1241
|
+
// so we clip the geometry to be sure that none of them is greater than 180 degrees
|
|
1242
|
+
// https://github.com/uber/h3-js/issues/24#issuecomment-431893796
|
|
1243
|
+
return polygonToCells(bboxClip(geometry, bboxWest).geometry.coordinates, resolution, true).concat(polygonToCells(bboxClip(geometry, bboxEast).geometry.coordinates, resolution, true));
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function getSpatialIndex(spatialDataType) {
|
|
1247
|
+
switch (spatialDataType) {
|
|
1248
|
+
case 'h3':
|
|
1249
|
+
return SpatialIndex.H3;
|
|
1250
|
+
case 'quadbin':
|
|
1251
|
+
return SpatialIndex.QUADBIN;
|
|
1252
|
+
default:
|
|
1253
|
+
throw new Error('Unexpected spatial data type');
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Current version of @carto/api-client.
|
|
1259
|
+
* @internal
|
|
1260
|
+
*/
|
|
1261
|
+
/** @internal */
|
|
1262
|
+
const V3_MINOR_VERSION = '3.4';
|
|
1263
|
+
/** @privateRemarks Source: @carto/constants, @deck.gl/carto */
|
|
1264
|
+
const DEFAULT_GEO_COLUMN = 'geom';
|
|
1265
|
+
/**
|
|
1266
|
+
* Fastly default limit is 8192; leave some padding.
|
|
1267
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
1268
|
+
*/
|
|
1269
|
+
const DEFAULT_MAX_LENGTH_URL = 7000;
|
|
1270
|
+
/** @privateRemarks Source: @deck.gl/carto */
|
|
1271
|
+
const DEFAULT_TILE_RESOLUTION = 0.5;
|
|
1272
|
+
/**
|
|
1273
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
1274
|
+
* @internal
|
|
1275
|
+
*/
|
|
1276
|
+
const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
1277
|
+
/**
|
|
1278
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
1279
|
+
* @internal
|
|
1280
|
+
*/
|
|
1281
|
+
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
1282
|
+
|
|
1283
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1284
|
+
function tileFeatures({
|
|
1285
|
+
tiles,
|
|
1286
|
+
spatialFilter,
|
|
1287
|
+
uniqueIdProperty,
|
|
1288
|
+
tileFormat,
|
|
1289
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1290
|
+
spatialDataType,
|
|
1291
|
+
options = {}
|
|
1292
|
+
}) {
|
|
1293
|
+
if (spatialDataType !== 'geo') {
|
|
1294
|
+
return tileFeaturesSpatialIndex({
|
|
1295
|
+
tiles: tiles,
|
|
1296
|
+
spatialFilter,
|
|
1297
|
+
spatialDataColumn,
|
|
1298
|
+
spatialDataType
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
return tileFeaturesGeometries({
|
|
1302
|
+
tiles,
|
|
1303
|
+
tileFormat,
|
|
1304
|
+
spatialFilter,
|
|
1305
|
+
uniqueIdProperty,
|
|
1306
|
+
options
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* deck.gl's DataFilterExtension supports GPU filtering with 1–4 values. We
|
|
1312
|
+
* allocate filters[0] to generic filters and filters[1] to time filters.
|
|
1313
|
+
*
|
|
1314
|
+
* getFilterValue() _must_ return an array of the same size as the filterSize
|
|
1315
|
+
* used to initialize the DataFilterExtension. We document that users must use
|
|
1316
|
+
* filterSize=4 for compatibility with @link {getDataFilterExtensionProps}.
|
|
1317
|
+
*/
|
|
1318
|
+
const DEFAULT_FILTER_SIZE = 4;
|
|
1319
|
+
/**
|
|
1320
|
+
* Creates props for DataFilterExtension, from `@deck.gl/extensions`, given
|
|
1321
|
+
* a set of filters. Requires that DataFilterExtension is initialized with
|
|
1322
|
+
* filterSize=4, where the CARTO filters will occupy the first two slots.
|
|
1323
|
+
*
|
|
1324
|
+
* @example To create a deck.gl layer with GPU data filtering:
|
|
1325
|
+
* ```typescript
|
|
1326
|
+
* import {DataFilterExtension} from '@deck.gl/extensions';
|
|
1327
|
+
* import {VectorTileLayer} from '@deck.gl/layers';
|
|
1328
|
+
* import {getDataFilterExtensionProps} from '@carto/api-client';
|
|
1329
|
+
*
|
|
1330
|
+
* const layer = new VectorTileLayer({
|
|
1331
|
+
* data: data,
|
|
1332
|
+
* extensions: [new DataFilterExtension({filterSize: 4})],
|
|
1333
|
+
* ...getDataFilterExtensionProps(filters),
|
|
1334
|
+
* });
|
|
41
1335
|
* ```
|
|
42
|
-
*
|
|
43
|
-
* @internalRemarks Source: @carto/react-api, @deck.gl/carto
|
|
44
1336
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
1337
|
+
function getDataFilterExtensionProps(filters, filtersLogicalOperator) {
|
|
1338
|
+
const {
|
|
1339
|
+
filtersWithoutTimeType,
|
|
1340
|
+
timeColumn,
|
|
1341
|
+
timeFilter
|
|
1342
|
+
} = getFiltersByType(filters);
|
|
1343
|
+
return {
|
|
1344
|
+
filterRange: getFilterRange(timeFilter, DEFAULT_FILTER_SIZE),
|
|
1345
|
+
updateTriggers: getUpdateTriggers(filtersWithoutTimeType, timeColumn, timeFilter),
|
|
1346
|
+
getFilterValue: getFilterValue(filtersWithoutTimeType, timeColumn, timeFilter, DEFAULT_FILTER_SIZE, filtersLogicalOperator)
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
/** @internal */
|
|
1350
|
+
function getFiltersByType(filters) {
|
|
1351
|
+
const filtersWithoutTimeType = {};
|
|
1352
|
+
let timeColumn = null;
|
|
1353
|
+
let timeFilter = null;
|
|
1354
|
+
for (const [column, columnData] of Object.entries(filters)) {
|
|
1355
|
+
for (const [type, typeData] of Object.entries(columnData)) {
|
|
1356
|
+
if (type === FilterType.TIME) {
|
|
1357
|
+
timeColumn = column;
|
|
1358
|
+
timeFilter = typeData;
|
|
1359
|
+
} else {
|
|
1360
|
+
filtersWithoutTimeType[column] = {
|
|
1361
|
+
[type]: typeData
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return {
|
|
1367
|
+
filtersWithoutTimeType,
|
|
1368
|
+
timeColumn,
|
|
1369
|
+
timeFilter
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
/** @internal */
|
|
1373
|
+
function getFilterRange(timeFilter, filterSize) {
|
|
1374
|
+
const result = Array(filterSize).fill([0, 0]);
|
|
1375
|
+
// According to getFilterValue all filters are resolved as 0 or 1 in the first position of the array
|
|
1376
|
+
// except the time filter value that is resolved with the real value of the feature in the second position of the array
|
|
1377
|
+
result[0] = [1, 1];
|
|
1378
|
+
if (timeFilter) {
|
|
1379
|
+
var _timeFilter$params;
|
|
1380
|
+
const offsetBy = ((_timeFilter$params = timeFilter.params) == null ? void 0 : _timeFilter$params.offsetBy) || 0;
|
|
1381
|
+
result[1] = timeFilter.values[0].map(v => v - offsetBy);
|
|
1382
|
+
}
|
|
1383
|
+
return result;
|
|
1384
|
+
}
|
|
1385
|
+
/** @internal */
|
|
1386
|
+
function getUpdateTriggers(filtersWithoutTimeType, timeColumn, timeFilter) {
|
|
1387
|
+
const result = _extends({}, filtersWithoutTimeType);
|
|
1388
|
+
// We don't want to change the layer UpdateTriggers every time that the time filter changes
|
|
1389
|
+
// because this filter is changed by the time series widget during its animation
|
|
1390
|
+
// so we remove the time filter value from the `updateTriggers`
|
|
1391
|
+
if (timeColumn && timeFilter) {
|
|
1392
|
+
var _timeFilter$params2;
|
|
1393
|
+
result[timeColumn] = _extends({}, result[timeColumn], {
|
|
1394
|
+
offsetBy: (_timeFilter$params2 = timeFilter.params) == null ? void 0 : _timeFilter$params2.offsetBy,
|
|
1395
|
+
[FilterType.TIME]: {} // Allows working with other filters, without an impact on performance.
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
getFilterValue: JSON.stringify(result)
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
/** @internal */
|
|
1403
|
+
function getFilterValue(filtersWithoutTimeType, timeColumn, timeFilter, filterSize, filtersLogicalOperator) {
|
|
1404
|
+
const result = Array(filterSize).fill(0);
|
|
1405
|
+
const featureFilter = _buildFeatureFilter({
|
|
1406
|
+
filters: filtersWithoutTimeType,
|
|
1407
|
+
type: 'number',
|
|
1408
|
+
filtersLogicalOperator
|
|
1409
|
+
});
|
|
1410
|
+
// We evaluate all filters except the time filter using _buildFeatureFilter function.
|
|
1411
|
+
// For the time filter, we return the value of the feature and we will change the getFilterRange result
|
|
1412
|
+
// every time this filter changes
|
|
1413
|
+
return feature => {
|
|
1414
|
+
result[0] = featureFilter(feature);
|
|
1415
|
+
if (timeColumn && timeFilter) {
|
|
1416
|
+
var _timeFilter$params3;
|
|
1417
|
+
const offsetBy = ((_timeFilter$params3 = timeFilter.params) == null ? void 0 : _timeFilter$params3.offsetBy) || 0;
|
|
1418
|
+
const f = feature.properties || feature;
|
|
1419
|
+
result[1] = f[timeColumn] - offsetBy;
|
|
1420
|
+
}
|
|
1421
|
+
return result;
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
64
1424
|
|
|
65
1425
|
const FILTER_TYPES = new Set(Object.values(FilterType));
|
|
66
1426
|
const isFilterType = type => FILTER_TYPES.has(type);
|
|
@@ -88,7 +1448,7 @@ function getApplicableFilters(owner, filters) {
|
|
|
88
1448
|
* Due to each data warehouse having its own behavior with columns,
|
|
89
1449
|
* we need to normalize them and transform every key to lowercase.
|
|
90
1450
|
*
|
|
91
|
-
* @
|
|
1451
|
+
* @privateRemarks Source: @carto/react-widgets
|
|
92
1452
|
* @internal
|
|
93
1453
|
*/
|
|
94
1454
|
function normalizeObjectKeys(el) {
|
|
@@ -102,14 +1462,14 @@ function normalizeObjectKeys(el) {
|
|
|
102
1462
|
return acc;
|
|
103
1463
|
}, {});
|
|
104
1464
|
}
|
|
105
|
-
/** @
|
|
1465
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
106
1466
|
function assert(condition, message) {
|
|
107
1467
|
if (!condition) {
|
|
108
1468
|
throw new Error(message);
|
|
109
1469
|
}
|
|
110
1470
|
}
|
|
111
1471
|
/**
|
|
112
|
-
* @
|
|
1472
|
+
* @privateRemarks Source: @carto/react-core
|
|
113
1473
|
* @internal
|
|
114
1474
|
*/
|
|
115
1475
|
class InvalidColumnError extends Error {
|
|
@@ -253,7 +1613,7 @@ function createPolygonSpatialFilter(spatialFilter) {
|
|
|
253
1613
|
* Check if a viewport is large enough to represent a global coverage.
|
|
254
1614
|
* In this case the spatial filter parameter for widget calculation is removed.
|
|
255
1615
|
*
|
|
256
|
-
* @
|
|
1616
|
+
* @privateRemarks Source: @carto/react-core
|
|
257
1617
|
*/
|
|
258
1618
|
function _isGlobalViewport(viewport) {
|
|
259
1619
|
const [minx, miny, maxx, maxy] = viewport;
|
|
@@ -266,7 +1626,7 @@ function _isGlobalViewport(viewport) {
|
|
|
266
1626
|
*
|
|
267
1627
|
* It results in a Polygon or MultiPolygon strictly inside the validity range.
|
|
268
1628
|
*
|
|
269
|
-
* @
|
|
1629
|
+
* @privateRemarks Source: @carto/react-core
|
|
270
1630
|
*/
|
|
271
1631
|
function _normalizeGeometry(geometry) {
|
|
272
1632
|
const WORLD = [-180, -90, +180, +90];
|
|
@@ -292,17 +1652,17 @@ function _normalizeGeometry(geometry) {
|
|
|
292
1652
|
}
|
|
293
1653
|
return result;
|
|
294
1654
|
}
|
|
295
|
-
/** @
|
|
1655
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
296
1656
|
function _cleanPolygonCoords(cc) {
|
|
297
1657
|
const coords = cc.filter(c => c.length > 0);
|
|
298
1658
|
return coords.length > 0 ? coords : null;
|
|
299
1659
|
}
|
|
300
|
-
/** @
|
|
1660
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
301
1661
|
function _cleanMultiPolygonCoords(ccc) {
|
|
302
1662
|
const coords = ccc.map(_cleanPolygonCoords).filter(cc => cc);
|
|
303
1663
|
return coords.length > 0 ? coords : null;
|
|
304
1664
|
}
|
|
305
|
-
/** @
|
|
1665
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
306
1666
|
function _clean(geometry) {
|
|
307
1667
|
if (!geometry) {
|
|
308
1668
|
return null;
|
|
@@ -317,19 +1677,19 @@ function _clean(geometry) {
|
|
|
317
1677
|
}
|
|
318
1678
|
return null;
|
|
319
1679
|
}
|
|
320
|
-
/** @
|
|
1680
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
321
1681
|
function _txContourCoords(cc, distance) {
|
|
322
1682
|
return cc.map(c => [c[0] + distance, c[1]]);
|
|
323
1683
|
}
|
|
324
|
-
/** @
|
|
1684
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
325
1685
|
function _txPolygonCoords(ccc, distance) {
|
|
326
1686
|
return ccc.map(cc => _txContourCoords(cc, distance));
|
|
327
1687
|
}
|
|
328
|
-
/** @
|
|
1688
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
329
1689
|
function _txMultiPolygonCoords(cccc, distance) {
|
|
330
1690
|
return cccc.map(ccc => _txPolygonCoords(ccc, distance));
|
|
331
1691
|
}
|
|
332
|
-
/** @
|
|
1692
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
333
1693
|
function _tx(geometry, distance) {
|
|
334
1694
|
if (geometry && getType(geometry) === 'Polygon') {
|
|
335
1695
|
const coords = _txPolygonCoords(geometry.coordinates, distance);
|
|
@@ -348,51 +1708,6 @@ function _isMultiPolygon(geometry) {
|
|
|
348
1708
|
return getType(geometry) === 'MultiPolygon';
|
|
349
1709
|
}
|
|
350
1710
|
|
|
351
|
-
function _extends() {
|
|
352
|
-
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
353
|
-
for (var e = 1; e < arguments.length; e++) {
|
|
354
|
-
var t = arguments[e];
|
|
355
|
-
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
356
|
-
}
|
|
357
|
-
return n;
|
|
358
|
-
}, _extends.apply(null, arguments);
|
|
359
|
-
}
|
|
360
|
-
function _objectWithoutPropertiesLoose(r, e) {
|
|
361
|
-
if (null == r) return {};
|
|
362
|
-
var t = {};
|
|
363
|
-
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
364
|
-
if (e.includes(n)) continue;
|
|
365
|
-
t[n] = r[n];
|
|
366
|
-
}
|
|
367
|
-
return t;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Current version of @carto/api-client.
|
|
372
|
-
* @internal
|
|
373
|
-
*/
|
|
374
|
-
/** @internal */
|
|
375
|
-
const V3_MINOR_VERSION = '3.4';
|
|
376
|
-
/** @internalRemarks Source: @carto/constants, @deck.gl/carto */
|
|
377
|
-
const DEFAULT_GEO_COLUMN = 'geom';
|
|
378
|
-
/**
|
|
379
|
-
* Fastly default limit is 8192; leave some padding.
|
|
380
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
381
|
-
*/
|
|
382
|
-
const DEFAULT_MAX_LENGTH_URL = 7000;
|
|
383
|
-
/** @internalRemarks Source: @deck.gl/carto */
|
|
384
|
-
const DEFAULT_TILE_RESOLUTION = 0.5;
|
|
385
|
-
/**
|
|
386
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
387
|
-
* @internal
|
|
388
|
-
*/
|
|
389
|
-
const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
390
|
-
/**
|
|
391
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
392
|
-
* @internal
|
|
393
|
-
*/
|
|
394
|
-
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
395
|
-
|
|
396
1711
|
// deck.gl
|
|
397
1712
|
// SPDX-License-Identifier: MIT
|
|
398
1713
|
// Copyright (c) vis.gl contributors
|
|
@@ -735,9 +2050,99 @@ const boundaryTableSource = async function boundaryTableSource(options) {
|
|
|
735
2050
|
return baseSource('boundary', options, urlParameters);
|
|
736
2051
|
};
|
|
737
2052
|
|
|
2053
|
+
const DEFAULT_TILE_SIZE = 512;
|
|
2054
|
+
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
2055
|
+
function getSpatialFiltersResolution(source, viewState) {
|
|
2056
|
+
var _source$dataResolutio, _source$aggregationRe;
|
|
2057
|
+
const dataResolution = (_source$dataResolutio = source.dataResolution) != null ? _source$dataResolutio : Number.MAX_VALUE;
|
|
2058
|
+
const aggregationResLevel = (_source$aggregationRe = source.aggregationResLevel) != null ? _source$aggregationRe : source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN;
|
|
2059
|
+
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
2060
|
+
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
2061
|
+
if (source.spatialDataType === 'h3') {
|
|
2062
|
+
var _maxH3SpatialFiltersR, _maxH3SpatialFiltersR2;
|
|
2063
|
+
const tileSize = DEFAULT_TILE_SIZE;
|
|
2064
|
+
const maxResolutionForZoom = (_maxH3SpatialFiltersR = (_maxH3SpatialFiltersR2 = maxH3SpatialFiltersResolutions.find(([zoom]) => zoom === currentZoomInt)) == null ? void 0 : _maxH3SpatialFiltersR2[1]) != null ? _maxH3SpatialFiltersR : Math.max(0, currentZoomInt - 3);
|
|
2065
|
+
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
2066
|
+
const hexagonResolution = _getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
2067
|
+
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
2068
|
+
}
|
|
2069
|
+
if (source.spatialDataType === 'quadbin') {
|
|
2070
|
+
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
2071
|
+
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
2072
|
+
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
2073
|
+
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
2074
|
+
}
|
|
2075
|
+
return undefined;
|
|
2076
|
+
}
|
|
2077
|
+
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]];
|
|
2078
|
+
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
2079
|
+
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
2080
|
+
const BIAS = 2;
|
|
2081
|
+
/**
|
|
2082
|
+
* Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
2083
|
+
* a H3 resolution such that the screen space size of the hexagons is
|
|
2084
|
+
* "similar" to the given tileSize on screen. Intended for use with deck.gl.
|
|
2085
|
+
* @internal
|
|
2086
|
+
*/
|
|
2087
|
+
function _getHexagonResolution(viewport, tileSize) {
|
|
2088
|
+
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
2089
|
+
// expressed as an offset to the viewport zoom.
|
|
2090
|
+
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
2091
|
+
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
2092
|
+
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
2093
|
+
// Clip and bias
|
|
2094
|
+
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
/**
|
|
2098
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
2099
|
+
*
|
|
2100
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
2101
|
+
*/
|
|
2102
|
+
class WidgetSource {
|
|
2103
|
+
constructor(props) {
|
|
2104
|
+
this.props = void 0;
|
|
2105
|
+
this.props = _extends({}, WidgetSource.defaultProps, {
|
|
2106
|
+
clientId: getClient()
|
|
2107
|
+
}, props);
|
|
2108
|
+
}
|
|
2109
|
+
_getModelSource(filters, filterOwner) {
|
|
2110
|
+
const props = this.props;
|
|
2111
|
+
return {
|
|
2112
|
+
apiVersion: props.apiVersion,
|
|
2113
|
+
apiBaseUrl: props.apiBaseUrl,
|
|
2114
|
+
clientId: props.clientId,
|
|
2115
|
+
accessToken: props.accessToken,
|
|
2116
|
+
connectionName: props.connectionName,
|
|
2117
|
+
filters: getApplicableFilters(filterOwner, filters || props.filters),
|
|
2118
|
+
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
2119
|
+
spatialDataType: props.spatialDataType,
|
|
2120
|
+
spatialDataColumn: props.spatialDataColumn,
|
|
2121
|
+
dataResolution: props.dataResolution
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
_getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
|
|
2125
|
+
// spatialFiltersResolution applies only to spatial index sources.
|
|
2126
|
+
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
if (!referenceViewState) {
|
|
2130
|
+
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
2131
|
+
}
|
|
2132
|
+
return getSpatialFiltersResolution(source, referenceViewState);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
WidgetSource.defaultProps = {
|
|
2136
|
+
apiVersion: ApiVersion.V3,
|
|
2137
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
2138
|
+
clientId: getClient(),
|
|
2139
|
+
filters: {},
|
|
2140
|
+
filtersLogicalOperator: 'and'
|
|
2141
|
+
};
|
|
2142
|
+
|
|
738
2143
|
/**
|
|
739
2144
|
* Return more descriptive error from API
|
|
740
|
-
* @
|
|
2145
|
+
* @privateRemarks Source: @carto/react-api
|
|
741
2146
|
*/
|
|
742
2147
|
function dealWithApiError({
|
|
743
2148
|
response,
|
|
@@ -756,11 +2161,10 @@ function dealWithApiError({
|
|
|
756
2161
|
case 403:
|
|
757
2162
|
throw new Error('Forbidden access to the requested data');
|
|
758
2163
|
default:
|
|
759
|
-
|
|
760
|
-
throw new Error(msg);
|
|
2164
|
+
throw new Error(data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify((data == null ? void 0 : data.hint) || ((_data$error2 = data.error) == null ? void 0 : _data$error2[0])));
|
|
761
2165
|
}
|
|
762
2166
|
}
|
|
763
|
-
/** @
|
|
2167
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
764
2168
|
async function makeCall({
|
|
765
2169
|
url,
|
|
766
2170
|
accessToken,
|
|
@@ -770,18 +2174,17 @@ async function makeCall({
|
|
|
770
2174
|
let data;
|
|
771
2175
|
const isPost = (opts == null ? void 0 : opts.method) === 'POST';
|
|
772
2176
|
try {
|
|
773
|
-
var _opts$abortController;
|
|
774
2177
|
response = await fetch(url.toString(), _extends({
|
|
775
2178
|
headers: _extends({
|
|
776
2179
|
Authorization: `Bearer ${accessToken}`
|
|
777
2180
|
}, isPost && {
|
|
778
2181
|
'Content-Type': 'application/json'
|
|
779
|
-
})
|
|
2182
|
+
}, opts.headers)
|
|
780
2183
|
}, isPost && {
|
|
781
2184
|
method: opts == null ? void 0 : opts.method,
|
|
782
2185
|
body: opts == null ? void 0 : opts.body
|
|
783
2186
|
}, {
|
|
784
|
-
signal: opts == null
|
|
2187
|
+
signal: opts == null ? void 0 : opts.signal
|
|
785
2188
|
}, opts == null ? void 0 : opts.otherOptions));
|
|
786
2189
|
data = await response.json();
|
|
787
2190
|
} catch (error) {
|
|
@@ -797,7 +2200,7 @@ async function makeCall({
|
|
|
797
2200
|
return data;
|
|
798
2201
|
}
|
|
799
2202
|
|
|
800
|
-
/** @
|
|
2203
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
801
2204
|
const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'pick', 'timeseries', 'range', 'scatterplot', 'table'];
|
|
802
2205
|
const {
|
|
803
2206
|
V3
|
|
@@ -805,7 +2208,7 @@ const {
|
|
|
805
2208
|
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
806
2209
|
/**
|
|
807
2210
|
* Execute a SQL model request.
|
|
808
|
-
* @
|
|
2211
|
+
* @privateRemarks Source: @carto/react-api
|
|
809
2212
|
*/
|
|
810
2213
|
function executeModel(props) {
|
|
811
2214
|
assert(props.source, 'executeModel: missing source');
|
|
@@ -891,115 +2294,36 @@ function objectToURLSearchParams(object) {
|
|
|
891
2294
|
} else if (Array.isArray(object[key])) {
|
|
892
2295
|
params.append(key, JSON.stringify(object[key]));
|
|
893
2296
|
} else if (object[key] === null) {
|
|
894
|
-
params.append(key, 'null');
|
|
895
|
-
} else if (object[key] !== undefined) {
|
|
896
|
-
params.append(key, String(object[key]));
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
return params;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
const DEFAULT_TILE_SIZE = 512;
|
|
903
|
-
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
904
|
-
function getSpatialFiltersResolution(source, viewState) {
|
|
905
|
-
var _source$dataResolutio, _source$aggregationRe;
|
|
906
|
-
const dataResolution = (_source$dataResolutio = source.dataResolution) != null ? _source$dataResolutio : Number.MAX_VALUE;
|
|
907
|
-
const aggregationResLevel = (_source$aggregationRe = source.aggregationResLevel) != null ? _source$aggregationRe : source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN;
|
|
908
|
-
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
909
|
-
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
910
|
-
if (source.spatialDataType === 'h3') {
|
|
911
|
-
var _maxH3SpatialFiltersR, _maxH3SpatialFiltersR2;
|
|
912
|
-
const tileSize = DEFAULT_TILE_SIZE;
|
|
913
|
-
const maxResolutionForZoom = (_maxH3SpatialFiltersR = (_maxH3SpatialFiltersR2 = maxH3SpatialFiltersResolutions.find(([zoom]) => zoom === currentZoomInt)) == null ? void 0 : _maxH3SpatialFiltersR2[1]) != null ? _maxH3SpatialFiltersR : Math.max(0, currentZoomInt - 3);
|
|
914
|
-
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
915
|
-
const hexagonResolution = _getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
916
|
-
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
917
|
-
}
|
|
918
|
-
if (source.spatialDataType === 'quadbin') {
|
|
919
|
-
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
920
|
-
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
921
|
-
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
922
|
-
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
923
|
-
}
|
|
924
|
-
return undefined;
|
|
925
|
-
}
|
|
926
|
-
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]];
|
|
927
|
-
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
928
|
-
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
929
|
-
const BIAS = 2;
|
|
930
|
-
/**
|
|
931
|
-
* Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
932
|
-
* a H3 resolution such that the screen space size of the hexagons is
|
|
933
|
-
* "similar" to the given tileSize on screen. Intended for use with deck.gl.
|
|
934
|
-
* @internal
|
|
935
|
-
*/
|
|
936
|
-
function _getHexagonResolution(viewport, tileSize) {
|
|
937
|
-
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
938
|
-
// expressed as an offset to the viewport zoom.
|
|
939
|
-
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
940
|
-
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
941
|
-
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
942
|
-
// Clip and bias
|
|
943
|
-
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
const _excluded = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
947
|
-
_excluded2 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
948
|
-
_excluded3 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController", "operationExp"],
|
|
949
|
-
_excluded4 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
950
|
-
_excluded5 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
951
|
-
_excluded6 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
952
|
-
_excluded7 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
|
|
953
|
-
_excluded8 = ["filterOwner", "abortController", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"];
|
|
954
|
-
/**
|
|
955
|
-
* Source for Widget API requests on a data source defined by a SQL query.
|
|
956
|
-
*
|
|
957
|
-
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
958
|
-
*/
|
|
959
|
-
class WidgetBaseSource {
|
|
960
|
-
constructor(props) {
|
|
961
|
-
this.props = void 0;
|
|
962
|
-
this.props = _extends({}, WidgetBaseSource.defaultProps, props);
|
|
963
|
-
}
|
|
964
|
-
_getModelSource(owner) {
|
|
965
|
-
const props = this.props;
|
|
966
|
-
return {
|
|
967
|
-
apiVersion: props.apiVersion,
|
|
968
|
-
apiBaseUrl: props.apiBaseUrl,
|
|
969
|
-
clientId: props.clientId,
|
|
970
|
-
accessToken: props.accessToken,
|
|
971
|
-
connectionName: props.connectionName,
|
|
972
|
-
filters: getApplicableFilters(owner, props.filters),
|
|
973
|
-
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
974
|
-
spatialDataType: props.spatialDataType,
|
|
975
|
-
spatialDataColumn: props.spatialDataColumn,
|
|
976
|
-
dataResolution: props.dataResolution
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
_getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
|
|
980
|
-
// spatialFiltersResolution applies only to spatial index sources.
|
|
981
|
-
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
if (!referenceViewState) {
|
|
985
|
-
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
2297
|
+
params.append(key, 'null');
|
|
2298
|
+
} else if (object[key] !== undefined) {
|
|
2299
|
+
params.append(key, String(object[key]));
|
|
986
2300
|
}
|
|
987
|
-
return getSpatialFiltersResolution(source, referenceViewState);
|
|
988
2301
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
2302
|
+
return params;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
const _excluded = ["signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"],
|
|
2306
|
+
_excluded2 = ["abortController", "signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"],
|
|
2307
|
+
_excluded3 = ["abortController", "signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "operationExp"],
|
|
2308
|
+
_excluded4 = ["abortController", "signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"],
|
|
2309
|
+
_excluded5 = ["abortController", "signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"],
|
|
2310
|
+
_excluded6 = ["abortController", "signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"],
|
|
2311
|
+
_excluded7 = ["abortController", "signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"],
|
|
2312
|
+
_excluded8 = ["abortController", "signal", "filters", "filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"];
|
|
2313
|
+
/**
|
|
2314
|
+
* Source for Widget API requests.
|
|
2315
|
+
*
|
|
2316
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
2317
|
+
*/
|
|
2318
|
+
class WidgetRemoteSource extends WidgetSource {
|
|
996
2319
|
async getCategories(options) {
|
|
997
2320
|
const {
|
|
2321
|
+
signal,
|
|
2322
|
+
filters = this.props.filters,
|
|
998
2323
|
filterOwner,
|
|
999
2324
|
spatialFilter,
|
|
1000
2325
|
spatialFiltersMode,
|
|
1001
|
-
spatialIndexReferenceViewState
|
|
1002
|
-
abortController
|
|
2326
|
+
spatialIndexReferenceViewState
|
|
1003
2327
|
} = options,
|
|
1004
2328
|
params = _objectWithoutPropertiesLoose(options, _excluded);
|
|
1005
2329
|
const {
|
|
@@ -1007,7 +2331,7 @@ class WidgetBaseSource {
|
|
|
1007
2331
|
operation,
|
|
1008
2332
|
operationColumn
|
|
1009
2333
|
} = params;
|
|
1010
|
-
const source = this.getModelSource(filterOwner);
|
|
2334
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1011
2335
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1012
2336
|
return executeModel({
|
|
1013
2337
|
model: 'category',
|
|
@@ -1022,28 +2346,20 @@ class WidgetBaseSource {
|
|
|
1022
2346
|
operationColumn: operationColumn || column
|
|
1023
2347
|
},
|
|
1024
2348
|
opts: {
|
|
1025
|
-
|
|
2349
|
+
signal,
|
|
2350
|
+
headers: this.props.headers
|
|
1026
2351
|
}
|
|
1027
2352
|
}).then(res => normalizeObjectKeys(res.rows));
|
|
1028
2353
|
}
|
|
1029
|
-
/****************************************************************************
|
|
1030
|
-
* FEATURES
|
|
1031
|
-
*/
|
|
1032
|
-
/**
|
|
1033
|
-
* Given a list of feature IDs (as found in `_carto_feature_id`) returns all
|
|
1034
|
-
* matching features. In datasets containing features with duplicate geometries,
|
|
1035
|
-
* feature IDs may be duplicated (IDs are a hash of geometry) and so more
|
|
1036
|
-
* results may be returned than IDs in the request.
|
|
1037
|
-
* @internal
|
|
1038
|
-
* @experimental
|
|
1039
|
-
*/
|
|
1040
2354
|
async getFeatures(options) {
|
|
1041
2355
|
const {
|
|
2356
|
+
abortController,
|
|
2357
|
+
signal = abortController == null ? void 0 : abortController.signal,
|
|
2358
|
+
filters = this.props.filters,
|
|
1042
2359
|
filterOwner,
|
|
1043
2360
|
spatialFilter,
|
|
1044
2361
|
spatialFiltersMode,
|
|
1045
|
-
spatialIndexReferenceViewState
|
|
1046
|
-
abortController
|
|
2362
|
+
spatialIndexReferenceViewState
|
|
1047
2363
|
} = options,
|
|
1048
2364
|
params = _objectWithoutPropertiesLoose(options, _excluded2);
|
|
1049
2365
|
const {
|
|
@@ -1054,7 +2370,7 @@ class WidgetBaseSource {
|
|
|
1054
2370
|
limit,
|
|
1055
2371
|
tileResolution
|
|
1056
2372
|
} = params;
|
|
1057
|
-
const source = this.getModelSource(filterOwner);
|
|
2373
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1058
2374
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1059
2375
|
return executeModel({
|
|
1060
2376
|
model: 'pick',
|
|
@@ -1072,7 +2388,8 @@ class WidgetBaseSource {
|
|
|
1072
2388
|
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
1073
2389
|
},
|
|
1074
2390
|
opts: {
|
|
1075
|
-
|
|
2391
|
+
signal,
|
|
2392
|
+
headers: this.props.headers
|
|
1076
2393
|
}
|
|
1077
2394
|
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1078
2395
|
}).then(({
|
|
@@ -1081,20 +2398,15 @@ class WidgetBaseSource {
|
|
|
1081
2398
|
rows
|
|
1082
2399
|
}));
|
|
1083
2400
|
}
|
|
1084
|
-
/****************************************************************************
|
|
1085
|
-
* FORMULA
|
|
1086
|
-
*/
|
|
1087
|
-
/**
|
|
1088
|
-
* Returns a scalar numerical statistic over all matching data. Suitable
|
|
1089
|
-
* for 'headline' or 'scorecard' figures such as counts and sums.
|
|
1090
|
-
*/
|
|
1091
2401
|
async getFormula(options) {
|
|
1092
2402
|
const {
|
|
2403
|
+
abortController,
|
|
2404
|
+
signal = abortController == null ? void 0 : abortController.signal,
|
|
2405
|
+
filters = this.props.filters,
|
|
1093
2406
|
filterOwner,
|
|
1094
2407
|
spatialFilter,
|
|
1095
2408
|
spatialFiltersMode,
|
|
1096
2409
|
spatialIndexReferenceViewState,
|
|
1097
|
-
abortController,
|
|
1098
2410
|
operationExp
|
|
1099
2411
|
} = options,
|
|
1100
2412
|
params = _objectWithoutPropertiesLoose(options, _excluded3);
|
|
@@ -1102,7 +2414,7 @@ class WidgetBaseSource {
|
|
|
1102
2414
|
column,
|
|
1103
2415
|
operation
|
|
1104
2416
|
} = params;
|
|
1105
|
-
const source = this.getModelSource(filterOwner);
|
|
2417
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1106
2418
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1107
2419
|
return executeModel({
|
|
1108
2420
|
model: 'formula',
|
|
@@ -1113,28 +2425,24 @@ class WidgetBaseSource {
|
|
|
1113
2425
|
}),
|
|
1114
2426
|
params: {
|
|
1115
2427
|
column: column != null ? column : '*',
|
|
1116
|
-
operation,
|
|
2428
|
+
operation: operation != null ? operation : 'count',
|
|
1117
2429
|
operationExp
|
|
1118
2430
|
},
|
|
1119
2431
|
opts: {
|
|
1120
|
-
|
|
2432
|
+
signal,
|
|
2433
|
+
headers: this.props.headers
|
|
1121
2434
|
}
|
|
1122
2435
|
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
1123
2436
|
}
|
|
1124
|
-
/****************************************************************************
|
|
1125
|
-
* HISTOGRAM
|
|
1126
|
-
*/
|
|
1127
|
-
/**
|
|
1128
|
-
* Returns a list of labeled datapoints for 'bins' of data defined as ticks
|
|
1129
|
-
* over a numerical range. Suitable for histogram charts.
|
|
1130
|
-
*/
|
|
1131
2437
|
async getHistogram(options) {
|
|
1132
2438
|
const {
|
|
2439
|
+
abortController,
|
|
2440
|
+
signal = abortController == null ? void 0 : abortController.signal,
|
|
2441
|
+
filters = this.props.filters,
|
|
1133
2442
|
filterOwner,
|
|
1134
2443
|
spatialFilter,
|
|
1135
2444
|
spatialFiltersMode,
|
|
1136
|
-
spatialIndexReferenceViewState
|
|
1137
|
-
abortController
|
|
2445
|
+
spatialIndexReferenceViewState
|
|
1138
2446
|
} = options,
|
|
1139
2447
|
params = _objectWithoutPropertiesLoose(options, _excluded4);
|
|
1140
2448
|
const {
|
|
@@ -1142,7 +2450,7 @@ class WidgetBaseSource {
|
|
|
1142
2450
|
operation,
|
|
1143
2451
|
ticks
|
|
1144
2452
|
} = params;
|
|
1145
|
-
const source = this.getModelSource(filterOwner);
|
|
2453
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1146
2454
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1147
2455
|
const data = await executeModel({
|
|
1148
2456
|
model: 'histogram',
|
|
@@ -1157,7 +2465,8 @@ class WidgetBaseSource {
|
|
|
1157
2465
|
ticks
|
|
1158
2466
|
},
|
|
1159
2467
|
opts: {
|
|
1160
|
-
|
|
2468
|
+
signal,
|
|
2469
|
+
headers: this.props.headers
|
|
1161
2470
|
}
|
|
1162
2471
|
}).then(res => normalizeObjectKeys(res.rows));
|
|
1163
2472
|
if (data.length) {
|
|
@@ -1172,27 +2481,21 @@ class WidgetBaseSource {
|
|
|
1172
2481
|
}
|
|
1173
2482
|
return [];
|
|
1174
2483
|
}
|
|
1175
|
-
/****************************************************************************
|
|
1176
|
-
* RANGE
|
|
1177
|
-
*/
|
|
1178
|
-
/**
|
|
1179
|
-
* Returns a range (min and max) for a numerical column of matching rows.
|
|
1180
|
-
* Suitable for displaying certain 'headline' or 'scorecard' statistics,
|
|
1181
|
-
* or rendering a range slider UI for filtering.
|
|
1182
|
-
*/
|
|
1183
2484
|
async getRange(options) {
|
|
1184
2485
|
const {
|
|
2486
|
+
abortController,
|
|
2487
|
+
signal = abortController == null ? void 0 : abortController.signal,
|
|
2488
|
+
filters = this.props.filters,
|
|
1185
2489
|
filterOwner,
|
|
1186
2490
|
spatialFilter,
|
|
1187
2491
|
spatialFiltersMode,
|
|
1188
|
-
spatialIndexReferenceViewState
|
|
1189
|
-
abortController
|
|
2492
|
+
spatialIndexReferenceViewState
|
|
1190
2493
|
} = options,
|
|
1191
2494
|
params = _objectWithoutPropertiesLoose(options, _excluded5);
|
|
1192
2495
|
const {
|
|
1193
2496
|
column
|
|
1194
2497
|
} = params;
|
|
1195
|
-
const source = this.getModelSource(filterOwner);
|
|
2498
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1196
2499
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1197
2500
|
return executeModel({
|
|
1198
2501
|
model: 'range',
|
|
@@ -1205,24 +2508,20 @@ class WidgetBaseSource {
|
|
|
1205
2508
|
column
|
|
1206
2509
|
},
|
|
1207
2510
|
opts: {
|
|
1208
|
-
|
|
2511
|
+
signal,
|
|
2512
|
+
headers: this.props.headers
|
|
1209
2513
|
}
|
|
1210
2514
|
}).then(res => normalizeObjectKeys(res.rows[0]));
|
|
1211
2515
|
}
|
|
1212
|
-
/****************************************************************************
|
|
1213
|
-
* SCATTER
|
|
1214
|
-
*/
|
|
1215
|
-
/**
|
|
1216
|
-
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
1217
|
-
* values. Suitable for rendering scatter plots.
|
|
1218
|
-
*/
|
|
1219
2516
|
async getScatter(options) {
|
|
1220
2517
|
const {
|
|
2518
|
+
abortController,
|
|
2519
|
+
signal = abortController == null ? void 0 : abortController.signal,
|
|
2520
|
+
filters = this.props.filters,
|
|
1221
2521
|
filterOwner,
|
|
1222
2522
|
spatialFilter,
|
|
1223
2523
|
spatialFiltersMode,
|
|
1224
|
-
spatialIndexReferenceViewState
|
|
1225
|
-
abortController
|
|
2524
|
+
spatialIndexReferenceViewState
|
|
1226
2525
|
} = options,
|
|
1227
2526
|
params = _objectWithoutPropertiesLoose(options, _excluded6);
|
|
1228
2527
|
const {
|
|
@@ -1231,7 +2530,7 @@ class WidgetBaseSource {
|
|
|
1231
2530
|
yAxisColumn,
|
|
1232
2531
|
yAxisJoinOperation
|
|
1233
2532
|
} = params;
|
|
1234
|
-
const source = this.getModelSource(filterOwner);
|
|
2533
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1235
2534
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1236
2535
|
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
1237
2536
|
const HARD_LIMIT = 500;
|
|
@@ -1250,27 +2549,23 @@ class WidgetBaseSource {
|
|
|
1250
2549
|
limit: HARD_LIMIT
|
|
1251
2550
|
},
|
|
1252
2551
|
opts: {
|
|
1253
|
-
|
|
2552
|
+
signal,
|
|
2553
|
+
headers: this.props.headers
|
|
1254
2554
|
}
|
|
1255
2555
|
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(({
|
|
1256
2556
|
x,
|
|
1257
2557
|
y
|
|
1258
2558
|
}) => [x, y]));
|
|
1259
2559
|
}
|
|
1260
|
-
/****************************************************************************
|
|
1261
|
-
* TABLE
|
|
1262
|
-
*/
|
|
1263
|
-
/**
|
|
1264
|
-
* Returns a list of arbitrary data rows, with support for pagination and
|
|
1265
|
-
* sorting. Suitable for displaying tables and lists.
|
|
1266
|
-
*/
|
|
1267
2560
|
async getTable(options) {
|
|
1268
2561
|
const {
|
|
2562
|
+
abortController,
|
|
2563
|
+
signal = abortController == null ? void 0 : abortController.signal,
|
|
2564
|
+
filters = this.props.filters,
|
|
1269
2565
|
filterOwner,
|
|
1270
2566
|
spatialFilter,
|
|
1271
2567
|
spatialFiltersMode,
|
|
1272
|
-
spatialIndexReferenceViewState
|
|
1273
|
-
abortController
|
|
2568
|
+
spatialIndexReferenceViewState
|
|
1274
2569
|
} = options,
|
|
1275
2570
|
params = _objectWithoutPropertiesLoose(options, _excluded7);
|
|
1276
2571
|
const {
|
|
@@ -1280,7 +2575,7 @@ class WidgetBaseSource {
|
|
|
1280
2575
|
offset = 0,
|
|
1281
2576
|
limit = 10
|
|
1282
2577
|
} = params;
|
|
1283
|
-
const source = this.getModelSource(filterOwner);
|
|
2578
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1284
2579
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1285
2580
|
return executeModel({
|
|
1286
2581
|
model: 'table',
|
|
@@ -1297,7 +2592,8 @@ class WidgetBaseSource {
|
|
|
1297
2592
|
offset
|
|
1298
2593
|
},
|
|
1299
2594
|
opts: {
|
|
1300
|
-
|
|
2595
|
+
signal,
|
|
2596
|
+
headers: this.props.headers
|
|
1301
2597
|
}
|
|
1302
2598
|
}).then(res => {
|
|
1303
2599
|
var _res$rows, _res$metadata$total, _res$metadata, _res$METADATA;
|
|
@@ -1308,17 +2604,12 @@ class WidgetBaseSource {
|
|
|
1308
2604
|
};
|
|
1309
2605
|
});
|
|
1310
2606
|
}
|
|
1311
|
-
/****************************************************************************
|
|
1312
|
-
* TIME SERIES
|
|
1313
|
-
*/
|
|
1314
|
-
/**
|
|
1315
|
-
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
1316
|
-
* time intervals. Suitable for rendering time series charts.
|
|
1317
|
-
*/
|
|
1318
2607
|
async getTimeSeries(options) {
|
|
1319
2608
|
const {
|
|
1320
|
-
filterOwner,
|
|
1321
2609
|
abortController,
|
|
2610
|
+
signal = abortController == null ? void 0 : abortController.signal,
|
|
2611
|
+
filters = this.props.filters,
|
|
2612
|
+
filterOwner,
|
|
1322
2613
|
spatialFilter,
|
|
1323
2614
|
spatialFiltersMode,
|
|
1324
2615
|
spatialIndexReferenceViewState
|
|
@@ -1335,7 +2626,7 @@ class WidgetBaseSource {
|
|
|
1335
2626
|
splitByCategoryLimit,
|
|
1336
2627
|
splitByCategoryValues
|
|
1337
2628
|
} = params;
|
|
1338
|
-
const source = this.getModelSource(filterOwner);
|
|
2629
|
+
const source = this.getModelSource(filters, filterOwner);
|
|
1339
2630
|
const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1340
2631
|
return executeModel({
|
|
1341
2632
|
model: 'timeseries',
|
|
@@ -1356,7 +2647,8 @@ class WidgetBaseSource {
|
|
|
1356
2647
|
splitByCategoryValues
|
|
1357
2648
|
},
|
|
1358
2649
|
opts: {
|
|
1359
|
-
|
|
2650
|
+
signal,
|
|
2651
|
+
headers: this.props.headers
|
|
1360
2652
|
}
|
|
1361
2653
|
}).then(res => {
|
|
1362
2654
|
var _res$metadata2;
|
|
@@ -1367,13 +2659,6 @@ class WidgetBaseSource {
|
|
|
1367
2659
|
});
|
|
1368
2660
|
}
|
|
1369
2661
|
}
|
|
1370
|
-
WidgetBaseSource.defaultProps = {
|
|
1371
|
-
apiVersion: ApiVersion.V3,
|
|
1372
|
-
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1373
|
-
clientId: getClient(),
|
|
1374
|
-
filters: {},
|
|
1375
|
-
filtersLogicalOperator: 'and'
|
|
1376
|
-
};
|
|
1377
2662
|
|
|
1378
2663
|
/**
|
|
1379
2664
|
* Source for Widget API requests on a data source defined by a SQL query.
|
|
@@ -1397,9 +2682,9 @@ WidgetBaseSource.defaultProps = {
|
|
|
1397
2682
|
* const { widgetSource } = await data;
|
|
1398
2683
|
* ```
|
|
1399
2684
|
*/
|
|
1400
|
-
class WidgetQuerySource extends
|
|
1401
|
-
getModelSource(
|
|
1402
|
-
return _extends({}, super._getModelSource(
|
|
2685
|
+
class WidgetQuerySource extends WidgetRemoteSource {
|
|
2686
|
+
getModelSource(filters, filterOwner) {
|
|
2687
|
+
return _extends({}, super._getModelSource(filters, filterOwner), {
|
|
1403
2688
|
type: 'query',
|
|
1404
2689
|
data: this.props.sqlQuery,
|
|
1405
2690
|
queryParameters: this.props.queryParameters
|
|
@@ -1429,15 +2714,706 @@ class WidgetQuerySource extends WidgetBaseSource {
|
|
|
1429
2714
|
* const { widgetSource } = await data;
|
|
1430
2715
|
* ```
|
|
1431
2716
|
*/
|
|
1432
|
-
class WidgetTableSource extends
|
|
1433
|
-
getModelSource(
|
|
1434
|
-
return _extends({}, super._getModelSource(
|
|
2717
|
+
class WidgetTableSource extends WidgetRemoteSource {
|
|
2718
|
+
getModelSource(filters, filterOwner) {
|
|
2719
|
+
return _extends({}, super._getModelSource(filters, filterOwner), {
|
|
1435
2720
|
type: 'table',
|
|
1436
2721
|
data: this.props.tableName
|
|
1437
2722
|
});
|
|
1438
2723
|
}
|
|
1439
2724
|
}
|
|
1440
2725
|
|
|
2726
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2727
|
+
const aggregationFunctions = {
|
|
2728
|
+
count: values => values.length,
|
|
2729
|
+
min: (...args) => applyAggregationFunction(min, ...args),
|
|
2730
|
+
max: (...args) => applyAggregationFunction(max, ...args),
|
|
2731
|
+
sum: (...args) => applyAggregationFunction(sum, ...args),
|
|
2732
|
+
avg: (...args) => applyAggregationFunction(avg, ...args)
|
|
2733
|
+
};
|
|
2734
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2735
|
+
function aggregate(feature, keys, operation) {
|
|
2736
|
+
if (!(keys != null && keys.length)) {
|
|
2737
|
+
throw new Error('Cannot aggregate a feature without having keys');
|
|
2738
|
+
} else if (keys.length === 1) {
|
|
2739
|
+
const value = feature[keys[0]];
|
|
2740
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
2741
|
+
}
|
|
2742
|
+
const aggregationFn = aggregationFunctions[operation];
|
|
2743
|
+
if (!aggregationFn) {
|
|
2744
|
+
throw new Error(`${operation} isn't a valid aggregation function`);
|
|
2745
|
+
}
|
|
2746
|
+
return aggregationFn(keys.map(column => {
|
|
2747
|
+
const value = feature[column];
|
|
2748
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
2749
|
+
}));
|
|
2750
|
+
}
|
|
2751
|
+
/*
|
|
2752
|
+
* Forced casting to Number (just of non empty strings) allows to work-around
|
|
2753
|
+
* some specific situations, where a big numeric field is transformed into a string when generating the tileset(eg.PG)
|
|
2754
|
+
*/
|
|
2755
|
+
function isPotentiallyValidNumber(value) {
|
|
2756
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
2757
|
+
}
|
|
2758
|
+
const applyAggregationFunction = (aggFn, values, keys, operation) => {
|
|
2759
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2760
|
+
const elements = ((normalizedKeys == null ? void 0 : normalizedKeys.length) || 0) <= 1 ? filterFalsyElements(values, normalizedKeys || []) : values;
|
|
2761
|
+
return aggFn(elements, keys, operation);
|
|
2762
|
+
};
|
|
2763
|
+
function filterFalsyElements(values, keys) {
|
|
2764
|
+
const filterFn = value => value !== null && value !== undefined;
|
|
2765
|
+
if (!(keys != null && keys.length)) {
|
|
2766
|
+
return values.filter(filterFn);
|
|
2767
|
+
}
|
|
2768
|
+
return values.filter(v => filterFn(v[keys[0]]));
|
|
2769
|
+
}
|
|
2770
|
+
// Aggregation functions
|
|
2771
|
+
function avg(values, keys, joinOperation) {
|
|
2772
|
+
return sum(values, keys, joinOperation) / (values.length || 1);
|
|
2773
|
+
}
|
|
2774
|
+
function sum(values, keys, joinOperation) {
|
|
2775
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2776
|
+
if (normalizedKeys) {
|
|
2777
|
+
return values.reduce((a, b) => a + aggregate(b, normalizedKeys, joinOperation), 0);
|
|
2778
|
+
}
|
|
2779
|
+
return values.reduce((a, b) => a + b, 0);
|
|
2780
|
+
}
|
|
2781
|
+
function min(values, keys, joinOperation) {
|
|
2782
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2783
|
+
if (normalizedKeys) {
|
|
2784
|
+
return values.reduce((a, b) => Math.min(a, aggregate(b, normalizedKeys, joinOperation)), Infinity);
|
|
2785
|
+
}
|
|
2786
|
+
return Math.min(...values);
|
|
2787
|
+
}
|
|
2788
|
+
function max(values, keys, joinOperation) {
|
|
2789
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2790
|
+
if (normalizedKeys) {
|
|
2791
|
+
return values.reduce((a, b) => Math.max(a, aggregate(b, normalizedKeys, joinOperation)), -Infinity);
|
|
2792
|
+
}
|
|
2793
|
+
return Math.max(...values);
|
|
2794
|
+
}
|
|
2795
|
+
// Aux
|
|
2796
|
+
// Keys can come as a string (one column) or a strings array (multiple column)
|
|
2797
|
+
// Use always an array to make the code easier
|
|
2798
|
+
function normalizeKeys(keys) {
|
|
2799
|
+
return Array.isArray(keys) ? keys : typeof keys === 'string' ? [keys] : undefined;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
/***
|
|
2803
|
+
Copyright 2013 Teun Duynstee
|
|
2804
|
+
|
|
2805
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
2806
|
+
you may not use this file except in compliance with the License.
|
|
2807
|
+
You may obtain a copy of the License at
|
|
2808
|
+
|
|
2809
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
2810
|
+
|
|
2811
|
+
Unless required by applicable law or agreed to in writing, software
|
|
2812
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
2813
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2814
|
+
See the License for the specific language governing permissions and
|
|
2815
|
+
limitations under the License.
|
|
2816
|
+
*/
|
|
2817
|
+
var thenBy_module = function () {
|
|
2818
|
+
function identity(v) {
|
|
2819
|
+
return v;
|
|
2820
|
+
}
|
|
2821
|
+
function ignoreCase(v) {
|
|
2822
|
+
return typeof v === "string" ? v.toLowerCase() : v;
|
|
2823
|
+
}
|
|
2824
|
+
function makeCompareFunction(f, opt) {
|
|
2825
|
+
opt = typeof opt === "object" ? opt : {
|
|
2826
|
+
direction: opt
|
|
2827
|
+
};
|
|
2828
|
+
if (typeof f != "function") {
|
|
2829
|
+
var prop = f;
|
|
2830
|
+
// make unary function
|
|
2831
|
+
f = function (v1) {
|
|
2832
|
+
return !!v1[prop] ? v1[prop] : "";
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
if (f.length === 1) {
|
|
2836
|
+
// f is a unary function mapping a single item to its sort score
|
|
2837
|
+
var uf = f;
|
|
2838
|
+
var preprocess = opt.ignoreCase ? ignoreCase : identity;
|
|
2839
|
+
var cmp = opt.cmp || function (v1, v2) {
|
|
2840
|
+
return v1 < v2 ? -1 : v1 > v2 ? 1 : 0;
|
|
2841
|
+
};
|
|
2842
|
+
f = function (v1, v2) {
|
|
2843
|
+
return cmp(preprocess(uf(v1)), preprocess(uf(v2)));
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
const descTokens = {
|
|
2847
|
+
"-1": '',
|
|
2848
|
+
desc: ''
|
|
2849
|
+
};
|
|
2850
|
+
if (opt.direction in descTokens) return function (v1, v2) {
|
|
2851
|
+
return -f(v1, v2);
|
|
2852
|
+
};
|
|
2853
|
+
return f;
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
/* adds a secondary compare function to the target function (`this` context)
|
|
2857
|
+
which is applied in case the first one returns 0 (equal)
|
|
2858
|
+
returns a new compare function, which has a `thenBy` method as well */
|
|
2859
|
+
function tb(func, opt) {
|
|
2860
|
+
/* should get value false for the first call. This can be done by calling the
|
|
2861
|
+
exported function, or the firstBy property on it (for es6 module compatibility)
|
|
2862
|
+
*/
|
|
2863
|
+
var x = typeof this == "function" && !this.firstBy ? this : false;
|
|
2864
|
+
var y = makeCompareFunction(func, opt);
|
|
2865
|
+
var f = x ? function (a, b) {
|
|
2866
|
+
return x(a, b) || y(a, b);
|
|
2867
|
+
} : y;
|
|
2868
|
+
f.thenBy = tb;
|
|
2869
|
+
return f;
|
|
2870
|
+
}
|
|
2871
|
+
tb.firstBy = tb;
|
|
2872
|
+
return tb;
|
|
2873
|
+
}();
|
|
2874
|
+
|
|
2875
|
+
/**
|
|
2876
|
+
* Apply sort structure to a collection of features
|
|
2877
|
+
* @param features
|
|
2878
|
+
* @param [sortOptions]
|
|
2879
|
+
* @param [sortOptions.sortBy] - One or more columns to sort by
|
|
2880
|
+
* @param [sortOptions.sortByDirection] - Direction by the columns will be sorted
|
|
2881
|
+
* @param [sortOptions.sortByColumnType] - Column type
|
|
2882
|
+
* @internal
|
|
2883
|
+
* @privateRemarks Source: @carto/react-core
|
|
2884
|
+
*/
|
|
2885
|
+
function applySorting(features, {
|
|
2886
|
+
sortBy,
|
|
2887
|
+
sortByDirection = 'asc',
|
|
2888
|
+
sortByColumnType = 'string'
|
|
2889
|
+
} = {}) {
|
|
2890
|
+
// If sortBy is undefined, pass all features
|
|
2891
|
+
if (sortBy === undefined) {
|
|
2892
|
+
return features;
|
|
2893
|
+
}
|
|
2894
|
+
// sortOptions exists, but are bad formatted
|
|
2895
|
+
const isValidSortBy = Array.isArray(sortBy) && sortBy.length ||
|
|
2896
|
+
// sortBy can be an array of columns
|
|
2897
|
+
typeof sortBy === 'string'; // or just one column
|
|
2898
|
+
if (!isValidSortBy) {
|
|
2899
|
+
throw new Error('Sorting options are bad formatted');
|
|
2900
|
+
}
|
|
2901
|
+
const sortFn = createSortFn({
|
|
2902
|
+
sortBy,
|
|
2903
|
+
sortByDirection,
|
|
2904
|
+
sortByColumnType: sortByColumnType || 'string'
|
|
2905
|
+
});
|
|
2906
|
+
return features.sort(sortFn);
|
|
2907
|
+
}
|
|
2908
|
+
// Aux
|
|
2909
|
+
function createSortFn({
|
|
2910
|
+
sortBy,
|
|
2911
|
+
sortByDirection,
|
|
2912
|
+
sortByColumnType
|
|
2913
|
+
}) {
|
|
2914
|
+
const [firstSortOption, ...othersSortOptions] = normalizeSortByOptions({
|
|
2915
|
+
sortBy,
|
|
2916
|
+
sortByDirection,
|
|
2917
|
+
sortByColumnType
|
|
2918
|
+
});
|
|
2919
|
+
let sortFn = thenBy_module.firstBy(...firstSortOption);
|
|
2920
|
+
for (const sortOptions of othersSortOptions) {
|
|
2921
|
+
sortFn = sortFn.thenBy(...sortOptions);
|
|
2922
|
+
}
|
|
2923
|
+
return sortFn;
|
|
2924
|
+
}
|
|
2925
|
+
function normalizeSortByOptions({
|
|
2926
|
+
sortBy,
|
|
2927
|
+
sortByDirection,
|
|
2928
|
+
sortByColumnType
|
|
2929
|
+
}) {
|
|
2930
|
+
const numberFormat = sortByColumnType === 'number' && {
|
|
2931
|
+
cmp: (a, b) => a - b
|
|
2932
|
+
};
|
|
2933
|
+
if (!Array.isArray(sortBy)) {
|
|
2934
|
+
sortBy = [sortBy];
|
|
2935
|
+
}
|
|
2936
|
+
return sortBy.map(sortByEl => {
|
|
2937
|
+
// sortByEl is 'column'
|
|
2938
|
+
if (typeof sortByEl === 'string') {
|
|
2939
|
+
return [sortByEl, _extends({
|
|
2940
|
+
direction: sortByDirection
|
|
2941
|
+
}, numberFormat)];
|
|
2942
|
+
}
|
|
2943
|
+
if (Array.isArray(sortByEl)) {
|
|
2944
|
+
// sortBy is ['column']
|
|
2945
|
+
if (sortByEl[1] === undefined) {
|
|
2946
|
+
return [sortByEl, _extends({
|
|
2947
|
+
direction: sortByDirection
|
|
2948
|
+
}, numberFormat)];
|
|
2949
|
+
}
|
|
2950
|
+
// sortBy is ['column', { ... }]
|
|
2951
|
+
if (typeof sortByEl[1] === 'object') {
|
|
2952
|
+
const othersSortOptions = numberFormat ? _extends({}, numberFormat, sortByEl[1]) : sortByEl[1];
|
|
2953
|
+
return [sortByEl[0], _extends({
|
|
2954
|
+
direction: sortByDirection
|
|
2955
|
+
}, othersSortOptions)];
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
return sortByEl;
|
|
2959
|
+
});
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2963
|
+
function groupValuesByColumn({
|
|
2964
|
+
data,
|
|
2965
|
+
valuesColumns,
|
|
2966
|
+
joinOperation,
|
|
2967
|
+
keysColumn,
|
|
2968
|
+
operation
|
|
2969
|
+
}) {
|
|
2970
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
2971
|
+
return null;
|
|
2972
|
+
}
|
|
2973
|
+
const groups = data.reduce((accumulator, item) => {
|
|
2974
|
+
const group = item[keysColumn];
|
|
2975
|
+
const values = accumulator.get(group) || [];
|
|
2976
|
+
accumulator.set(group, values);
|
|
2977
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
2978
|
+
const isValid = (operation === 'count' ? true : aggregatedValue !== null) && aggregatedValue !== undefined;
|
|
2979
|
+
if (isValid) {
|
|
2980
|
+
values.push(aggregatedValue);
|
|
2981
|
+
accumulator.set(group, values);
|
|
2982
|
+
}
|
|
2983
|
+
return accumulator;
|
|
2984
|
+
}, new Map()); // We use a map to be able to maintain the type in the key value
|
|
2985
|
+
const targetOperation = aggregationFunctions[operation];
|
|
2986
|
+
if (targetOperation) {
|
|
2987
|
+
return Array.from(groups).map(([name, value]) => ({
|
|
2988
|
+
name,
|
|
2989
|
+
value: targetOperation(value)
|
|
2990
|
+
}));
|
|
2991
|
+
}
|
|
2992
|
+
return [];
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
/**
|
|
2996
|
+
* Returns midnight (local time) on the Monday preceeding a given date, in
|
|
2997
|
+
* milliseconds since the UNIX epoch.
|
|
2998
|
+
*/
|
|
2999
|
+
/**
|
|
3000
|
+
* Returns midnight (UTC) on the Monday preceeding a given date, in
|
|
3001
|
+
* milliseconds since the UNIX epoch.
|
|
3002
|
+
*/
|
|
3003
|
+
function getUTCMonday(date) {
|
|
3004
|
+
const dateCp = new Date(date);
|
|
3005
|
+
const day = dateCp.getUTCDay();
|
|
3006
|
+
const diff = dateCp.getUTCDate() - day + (day ? 1 : -6); // adjust when day is sunday
|
|
3007
|
+
dateCp.setUTCDate(diff);
|
|
3008
|
+
return Date.UTC(dateCp.getUTCFullYear(), dateCp.getUTCMonth(), dateCp.getUTCDate());
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
const GROUP_KEY_FN_MAPPING = {
|
|
3012
|
+
year: date => Date.UTC(date.getUTCFullYear()),
|
|
3013
|
+
month: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth()),
|
|
3014
|
+
week: date => getUTCMonday(date),
|
|
3015
|
+
day: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
|
|
3016
|
+
hour: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours()),
|
|
3017
|
+
minute: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes()),
|
|
3018
|
+
second: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds())
|
|
3019
|
+
};
|
|
3020
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
3021
|
+
function groupValuesByDateColumn({
|
|
3022
|
+
data,
|
|
3023
|
+
valuesColumns,
|
|
3024
|
+
joinOperation,
|
|
3025
|
+
keysColumn,
|
|
3026
|
+
groupType,
|
|
3027
|
+
operation
|
|
3028
|
+
}) {
|
|
3029
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
3030
|
+
return null;
|
|
3031
|
+
}
|
|
3032
|
+
const groupKeyFn = GROUP_KEY_FN_MAPPING[groupType];
|
|
3033
|
+
if (!groupKeyFn) {
|
|
3034
|
+
return null;
|
|
3035
|
+
}
|
|
3036
|
+
const groups = data.reduce((acc, item) => {
|
|
3037
|
+
const value = item[keysColumn];
|
|
3038
|
+
const formattedValue = new Date(value);
|
|
3039
|
+
const groupKey = groupKeyFn(formattedValue);
|
|
3040
|
+
if (!isNaN(groupKey)) {
|
|
3041
|
+
let groupedValues = acc.get(groupKey);
|
|
3042
|
+
if (!groupedValues) {
|
|
3043
|
+
groupedValues = [];
|
|
3044
|
+
acc.set(groupKey, groupedValues);
|
|
3045
|
+
}
|
|
3046
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
3047
|
+
const isValid = aggregatedValue !== null && aggregatedValue !== undefined;
|
|
3048
|
+
if (isValid) {
|
|
3049
|
+
groupedValues.push(aggregatedValue);
|
|
3050
|
+
acc.set(groupKey, groupedValues);
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
return acc;
|
|
3054
|
+
}, new Map());
|
|
3055
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3056
|
+
return [...groups.entries()].map(([name, value]) => ({
|
|
3057
|
+
name,
|
|
3058
|
+
value: targetOperation(value)
|
|
3059
|
+
})).sort((a, b) => a.name - b.name);
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
/**
|
|
3063
|
+
* Histogram computation.
|
|
3064
|
+
* @privateRemarks Source: @carto/react-core
|
|
3065
|
+
*/
|
|
3066
|
+
function histogram({
|
|
3067
|
+
data,
|
|
3068
|
+
valuesColumns,
|
|
3069
|
+
joinOperation,
|
|
3070
|
+
ticks,
|
|
3071
|
+
operation
|
|
3072
|
+
}) {
|
|
3073
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
3074
|
+
return [];
|
|
3075
|
+
}
|
|
3076
|
+
const binsContainer = [Number.MIN_SAFE_INTEGER, ...ticks].map((tick, index, arr) => ({
|
|
3077
|
+
bin: index,
|
|
3078
|
+
start: tick,
|
|
3079
|
+
end: index === arr.length - 1 ? Number.MAX_SAFE_INTEGER : arr[index + 1],
|
|
3080
|
+
values: []
|
|
3081
|
+
}));
|
|
3082
|
+
data.forEach(feature => {
|
|
3083
|
+
const featureValue = aggregate(feature, valuesColumns, joinOperation);
|
|
3084
|
+
const isValid = featureValue !== null && featureValue !== undefined;
|
|
3085
|
+
if (!isValid) {
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
const binContainer = binsContainer.find(bin => bin.start <= featureValue && bin.end > featureValue);
|
|
3089
|
+
if (!binContainer) {
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
binContainer.values.push(featureValue);
|
|
3093
|
+
});
|
|
3094
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3095
|
+
const transformedBins = binsContainer.map(binContainer => binContainer.values);
|
|
3096
|
+
return transformedBins.map(values => values.length ? targetOperation(values) : 0);
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
/**
|
|
3100
|
+
* Filters invalid features and formats data.
|
|
3101
|
+
* @privateRemarks Source: @carto/react-core
|
|
3102
|
+
*/
|
|
3103
|
+
function scatterPlot({
|
|
3104
|
+
data,
|
|
3105
|
+
xAxisColumns,
|
|
3106
|
+
xAxisJoinOperation,
|
|
3107
|
+
yAxisColumns,
|
|
3108
|
+
yAxisJoinOperation
|
|
3109
|
+
}) {
|
|
3110
|
+
return data.reduce((acc, feature) => {
|
|
3111
|
+
const xValue = aggregate(feature, xAxisColumns, xAxisJoinOperation);
|
|
3112
|
+
const xIsValid = xValue !== null && xValue !== undefined;
|
|
3113
|
+
const yValue = aggregate(feature, yAxisColumns, yAxisJoinOperation);
|
|
3114
|
+
const yIsValid = yValue !== null && yValue !== undefined;
|
|
3115
|
+
if (xIsValid && yIsValid) {
|
|
3116
|
+
acc.push([xValue, yValue]);
|
|
3117
|
+
}
|
|
3118
|
+
return acc;
|
|
3119
|
+
}, []);
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
/**
|
|
3123
|
+
* Source for Widget API requests on a data source defined by a tileset.
|
|
3124
|
+
*
|
|
3125
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
3126
|
+
* {@link vectorTilesetSource}, {@link h3TilesetSource}, or {@link quadbinTilesetSource},
|
|
3127
|
+
* which can be shared with map layers. Sources contain a `widgetSource`
|
|
3128
|
+
* property, for use by widget implementations.
|
|
3129
|
+
*
|
|
3130
|
+
* Example:
|
|
3131
|
+
*
|
|
3132
|
+
* ```javascript
|
|
3133
|
+
* import { vectorTilesetSource } from '@carto/api-client';
|
|
3134
|
+
*
|
|
3135
|
+
* const data = vectorTilesetSource({
|
|
3136
|
+
* accessToken: '••••',
|
|
3137
|
+
* connectionName: 'carto_dw',
|
|
3138
|
+
* tableName: 'carto-demo-data.demo_rasters.my_tileset_source'
|
|
3139
|
+
* });
|
|
3140
|
+
*
|
|
3141
|
+
* const { widgetSource } = await data;
|
|
3142
|
+
* ```
|
|
3143
|
+
*/
|
|
3144
|
+
class WidgetTilesetSource extends WidgetSource {
|
|
3145
|
+
constructor(...args) {
|
|
3146
|
+
super(...args);
|
|
3147
|
+
this._tiles = [];
|
|
3148
|
+
this._features = [];
|
|
3149
|
+
this._tileFeatureExtractOptions = {};
|
|
3150
|
+
this._tileFeatureExtractPreviousInputs = {};
|
|
3151
|
+
}
|
|
3152
|
+
getModelSource(filters, filterOwner) {
|
|
3153
|
+
return _extends({}, super._getModelSource(filters, filterOwner), {
|
|
3154
|
+
type: 'tileset',
|
|
3155
|
+
data: this.props.tableName
|
|
3156
|
+
});
|
|
3157
|
+
}
|
|
3158
|
+
/**
|
|
3159
|
+
* Loads features as a list of tiles (typically provided by deck.gl).
|
|
3160
|
+
* After tiles are loaded, {@link extractTileFeatures} must be called
|
|
3161
|
+
* before computing statistics on the tiles.
|
|
3162
|
+
*/
|
|
3163
|
+
loadTiles(tiles) {
|
|
3164
|
+
this._tiles = tiles;
|
|
3165
|
+
this._features.length = 0;
|
|
3166
|
+
}
|
|
3167
|
+
/** Configures options used to extract features from tiles. */
|
|
3168
|
+
setTileFeatureExtractOptions(options) {
|
|
3169
|
+
this._tileFeatureExtractOptions = options;
|
|
3170
|
+
this._features.length = 0;
|
|
3171
|
+
}
|
|
3172
|
+
_extractTileFeatures(spatialFilter) {
|
|
3173
|
+
// When spatial filter has not changed, don't redo extraction. If tiles or
|
|
3174
|
+
// tile extract options change, features will have been cleared already.
|
|
3175
|
+
const prevInputs = this._tileFeatureExtractPreviousInputs;
|
|
3176
|
+
if (this._features.length && prevInputs.spatialFilter && booleanEqual(prevInputs.spatialFilter, spatialFilter)) {
|
|
3177
|
+
return;
|
|
3178
|
+
}
|
|
3179
|
+
this._features = tileFeatures(_extends({
|
|
3180
|
+
tiles: this._tiles,
|
|
3181
|
+
tileFormat: this.props.tileFormat
|
|
3182
|
+
}, this._tileFeatureExtractOptions, {
|
|
3183
|
+
spatialFilter,
|
|
3184
|
+
spatialDataColumn: this.props.spatialDataColumn,
|
|
3185
|
+
spatialDataType: this.props.spatialDataType
|
|
3186
|
+
}));
|
|
3187
|
+
prevInputs.spatialFilter = spatialFilter;
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Loads features as GeoJSON (used for testing).
|
|
3191
|
+
* @experimental
|
|
3192
|
+
* @internal Not for public use. Spatial filters in other method calls will be ignored.
|
|
3193
|
+
*/
|
|
3194
|
+
loadGeoJSON({
|
|
3195
|
+
geojson,
|
|
3196
|
+
spatialFilter
|
|
3197
|
+
}) {
|
|
3198
|
+
this._features = geojsonFeatures(_extends({
|
|
3199
|
+
geojson,
|
|
3200
|
+
spatialFilter
|
|
3201
|
+
}, this._tileFeatureExtractOptions));
|
|
3202
|
+
this._tileFeatureExtractPreviousInputs.spatialFilter = spatialFilter;
|
|
3203
|
+
}
|
|
3204
|
+
async getFeatures() {
|
|
3205
|
+
throw new Error('getFeatures not supported for tilesets');
|
|
3206
|
+
}
|
|
3207
|
+
async getFormula({
|
|
3208
|
+
column = '*',
|
|
3209
|
+
operation = 'count',
|
|
3210
|
+
joinOperation,
|
|
3211
|
+
filters,
|
|
3212
|
+
filterOwner,
|
|
3213
|
+
spatialFilter
|
|
3214
|
+
}) {
|
|
3215
|
+
if (operation === 'custom') {
|
|
3216
|
+
throw new Error('Custom aggregation not supported for tilesets');
|
|
3217
|
+
}
|
|
3218
|
+
// Column is required except when operation is 'count'.
|
|
3219
|
+
if (column && column !== '*' || operation !== 'count') {
|
|
3220
|
+
assertColumn(this._features, column);
|
|
3221
|
+
}
|
|
3222
|
+
const filteredFeatures = this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3223
|
+
if (filteredFeatures.length === 0 && operation !== 'count') {
|
|
3224
|
+
return {
|
|
3225
|
+
value: null
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3229
|
+
return {
|
|
3230
|
+
value: targetOperation(filteredFeatures, column, joinOperation)
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
async getHistogram({
|
|
3234
|
+
operation = 'count',
|
|
3235
|
+
ticks,
|
|
3236
|
+
column,
|
|
3237
|
+
joinOperation,
|
|
3238
|
+
filters,
|
|
3239
|
+
filterOwner,
|
|
3240
|
+
spatialFilter
|
|
3241
|
+
}) {
|
|
3242
|
+
const filteredFeatures = this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3243
|
+
assertColumn(this._features, column);
|
|
3244
|
+
if (!this._features.length) {
|
|
3245
|
+
return [];
|
|
3246
|
+
}
|
|
3247
|
+
return histogram({
|
|
3248
|
+
data: filteredFeatures,
|
|
3249
|
+
valuesColumns: normalizeColumns(column),
|
|
3250
|
+
joinOperation,
|
|
3251
|
+
ticks,
|
|
3252
|
+
operation
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
async getCategories({
|
|
3256
|
+
column,
|
|
3257
|
+
operation = 'count',
|
|
3258
|
+
operationColumn,
|
|
3259
|
+
joinOperation,
|
|
3260
|
+
filters,
|
|
3261
|
+
filterOwner,
|
|
3262
|
+
spatialFilter
|
|
3263
|
+
}) {
|
|
3264
|
+
const filteredFeatures = this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3265
|
+
if (!filteredFeatures.length) {
|
|
3266
|
+
return [];
|
|
3267
|
+
}
|
|
3268
|
+
assertColumn(this._features, column, operationColumn);
|
|
3269
|
+
const groups = groupValuesByColumn({
|
|
3270
|
+
data: filteredFeatures,
|
|
3271
|
+
valuesColumns: normalizeColumns(operationColumn || column),
|
|
3272
|
+
joinOperation,
|
|
3273
|
+
keysColumn: column,
|
|
3274
|
+
operation
|
|
3275
|
+
});
|
|
3276
|
+
return groups || [];
|
|
3277
|
+
}
|
|
3278
|
+
async getScatter({
|
|
3279
|
+
xAxisColumn,
|
|
3280
|
+
yAxisColumn,
|
|
3281
|
+
xAxisJoinOperation,
|
|
3282
|
+
yAxisJoinOperation,
|
|
3283
|
+
filters,
|
|
3284
|
+
filterOwner,
|
|
3285
|
+
spatialFilter
|
|
3286
|
+
}) {
|
|
3287
|
+
const filteredFeatures = this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3288
|
+
if (!filteredFeatures.length) {
|
|
3289
|
+
return [];
|
|
3290
|
+
}
|
|
3291
|
+
assertColumn(this._features, xAxisColumn, yAxisColumn);
|
|
3292
|
+
return scatterPlot({
|
|
3293
|
+
data: filteredFeatures,
|
|
3294
|
+
xAxisColumns: normalizeColumns(xAxisColumn),
|
|
3295
|
+
xAxisJoinOperation,
|
|
3296
|
+
yAxisColumns: normalizeColumns(yAxisColumn),
|
|
3297
|
+
yAxisJoinOperation
|
|
3298
|
+
});
|
|
3299
|
+
}
|
|
3300
|
+
async getTable({
|
|
3301
|
+
columns,
|
|
3302
|
+
searchFilterColumn,
|
|
3303
|
+
searchFilterText,
|
|
3304
|
+
sortBy,
|
|
3305
|
+
sortDirection,
|
|
3306
|
+
sortByColumnType,
|
|
3307
|
+
offset = 0,
|
|
3308
|
+
limit = 10,
|
|
3309
|
+
filters,
|
|
3310
|
+
filterOwner,
|
|
3311
|
+
spatialFilter
|
|
3312
|
+
}) {
|
|
3313
|
+
// Filter.
|
|
3314
|
+
let filteredFeatures = this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3315
|
+
if (!filteredFeatures.length) {
|
|
3316
|
+
return {
|
|
3317
|
+
rows: [],
|
|
3318
|
+
totalCount: 0
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
// Search.
|
|
3322
|
+
if (searchFilterColumn && searchFilterText) {
|
|
3323
|
+
filteredFeatures = filteredFeatures.filter(row => row[searchFilterColumn] && String(row[searchFilterColumn]).toLowerCase().includes(String(searchFilterText).toLowerCase()));
|
|
3324
|
+
}
|
|
3325
|
+
// Sort.
|
|
3326
|
+
let rows = applySorting(filteredFeatures, {
|
|
3327
|
+
sortBy,
|
|
3328
|
+
sortByDirection: sortDirection,
|
|
3329
|
+
sortByColumnType
|
|
3330
|
+
});
|
|
3331
|
+
const totalCount = rows.length;
|
|
3332
|
+
// Offset and limit.
|
|
3333
|
+
rows = rows.slice(Math.min(offset, totalCount), Math.min(offset + limit, totalCount));
|
|
3334
|
+
// Select columns.
|
|
3335
|
+
rows = rows.map(srcRow => {
|
|
3336
|
+
const dstRow = {};
|
|
3337
|
+
for (const column of columns) {
|
|
3338
|
+
dstRow[column] = srcRow[column];
|
|
3339
|
+
}
|
|
3340
|
+
return dstRow;
|
|
3341
|
+
});
|
|
3342
|
+
return {
|
|
3343
|
+
rows,
|
|
3344
|
+
totalCount
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
async getTimeSeries({
|
|
3348
|
+
column,
|
|
3349
|
+
stepSize,
|
|
3350
|
+
operation,
|
|
3351
|
+
operationColumn,
|
|
3352
|
+
joinOperation,
|
|
3353
|
+
filters,
|
|
3354
|
+
filterOwner,
|
|
3355
|
+
spatialFilter
|
|
3356
|
+
}) {
|
|
3357
|
+
const filteredFeatures = this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3358
|
+
if (!filteredFeatures.length) {
|
|
3359
|
+
return {
|
|
3360
|
+
rows: []
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
assertColumn(this._features, column, operationColumn);
|
|
3364
|
+
const rows = groupValuesByDateColumn({
|
|
3365
|
+
data: filteredFeatures,
|
|
3366
|
+
valuesColumns: normalizeColumns(operationColumn || column),
|
|
3367
|
+
keysColumn: column,
|
|
3368
|
+
groupType: stepSize,
|
|
3369
|
+
operation,
|
|
3370
|
+
joinOperation
|
|
3371
|
+
}) || [];
|
|
3372
|
+
return {
|
|
3373
|
+
rows
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
async getRange({
|
|
3377
|
+
column,
|
|
3378
|
+
filters,
|
|
3379
|
+
filterOwner,
|
|
3380
|
+
spatialFilter
|
|
3381
|
+
}) {
|
|
3382
|
+
assertColumn(this._features, column);
|
|
3383
|
+
const filteredFeatures = this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3384
|
+
if (!this._features.length) {
|
|
3385
|
+
// TODO: Is this the only nullable response in the Widgets API? If so,
|
|
3386
|
+
// can we do something more consistent?
|
|
3387
|
+
return null;
|
|
3388
|
+
}
|
|
3389
|
+
return {
|
|
3390
|
+
min: aggregationFunctions.min(filteredFeatures, column),
|
|
3391
|
+
max: aggregationFunctions.max(filteredFeatures, column)
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
/****************************************************************************
|
|
3395
|
+
* INTERNAL
|
|
3396
|
+
*/
|
|
3397
|
+
_getFilteredFeatures(spatialFilter, filters, filterOwner) {
|
|
3398
|
+
assert(spatialFilter, 'spatialFilter required for tilesets');
|
|
3399
|
+
this._extractTileFeatures(spatialFilter);
|
|
3400
|
+
return applyFilters(this._features, getApplicableFilters(filterOwner, filters || this.props.filters), this.props.filtersLogicalOperator || 'and');
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
function assertColumn(features, ...columnArgs) {
|
|
3404
|
+
// TODO(cleanup): Can drop support for multiple column shapes here?
|
|
3405
|
+
// Due to the multiple column shape, we normalise it as an array with normalizeColumns
|
|
3406
|
+
const columns = Array.from(new Set(columnArgs.map(normalizeColumns).flat()));
|
|
3407
|
+
const featureKeys = Object.keys(features[0]);
|
|
3408
|
+
const invalidColumns = columns.filter(column => !featureKeys.includes(column));
|
|
3409
|
+
if (invalidColumns.length) {
|
|
3410
|
+
throw new InvalidColumnError(`Missing column(s): ${invalidColumns.join(', ')}`);
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
function normalizeColumns(columns) {
|
|
3414
|
+
return Array.isArray(columns) ? columns : typeof columns === 'string' ? [columns] : [];
|
|
3415
|
+
}
|
|
3416
|
+
|
|
1441
3417
|
const h3QuerySource = async function h3QuerySource(options) {
|
|
1442
3418
|
const {
|
|
1443
3419
|
aggregationExp,
|
|
@@ -1613,7 +3589,7 @@ const vectorQuerySource = async function vectorQuerySource(options) {
|
|
|
1613
3589
|
const {
|
|
1614
3590
|
columns,
|
|
1615
3591
|
filters,
|
|
1616
|
-
spatialDataColumn =
|
|
3592
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1617
3593
|
sqlQuery,
|
|
1618
3594
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1619
3595
|
queryParameters,
|
|
@@ -1652,7 +3628,7 @@ const vectorTableSource = async function vectorTableSource(options) {
|
|
|
1652
3628
|
const {
|
|
1653
3629
|
columns,
|
|
1654
3630
|
filters,
|
|
1655
|
-
spatialDataColumn =
|
|
3631
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1656
3632
|
tableName,
|
|
1657
3633
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1658
3634
|
aggregationExp
|
|
@@ -1738,5 +3714,5 @@ const query = async function query(options) {
|
|
|
1738
3714
|
});
|
|
1739
3715
|
};
|
|
1740
3716
|
|
|
1741
|
-
export { ApiVersion, CartoAPIError, DEFAULT_API_BASE_URL, FilterType, SOURCE_DEFAULTS,
|
|
3717
|
+
export { ApiVersion, CartoAPIError, DEFAULT_API_BASE_URL, FEATURE_GEOM_PROPERTY, FilterType, Provider, SOURCE_DEFAULTS, SpatialIndex, TileFormat, WidgetQuerySource, WidgetRemoteSource, WidgetSource, WidgetTableSource, WidgetTilesetSource, _buildFeatureFilter, _getHexagonResolution, addFilter, aggregate, aggregationFunctions, applyFilters, applySorting, boundaryQuerySource, boundaryTableSource, buildBinaryFeatureFilter, buildPublicMapUrl, buildStatsUrl, clearFilters, createPolygonSpatialFilter, createViewportSpatialFilter, filterFunctions, geojsonFeatures, getClient, getDataFilterExtensionProps, getFilter, groupValuesByColumn, groupValuesByDateColumn, h3QuerySource, h3TableSource, h3TilesetSource, hasFilter, histogram, makeIntervalComplete, quadbinQuerySource, quadbinTableSource, quadbinTilesetSource, query, rasterSource, removeFilter, requestWithParameters, scatterPlot, setClient, tileFeatures, tileFeaturesGeometries, tileFeaturesSpatialIndex, transformToTileCoords, vectorQuerySource, vectorTableSource, vectorTilesetSource };
|
|
1742
3718
|
//# sourceMappingURL=api-client.modern.js.map
|