@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
package/build/api-client.cjs
CHANGED
|
@@ -1,67 +1,1424 @@
|
|
|
1
|
-
var
|
|
1
|
+
var intersects = require('@turf/boolean-intersects');
|
|
2
2
|
var bboxPolygon = require('@turf/bbox-polygon');
|
|
3
|
+
var booleanWithin = require('@turf/boolean-within');
|
|
4
|
+
var intersect = require('@turf/intersect');
|
|
5
|
+
var helpers = require('@turf/helpers');
|
|
6
|
+
var bboxClip = require('@turf/bbox-clip');
|
|
7
|
+
var h3Js = require('h3-js');
|
|
3
8
|
var union = require('@turf/union');
|
|
4
9
|
var invariant = require('@turf/invariant');
|
|
5
|
-
var
|
|
10
|
+
var booleanEqual = require('@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;
|
|
20
25
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Sets current client ID, used to categorize API requests. For internal use only.
|
|
23
|
-
*
|
|
24
|
-
* @internal
|
|
25
|
-
* @
|
|
26
|
-
*/
|
|
27
|
-
function setClient(c) {
|
|
28
|
-
client = c;
|
|
26
|
+
/**
|
|
27
|
+
* Sets current client ID, used to categorize API requests. For internal use only.
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
* @privateRemarks Source: @carto/react-core
|
|
31
|
+
*/
|
|
32
|
+
function setClient(c) {
|
|
33
|
+
client = c;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Defines a comparator used when matching a column's values against given filter values.
|
|
38
|
+
*
|
|
39
|
+
* Example:
|
|
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
|
+
exports.FilterType = void 0;
|
|
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
|
+
})(exports.FilterType || (exports.FilterType = {}));
|
|
60
|
+
/** @privateRemarks Source: @carto/constants */
|
|
61
|
+
exports.ApiVersion = void 0;
|
|
62
|
+
(function (ApiVersion) {
|
|
63
|
+
ApiVersion["V1"] = "v1";
|
|
64
|
+
ApiVersion["V2"] = "v2";
|
|
65
|
+
ApiVersion["V3"] = "v3";
|
|
66
|
+
})(exports.ApiVersion || (exports.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
|
+
exports.TileFormat = void 0;
|
|
71
|
+
(function (TileFormat) {
|
|
72
|
+
TileFormat["MVT"] = "mvt";
|
|
73
|
+
TileFormat["JSON"] = "json";
|
|
74
|
+
TileFormat["GEOJSON"] = "geojson";
|
|
75
|
+
TileFormat["BINARY"] = "binary";
|
|
76
|
+
})(exports.TileFormat || (exports.TileFormat = {}));
|
|
77
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
78
|
+
exports.SpatialIndex = void 0;
|
|
79
|
+
(function (SpatialIndex) {
|
|
80
|
+
SpatialIndex["H3"] = "h3";
|
|
81
|
+
SpatialIndex["S2"] = "s2";
|
|
82
|
+
SpatialIndex["QUADBIN"] = "quadbin";
|
|
83
|
+
})(exports.SpatialIndex || (exports.SpatialIndex = {}));
|
|
84
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
85
|
+
exports.Provider = void 0;
|
|
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
|
+
})(exports.Provider || (exports.Provider = {}));
|
|
94
|
+
|
|
95
|
+
function makeIntervalComplete(intervals) {
|
|
96
|
+
return intervals.map(val => {
|
|
97
|
+
if (val[0] === undefined || val[0] === null) {
|
|
98
|
+
return [Number.MIN_SAFE_INTEGER, val[1]];
|
|
99
|
+
}
|
|
100
|
+
if (val[1] === undefined || val[1] === null) {
|
|
101
|
+
return [val[0], Number.MAX_SAFE_INTEGER];
|
|
102
|
+
}
|
|
103
|
+
return val;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const filterFunctions = {
|
|
108
|
+
[exports.FilterType.IN]: filterIn,
|
|
109
|
+
[exports.FilterType.BETWEEN]: filterBetween,
|
|
110
|
+
[exports.FilterType.TIME]: filterTime,
|
|
111
|
+
[exports.FilterType.CLOSED_OPEN]: filterClosedOpen,
|
|
112
|
+
[exports.FilterType.STRING_SEARCH]: filterStringSearch
|
|
113
|
+
};
|
|
114
|
+
function filterIn(filterValues, featureValue) {
|
|
115
|
+
return filterValues.includes(featureValue);
|
|
116
|
+
}
|
|
117
|
+
// FilterTypes.BETWEEN
|
|
118
|
+
function filterBetween(filterValues, featureValue) {
|
|
119
|
+
const checkRange = range => {
|
|
120
|
+
const [lowerBound, upperBound] = range;
|
|
121
|
+
return featureValue >= lowerBound && featureValue <= upperBound;
|
|
122
|
+
};
|
|
123
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
124
|
+
}
|
|
125
|
+
function filterTime(filterValues, featureValue) {
|
|
126
|
+
const featureValueAsTimestamp = new Date(featureValue).getTime();
|
|
127
|
+
if (isFinite(featureValueAsTimestamp)) {
|
|
128
|
+
return filterBetween(filterValues, featureValueAsTimestamp);
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(`Column used to filter by time isn't well formatted.`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// FilterTypes.CLOSED_OPEN
|
|
134
|
+
function filterClosedOpen(filterValues, featureValue) {
|
|
135
|
+
const checkRange = range => {
|
|
136
|
+
const [lowerBound, upperBound] = range;
|
|
137
|
+
return featureValue >= lowerBound && featureValue < upperBound;
|
|
138
|
+
};
|
|
139
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
140
|
+
}
|
|
141
|
+
// FilterTypes.STRING_SEARCH
|
|
142
|
+
function filterStringSearch(filterValues, featureValue, params) {
|
|
143
|
+
if (params === void 0) {
|
|
144
|
+
params = {};
|
|
145
|
+
}
|
|
146
|
+
const normalizedFeatureValue = normalize(featureValue, params);
|
|
147
|
+
const stringRegExp = params.useRegExp ? filterValues : filterValues.map(filterValue => {
|
|
148
|
+
let stringRegExp = escapeRegExp(normalize(filterValue, params));
|
|
149
|
+
if (params.mustStart) stringRegExp = `^${stringRegExp}`;
|
|
150
|
+
if (params.mustEnd) stringRegExp = `${stringRegExp}$`;
|
|
151
|
+
return stringRegExp;
|
|
152
|
+
});
|
|
153
|
+
const regex = new RegExp(stringRegExp.join('|'), params.caseSensitive ? 'g' : 'gi');
|
|
154
|
+
return !!normalizedFeatureValue.match(regex);
|
|
155
|
+
}
|
|
156
|
+
// Aux
|
|
157
|
+
const specialCharRegExp = /[.*+?^${}()|[\]\\]/g;
|
|
158
|
+
const normalizeRegExp = /\p{Diacritic}/gu;
|
|
159
|
+
function escapeRegExp(value) {
|
|
160
|
+
return value.replace(specialCharRegExp, '\\$&');
|
|
161
|
+
}
|
|
162
|
+
function normalize(data, params) {
|
|
163
|
+
let normalizedData = String(data);
|
|
164
|
+
if (!params.keepSpecialCharacters) normalizedData = normalizedData.normalize('NFD').replace(normalizeRegExp, '');
|
|
165
|
+
return normalizedData;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const LOGICAL_OPERATOR_METHODS = {
|
|
169
|
+
and: 'every',
|
|
170
|
+
or: 'some'
|
|
171
|
+
};
|
|
172
|
+
function passesFilter(columns, filters, feature, filtersLogicalOperator) {
|
|
173
|
+
const method = LOGICAL_OPERATOR_METHODS[filtersLogicalOperator];
|
|
174
|
+
return columns[method](column => {
|
|
175
|
+
const columnFilters = filters[column];
|
|
176
|
+
const columnFilterTypes = Object.keys(columnFilters);
|
|
177
|
+
if (!feature || feature[column] === null || feature[column] === undefined) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return columnFilterTypes.every(filter => {
|
|
181
|
+
const filterFunction = filterFunctions[filter];
|
|
182
|
+
if (!filterFunction) {
|
|
183
|
+
throw new Error(`"${filter}" filter is not implemented.`);
|
|
184
|
+
}
|
|
185
|
+
return filterFunction(columnFilters[filter].values, feature[column], columnFilters[filter].params);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* @internal
|
|
191
|
+
* @privateRemarks Exported for use in @deck.gl/carto's getDataFilterExtensionProps.
|
|
192
|
+
*/
|
|
193
|
+
function _buildFeatureFilter(_ref) {
|
|
194
|
+
let {
|
|
195
|
+
filters = {},
|
|
196
|
+
type = 'boolean',
|
|
197
|
+
filtersLogicalOperator = 'and'
|
|
198
|
+
} = _ref;
|
|
199
|
+
const columns = Object.keys(filters);
|
|
200
|
+
if (!columns.length) {
|
|
201
|
+
return () => type === 'number' ? 1 : true;
|
|
202
|
+
}
|
|
203
|
+
return feature => {
|
|
204
|
+
const f = feature.properties || feature;
|
|
205
|
+
const featurePassesFilter = passesFilter(columns, filters, f, filtersLogicalOperator);
|
|
206
|
+
return type === 'number' ? Number(featurePassesFilter) : featurePassesFilter;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Apply certain filters to a collection of features.
|
|
211
|
+
* @internal
|
|
212
|
+
*/
|
|
213
|
+
function applyFilters(features, filters, filtersLogicalOperator) {
|
|
214
|
+
return Object.keys(filters).length ? features.filter(_buildFeatureFilter({
|
|
215
|
+
filters,
|
|
216
|
+
filtersLogicalOperator
|
|
217
|
+
})) : features;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Binary.
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
function buildBinaryFeatureFilter(_ref2) {
|
|
224
|
+
let {
|
|
225
|
+
filters = {}
|
|
226
|
+
} = _ref2;
|
|
227
|
+
const columns = Object.keys(filters);
|
|
228
|
+
if (!columns.length) {
|
|
229
|
+
return () => 1;
|
|
230
|
+
}
|
|
231
|
+
return (featureIdIdx, binaryData) => passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
|
|
232
|
+
}
|
|
233
|
+
function getValueFromNumericProps(featureIdIdx, binaryData, _ref3) {
|
|
234
|
+
let {
|
|
235
|
+
column
|
|
236
|
+
} = _ref3;
|
|
237
|
+
return binaryData.numericProps?.[column]?.value[featureIdIdx];
|
|
238
|
+
}
|
|
239
|
+
function getValueFromProperties(featureIdIdx, binaryData, _ref4) {
|
|
240
|
+
let {
|
|
241
|
+
column
|
|
242
|
+
} = _ref4;
|
|
243
|
+
const propertyIdx = binaryData.featureIds.value[featureIdIdx];
|
|
244
|
+
return binaryData.properties[propertyIdx]?.[column];
|
|
245
|
+
}
|
|
246
|
+
const GET_VALUE_BY_BINARY_PROP = {
|
|
247
|
+
properties: getValueFromProperties,
|
|
248
|
+
numericProps: getValueFromNumericProps
|
|
249
|
+
};
|
|
250
|
+
function getBinaryPropertyByFilterValues(filterValues) {
|
|
251
|
+
return typeof filterValues.flat()[0] === 'string' ? 'properties' : 'numericProps';
|
|
252
|
+
}
|
|
253
|
+
function getFeatureValue(featureIdIdx, binaryData, filter) {
|
|
254
|
+
const {
|
|
255
|
+
column,
|
|
256
|
+
values
|
|
257
|
+
} = filter;
|
|
258
|
+
const binaryProp = getBinaryPropertyByFilterValues(values);
|
|
259
|
+
const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
|
|
260
|
+
return getFeatureValueFn(featureIdIdx, binaryData, {
|
|
261
|
+
column
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData) {
|
|
265
|
+
return columns.every(column => {
|
|
266
|
+
const columnFilters = filters[column];
|
|
267
|
+
return Object.entries(columnFilters).every(_ref5 => {
|
|
268
|
+
let [type, {
|
|
269
|
+
values
|
|
270
|
+
}] = _ref5;
|
|
271
|
+
const filterFn = filterFunctions[type];
|
|
272
|
+
if (!filterFn) {
|
|
273
|
+
throw new Error(`"${type}" filter is not implemented.`);
|
|
274
|
+
}
|
|
275
|
+
if (!values) return 0;
|
|
276
|
+
const featureValue = getFeatureValue(featureIdIdx, binaryData, {
|
|
277
|
+
type: type,
|
|
278
|
+
column,
|
|
279
|
+
values
|
|
280
|
+
});
|
|
281
|
+
if (featureValue === undefined || featureValue === null) return 0;
|
|
282
|
+
return filterFn(values, featureValue);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function geojsonFeatures(_ref) {
|
|
288
|
+
let {
|
|
289
|
+
geojson,
|
|
290
|
+
spatialFilter,
|
|
291
|
+
uniqueIdProperty
|
|
292
|
+
} = _ref;
|
|
293
|
+
let uniqueIdx = 0;
|
|
294
|
+
const map = new Map();
|
|
295
|
+
if (!spatialFilter) {
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
for (const feature of geojson.features) {
|
|
299
|
+
const uniqueId = uniqueIdProperty ? feature.properties[uniqueIdProperty] : ++uniqueIdx;
|
|
300
|
+
if (!map.has(uniqueId) && intersects(spatialFilter, feature)) {
|
|
301
|
+
map.set(uniqueId, feature.properties);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return Array.from(map.values());
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// math.gl
|
|
308
|
+
// SPDX-License-Identifier: MIT
|
|
309
|
+
// Copyright (c) vis.gl contributors
|
|
310
|
+
const DEFAULT_CONFIG = {
|
|
311
|
+
EPSILON: 1e-12,
|
|
312
|
+
debug: false,
|
|
313
|
+
precision: 4,
|
|
314
|
+
printTypes: false,
|
|
315
|
+
printDegrees: false,
|
|
316
|
+
printRowMajor: true,
|
|
317
|
+
_cartographicRadians: false
|
|
318
|
+
};
|
|
319
|
+
// Configuration is truly global as of v3.6 to ensure single config even if multiple copies of math.gl
|
|
320
|
+
// Multiple copies of config can be quite tricky to debug...
|
|
321
|
+
globalThis.mathgl = globalThis.mathgl || {
|
|
322
|
+
config: {
|
|
323
|
+
...DEFAULT_CONFIG
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Check if value is an "array"
|
|
328
|
+
* Returns `true` if value is either an array or a typed array
|
|
329
|
+
* Note: returns `false` for `ArrayBuffer` and `DataView` instances
|
|
330
|
+
* @note isTypedArray and isNumericArray are often more useful in TypeScript
|
|
331
|
+
*/
|
|
332
|
+
function isArray(value) {
|
|
333
|
+
return Array.isArray(value) || ArrayBuffer.isView(value) && !(value instanceof DataView);
|
|
334
|
+
}
|
|
335
|
+
function lerp(a, b, t) {
|
|
336
|
+
if (isArray(a)) {
|
|
337
|
+
return a.map((ai, i) => lerp(ai, b[i], t));
|
|
338
|
+
}
|
|
339
|
+
return t * b + (1 - t) * a;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Replacement for the external assert method to reduce bundle size
|
|
343
|
+
// Note: We don't use the second "message" argument in calling code,
|
|
344
|
+
// so no need to support it here
|
|
345
|
+
function assert$1(condition, message) {
|
|
346
|
+
if (!condition) {
|
|
347
|
+
throw new Error(message || '@math.gl/web-mercator: assertion failed.');
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// TODO - THE UTILITIES IN THIS FILE SHOULD BE IMPORTED FROM WEB-MERCATOR-VIEWPORT MODULE
|
|
352
|
+
// CONSTANTS
|
|
353
|
+
const PI = Math.PI;
|
|
354
|
+
const PI_4 = PI / 4;
|
|
355
|
+
const DEGREES_TO_RADIANS = PI / 180;
|
|
356
|
+
const RADIANS_TO_DEGREES = 180 / PI;
|
|
357
|
+
const TILE_SIZE = 512;
|
|
358
|
+
/**
|
|
359
|
+
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
|
|
360
|
+
* Performs the nonlinear part of the web mercator projection.
|
|
361
|
+
* Remaining projection is done with 4x4 matrices which also handles
|
|
362
|
+
* perspective.
|
|
363
|
+
*
|
|
364
|
+
* @param lngLat - [lng, lat] coordinates
|
|
365
|
+
* Specifies a point on the sphere to project onto the map.
|
|
366
|
+
* @return [x,y] coordinates.
|
|
367
|
+
*/
|
|
368
|
+
function lngLatToWorld(lngLat) {
|
|
369
|
+
const [lng, lat] = lngLat;
|
|
370
|
+
assert$1(Number.isFinite(lng));
|
|
371
|
+
assert$1(Number.isFinite(lat) && lat >= -90 && lat <= 90, 'invalid latitude');
|
|
372
|
+
const lambda2 = lng * DEGREES_TO_RADIANS;
|
|
373
|
+
const phi2 = lat * DEGREES_TO_RADIANS;
|
|
374
|
+
const x = TILE_SIZE * (lambda2 + PI) / (2 * PI);
|
|
375
|
+
const y = TILE_SIZE * (PI + Math.log(Math.tan(PI_4 + phi2 * 0.5))) / (2 * PI);
|
|
376
|
+
return [x, y];
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Unproject world point [x,y] on map onto {lat, lon} on sphere
|
|
380
|
+
*
|
|
381
|
+
* @param xy - array with [x,y] members
|
|
382
|
+
* representing point on projected map plane
|
|
383
|
+
* @return - array with [x,y] of point on sphere.
|
|
384
|
+
* Has toArray method if you need a GeoJSON Array.
|
|
385
|
+
* Per cartographic tradition, lat and lon are specified as degrees.
|
|
386
|
+
*/
|
|
387
|
+
function worldToLngLat(xy) {
|
|
388
|
+
const [x, y] = xy;
|
|
389
|
+
const lambda2 = x / TILE_SIZE * (2 * PI) - PI;
|
|
390
|
+
const phi2 = 2 * (Math.atan(Math.exp(y / TILE_SIZE * (2 * PI) - PI)) - PI_4);
|
|
391
|
+
return [lambda2 * RADIANS_TO_DEGREES, phi2 * RADIANS_TO_DEGREES];
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const TRANSFORM_FN$1 = {
|
|
395
|
+
Point: transformPoint$1,
|
|
396
|
+
MultiPoint: transformMultiPoint$1,
|
|
397
|
+
LineString: transformLineString$1,
|
|
398
|
+
MultiLineString: transformMultiLineString$1,
|
|
399
|
+
Polygon: transformPolygon$1,
|
|
400
|
+
MultiPolygon: transformMultiPolygon$1
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* Transform WGS84 coordinates to tile coords.
|
|
404
|
+
* 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)
|
|
405
|
+
*
|
|
406
|
+
* @param geometry - any valid geojson geometry
|
|
407
|
+
* @param bbox - geojson bbox
|
|
408
|
+
*/
|
|
409
|
+
function transformToTileCoords(geometry, bbox) {
|
|
410
|
+
const [west, south, east, north] = bbox;
|
|
411
|
+
const nw = projectFlat([west, north]);
|
|
412
|
+
const se = projectFlat([east, south]);
|
|
413
|
+
const projectedBbox = [nw, se];
|
|
414
|
+
if (geometry.type === 'GeometryCollection') {
|
|
415
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
416
|
+
}
|
|
417
|
+
const transformFn = TRANSFORM_FN$1[geometry.type];
|
|
418
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
419
|
+
return {
|
|
420
|
+
...geometry,
|
|
421
|
+
coordinates
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function transformPoint$1(_ref, _ref2) {
|
|
425
|
+
let [pointX, pointY] = _ref;
|
|
426
|
+
let [nw, se] = _ref2;
|
|
427
|
+
const x = inverseLerp(nw[0], se[0], pointX);
|
|
428
|
+
const y = inverseLerp(nw[1], se[1], pointY);
|
|
429
|
+
return [x, y];
|
|
430
|
+
}
|
|
431
|
+
function getPoints$1(geometry, bbox) {
|
|
432
|
+
return geometry.map(g => transformPoint$1(projectFlat(g), bbox));
|
|
433
|
+
}
|
|
434
|
+
function transformMultiPoint$1(multiPoint, bbox) {
|
|
435
|
+
return getPoints$1(multiPoint, bbox);
|
|
436
|
+
}
|
|
437
|
+
function transformLineString$1(line, bbox) {
|
|
438
|
+
return getPoints$1(line, bbox);
|
|
439
|
+
}
|
|
440
|
+
function transformMultiLineString$1(multiLineString, bbox) {
|
|
441
|
+
return multiLineString.map(lineString => transformLineString$1(lineString, bbox));
|
|
442
|
+
}
|
|
443
|
+
function transformPolygon$1(polygon, bbox) {
|
|
444
|
+
return polygon.map(polygonRing => getPoints$1(polygonRing, bbox));
|
|
445
|
+
}
|
|
446
|
+
function transformMultiPolygon$1(multiPolygon, bbox) {
|
|
447
|
+
return multiPolygon.map(polygon => transformPolygon$1(polygon, bbox));
|
|
448
|
+
}
|
|
449
|
+
function projectFlat(xyz) {
|
|
450
|
+
return lngLatToWorld(xyz);
|
|
451
|
+
}
|
|
452
|
+
function inverseLerp(a, b, x) {
|
|
453
|
+
return (x - a) / (b - a);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const TRANSFORM_FN = {
|
|
457
|
+
Point: transformPoint,
|
|
458
|
+
MultiPoint: transformMultiPoint,
|
|
459
|
+
LineString: transformLineString,
|
|
460
|
+
MultiLineString: transformMultiLineString,
|
|
461
|
+
Polygon: transformPolygon,
|
|
462
|
+
MultiPolygon: transformMultiPolygon
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* Transform tile coords to WGS84 coordinates.
|
|
466
|
+
*
|
|
467
|
+
* @param geometry - any valid geojson geometry
|
|
468
|
+
* @param bbox - geojson bbox
|
|
469
|
+
*/
|
|
470
|
+
function transformTileCoordsToWGS84(geometry, bbox) {
|
|
471
|
+
const [west, south, east, north] = bbox;
|
|
472
|
+
const nw = lngLatToWorld([west, north]);
|
|
473
|
+
const se = lngLatToWorld([east, south]);
|
|
474
|
+
const projectedBbox = [nw, se];
|
|
475
|
+
if (geometry.type === 'GeometryCollection') {
|
|
476
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
477
|
+
}
|
|
478
|
+
const transformFn = TRANSFORM_FN[geometry.type];
|
|
479
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
480
|
+
return {
|
|
481
|
+
...geometry,
|
|
482
|
+
coordinates
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
function transformPoint(_ref, _ref2) {
|
|
486
|
+
let [pointX, pointY] = _ref;
|
|
487
|
+
let [nw, se] = _ref2;
|
|
488
|
+
const x = lerp(nw[0], se[0], pointX);
|
|
489
|
+
const y = lerp(nw[1], se[1], pointY);
|
|
490
|
+
return worldToLngLat([x, y]);
|
|
491
|
+
}
|
|
492
|
+
function getPoints(geometry, bbox) {
|
|
493
|
+
return geometry.map(g => transformPoint(g, bbox));
|
|
494
|
+
}
|
|
495
|
+
function transformMultiPoint(multiPoint, bbox) {
|
|
496
|
+
return getPoints(multiPoint, bbox);
|
|
497
|
+
}
|
|
498
|
+
function transformLineString(line, bbox) {
|
|
499
|
+
return getPoints(line, bbox);
|
|
500
|
+
}
|
|
501
|
+
function transformMultiLineString(multiLineString, bbox) {
|
|
502
|
+
return multiLineString.map(lineString => transformLineString(lineString, bbox));
|
|
503
|
+
}
|
|
504
|
+
function transformPolygon(polygon, bbox) {
|
|
505
|
+
return polygon.map(polygonRing => getPoints(polygonRing, bbox));
|
|
506
|
+
}
|
|
507
|
+
function transformMultiPolygon(multiPolygon, bbox) {
|
|
508
|
+
return multiPolygon.map(polygon => transformPolygon(polygon, bbox));
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const FEATURE_GEOM_PROPERTY = '__geomValue';
|
|
512
|
+
function tileFeaturesGeometries(_ref) {
|
|
513
|
+
let {
|
|
514
|
+
tiles,
|
|
515
|
+
tileFormat,
|
|
516
|
+
spatialFilter,
|
|
517
|
+
uniqueIdProperty,
|
|
518
|
+
options
|
|
519
|
+
} = _ref;
|
|
520
|
+
const map = new Map();
|
|
521
|
+
for (const tile of tiles) {
|
|
522
|
+
// Discard if it's not a visible tile (only check false value, not undefined)
|
|
523
|
+
// or tile has not data
|
|
524
|
+
if (tile.isVisible === false || !tile.data) {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const bbox = [tile.bbox.west, tile.bbox.south, tile.bbox.east, tile.bbox.north];
|
|
528
|
+
const bboxToGeom = bboxPolygon(bbox);
|
|
529
|
+
const tileIsFullyVisible = booleanWithin(bboxToGeom, spatialFilter);
|
|
530
|
+
// Clip the geometry to intersect with the tile
|
|
531
|
+
const spatialFilterFeature = {
|
|
532
|
+
type: 'Feature',
|
|
533
|
+
geometry: spatialFilter,
|
|
534
|
+
properties: {}
|
|
535
|
+
};
|
|
536
|
+
const clippedGeometryToIntersect = intersect(helpers.featureCollection([bboxToGeom, spatialFilterFeature]));
|
|
537
|
+
if (!clippedGeometryToIntersect) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
// We assume that MVT tileFormat uses local coordinates so we transform the geometry to intersect to tile coordinates [0..1],
|
|
541
|
+
// while in the case of 'geojson' or binary, the geometries are already in WGS84
|
|
542
|
+
const transformedGeometryToIntersect = tileFormat === exports.TileFormat.MVT ? transformToTileCoords(clippedGeometryToIntersect.geometry, bbox) : clippedGeometryToIntersect.geometry;
|
|
543
|
+
createIndicesForPoints(tile.data.points);
|
|
544
|
+
calculateFeatures({
|
|
545
|
+
map,
|
|
546
|
+
tileIsFullyVisible,
|
|
547
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
548
|
+
data: tile.data.points,
|
|
549
|
+
type: 'Point',
|
|
550
|
+
bbox,
|
|
551
|
+
tileFormat,
|
|
552
|
+
uniqueIdProperty,
|
|
553
|
+
options
|
|
554
|
+
});
|
|
555
|
+
calculateFeatures({
|
|
556
|
+
map,
|
|
557
|
+
tileIsFullyVisible,
|
|
558
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
559
|
+
data: tile.data.lines,
|
|
560
|
+
type: 'LineString',
|
|
561
|
+
bbox,
|
|
562
|
+
tileFormat,
|
|
563
|
+
uniqueIdProperty,
|
|
564
|
+
options
|
|
565
|
+
});
|
|
566
|
+
calculateFeatures({
|
|
567
|
+
map,
|
|
568
|
+
tileIsFullyVisible,
|
|
569
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
570
|
+
data: tile.data.polygons,
|
|
571
|
+
type: 'Polygon',
|
|
572
|
+
bbox,
|
|
573
|
+
tileFormat,
|
|
574
|
+
uniqueIdProperty,
|
|
575
|
+
options
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
return Array.from(map.values());
|
|
579
|
+
}
|
|
580
|
+
function processTileFeatureProperties(_ref2) {
|
|
581
|
+
let {
|
|
582
|
+
map,
|
|
583
|
+
data,
|
|
584
|
+
startIndex,
|
|
585
|
+
endIndex,
|
|
586
|
+
type,
|
|
587
|
+
bbox,
|
|
588
|
+
tileFormat,
|
|
589
|
+
uniqueIdProperty,
|
|
590
|
+
storeGeometry,
|
|
591
|
+
geometryIntersection
|
|
592
|
+
} = _ref2;
|
|
593
|
+
const tileProps = getPropertiesFromTile(data, startIndex);
|
|
594
|
+
const uniquePropertyValue = getUniquePropertyValue(tileProps, uniqueIdProperty, map);
|
|
595
|
+
if (!uniquePropertyValue || map.has(uniquePropertyValue)) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
let geometry = null;
|
|
599
|
+
// Only calculate geometry if necessary
|
|
600
|
+
if (storeGeometry || geometryIntersection) {
|
|
601
|
+
const {
|
|
602
|
+
positions
|
|
603
|
+
} = data;
|
|
604
|
+
const ringCoordinates = getRingCoordinatesFor(startIndex, endIndex, positions);
|
|
605
|
+
geometry = getFeatureByType(ringCoordinates, type);
|
|
606
|
+
}
|
|
607
|
+
// If intersection is required, check before proceeding
|
|
608
|
+
if (geometry && geometryIntersection && !intersects(geometry, geometryIntersection)) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const properties = parseProperties(tileProps);
|
|
612
|
+
// Only save geometry if necessary
|
|
613
|
+
if (storeGeometry && geometry) {
|
|
614
|
+
properties[FEATURE_GEOM_PROPERTY] = tileFormat === exports.TileFormat.MVT ? transformTileCoordsToWGS84(geometry, bbox) : geometry;
|
|
615
|
+
}
|
|
616
|
+
map.set(uniquePropertyValue, properties);
|
|
617
|
+
}
|
|
618
|
+
function addIntersectedFeaturesInTile(_ref3) {
|
|
619
|
+
let {
|
|
620
|
+
map,
|
|
621
|
+
data,
|
|
622
|
+
geometryIntersection,
|
|
623
|
+
type,
|
|
624
|
+
bbox,
|
|
625
|
+
tileFormat,
|
|
626
|
+
uniqueIdProperty,
|
|
627
|
+
options
|
|
628
|
+
} = _ref3;
|
|
629
|
+
const indices = getIndices(data);
|
|
630
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
631
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
632
|
+
const startIndex = indices[i];
|
|
633
|
+
const endIndex = indices[i + 1];
|
|
634
|
+
processTileFeatureProperties({
|
|
635
|
+
map,
|
|
636
|
+
data,
|
|
637
|
+
startIndex,
|
|
638
|
+
endIndex,
|
|
639
|
+
type,
|
|
640
|
+
bbox,
|
|
641
|
+
tileFormat,
|
|
642
|
+
uniqueIdProperty,
|
|
643
|
+
storeGeometry,
|
|
644
|
+
geometryIntersection
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function getIndices(data) {
|
|
649
|
+
let indices;
|
|
650
|
+
switch (data.type) {
|
|
651
|
+
case 'Point':
|
|
652
|
+
// @ts-expect-error Missing or changed types?
|
|
653
|
+
indices = data.pointIndices;
|
|
654
|
+
break;
|
|
655
|
+
case 'LineString':
|
|
656
|
+
indices = data.pathIndices;
|
|
657
|
+
break;
|
|
658
|
+
case 'Polygon':
|
|
659
|
+
indices = data.primitivePolygonIndices;
|
|
660
|
+
break;
|
|
661
|
+
default:
|
|
662
|
+
throw new Error(`Unexpected type, "${data.type}"`);
|
|
663
|
+
}
|
|
664
|
+
return indices.value;
|
|
665
|
+
}
|
|
666
|
+
function getFeatureId(data, startIndex) {
|
|
667
|
+
return data.featureIds.value[startIndex];
|
|
668
|
+
}
|
|
669
|
+
function getPropertiesFromTile(data, startIndex) {
|
|
670
|
+
const featureId = getFeatureId(data, startIndex);
|
|
671
|
+
const {
|
|
672
|
+
properties,
|
|
673
|
+
numericProps,
|
|
674
|
+
fields
|
|
675
|
+
} = data;
|
|
676
|
+
const result = {
|
|
677
|
+
uniqueId: fields?.[featureId]?.id,
|
|
678
|
+
properties: properties[featureId],
|
|
679
|
+
numericProps: {}
|
|
680
|
+
};
|
|
681
|
+
for (const key in numericProps) {
|
|
682
|
+
result.numericProps[key] = numericProps[key].value[startIndex];
|
|
683
|
+
}
|
|
684
|
+
return result;
|
|
685
|
+
}
|
|
686
|
+
function parseProperties(tileProps) {
|
|
687
|
+
const {
|
|
688
|
+
properties,
|
|
689
|
+
numericProps
|
|
690
|
+
} = tileProps;
|
|
691
|
+
return Object.assign({}, properties, numericProps);
|
|
692
|
+
}
|
|
693
|
+
function getUniquePropertyValue(tileProps, uniqueIdProperty, map) {
|
|
694
|
+
if (uniqueIdProperty) {
|
|
695
|
+
return getValueFromTileProps(tileProps, uniqueIdProperty);
|
|
696
|
+
}
|
|
697
|
+
if (tileProps.uniqueId) {
|
|
698
|
+
return tileProps.uniqueId;
|
|
699
|
+
}
|
|
700
|
+
const artificialId = map.size + 1; // a counter, assumed as a valid new id
|
|
701
|
+
return getValueFromTileProps(tileProps, 'cartodb_id') || getValueFromTileProps(tileProps, 'geoid') || artificialId;
|
|
702
|
+
}
|
|
703
|
+
function getValueFromTileProps(tileProps, propertyName) {
|
|
704
|
+
const {
|
|
705
|
+
properties,
|
|
706
|
+
numericProps
|
|
707
|
+
} = tileProps;
|
|
708
|
+
return numericProps[propertyName] || properties[propertyName];
|
|
709
|
+
}
|
|
710
|
+
function getFeatureByType(coordinates, type) {
|
|
711
|
+
switch (type) {
|
|
712
|
+
case 'Polygon':
|
|
713
|
+
return {
|
|
714
|
+
type: 'Polygon',
|
|
715
|
+
coordinates: [coordinates]
|
|
716
|
+
};
|
|
717
|
+
case 'LineString':
|
|
718
|
+
return {
|
|
719
|
+
type: 'LineString',
|
|
720
|
+
coordinates
|
|
721
|
+
};
|
|
722
|
+
case 'Point':
|
|
723
|
+
return {
|
|
724
|
+
type: 'Point',
|
|
725
|
+
coordinates: coordinates[0]
|
|
726
|
+
};
|
|
727
|
+
default:
|
|
728
|
+
throw new Error('Invalid geometry type');
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function getRingCoordinatesFor(startIndex, endIndex, positions) {
|
|
732
|
+
const ringCoordinates = [];
|
|
733
|
+
for (let j = startIndex; j < endIndex; j++) {
|
|
734
|
+
ringCoordinates.push(Array.from(positions.value.subarray(j * positions.size, (j + 1) * positions.size)));
|
|
735
|
+
}
|
|
736
|
+
return ringCoordinates;
|
|
737
|
+
}
|
|
738
|
+
function calculateFeatures(_ref4) {
|
|
739
|
+
let {
|
|
740
|
+
map,
|
|
741
|
+
tileIsFullyVisible,
|
|
742
|
+
geometryIntersection,
|
|
743
|
+
data,
|
|
744
|
+
type,
|
|
745
|
+
bbox,
|
|
746
|
+
tileFormat,
|
|
747
|
+
uniqueIdProperty,
|
|
748
|
+
options
|
|
749
|
+
} = _ref4;
|
|
750
|
+
if (!data?.properties.length) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (tileIsFullyVisible) {
|
|
754
|
+
addAllFeaturesInTile({
|
|
755
|
+
map,
|
|
756
|
+
data,
|
|
757
|
+
type,
|
|
758
|
+
bbox,
|
|
759
|
+
tileFormat,
|
|
760
|
+
uniqueIdProperty,
|
|
761
|
+
options
|
|
762
|
+
});
|
|
763
|
+
} else {
|
|
764
|
+
addIntersectedFeaturesInTile({
|
|
765
|
+
map,
|
|
766
|
+
data,
|
|
767
|
+
geometryIntersection,
|
|
768
|
+
type,
|
|
769
|
+
bbox,
|
|
770
|
+
tileFormat,
|
|
771
|
+
uniqueIdProperty,
|
|
772
|
+
options
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function addAllFeaturesInTile(_ref5) {
|
|
777
|
+
let {
|
|
778
|
+
map,
|
|
779
|
+
data,
|
|
780
|
+
type,
|
|
781
|
+
bbox,
|
|
782
|
+
tileFormat,
|
|
783
|
+
uniqueIdProperty,
|
|
784
|
+
options
|
|
785
|
+
} = _ref5;
|
|
786
|
+
const indices = getIndices(data);
|
|
787
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
788
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
789
|
+
const startIndex = indices[i];
|
|
790
|
+
const endIndex = indices[i + 1];
|
|
791
|
+
processTileFeatureProperties({
|
|
792
|
+
map,
|
|
793
|
+
data,
|
|
794
|
+
startIndex,
|
|
795
|
+
endIndex,
|
|
796
|
+
type,
|
|
797
|
+
bbox,
|
|
798
|
+
tileFormat,
|
|
799
|
+
uniqueIdProperty,
|
|
800
|
+
storeGeometry
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function createIndicesForPoints(data) {
|
|
805
|
+
const featureIds = data.featureIds.value;
|
|
806
|
+
const lastFeatureId = featureIds[featureIds.length - 1];
|
|
807
|
+
const PointIndicesArray = featureIds.constructor;
|
|
808
|
+
const pointIndices = {
|
|
809
|
+
value: new PointIndicesArray(featureIds.length + 1),
|
|
810
|
+
size: 1
|
|
811
|
+
};
|
|
812
|
+
pointIndices.value.set(featureIds);
|
|
813
|
+
pointIndices.value.set([lastFeatureId + 1], featureIds.length);
|
|
814
|
+
// @ts-expect-error Missing or changed types?
|
|
815
|
+
data.pointIndices = pointIndices;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// a tile is an array [x,y,z]
|
|
819
|
+
var d2r = Math.PI / 180,
|
|
820
|
+
r2d = 180 / Math.PI;
|
|
821
|
+
function tileToBBOX(tile) {
|
|
822
|
+
var e = tile2lon(tile[0] + 1, tile[2]);
|
|
823
|
+
var w = tile2lon(tile[0], tile[2]);
|
|
824
|
+
var s = tile2lat(tile[1] + 1, tile[2]);
|
|
825
|
+
var n = tile2lat(tile[1], tile[2]);
|
|
826
|
+
return [w, s, e, n];
|
|
827
|
+
}
|
|
828
|
+
function tileToGeoJSON(tile) {
|
|
829
|
+
var bbox = tileToBBOX(tile);
|
|
830
|
+
var poly = {
|
|
831
|
+
type: 'Polygon',
|
|
832
|
+
coordinates: [[[bbox[0], bbox[1]], [bbox[0], bbox[3]], [bbox[2], bbox[3]], [bbox[2], bbox[1]], [bbox[0], bbox[1]]]]
|
|
833
|
+
};
|
|
834
|
+
return poly;
|
|
835
|
+
}
|
|
836
|
+
function tile2lon(x, z) {
|
|
837
|
+
return x / Math.pow(2, z) * 360 - 180;
|
|
838
|
+
}
|
|
839
|
+
function tile2lat(y, z) {
|
|
840
|
+
var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
|
|
841
|
+
return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
|
842
|
+
}
|
|
843
|
+
function pointToTile(lon, lat, z) {
|
|
844
|
+
var tile = pointToTileFraction(lon, lat, z);
|
|
845
|
+
tile[0] = Math.floor(tile[0]);
|
|
846
|
+
tile[1] = Math.floor(tile[1]);
|
|
847
|
+
return tile;
|
|
848
|
+
}
|
|
849
|
+
function getChildren(tile) {
|
|
850
|
+
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]];
|
|
851
|
+
}
|
|
852
|
+
function getParent(tile) {
|
|
853
|
+
// top left
|
|
854
|
+
if (tile[0] % 2 === 0 && tile[1] % 2 === 0) {
|
|
855
|
+
return [tile[0] / 2, tile[1] / 2, tile[2] - 1];
|
|
856
|
+
}
|
|
857
|
+
// bottom left
|
|
858
|
+
else if (tile[0] % 2 === 0 && !tile[1] % 2 === 0) {
|
|
859
|
+
return [tile[0] / 2, (tile[1] - 1) / 2, tile[2] - 1];
|
|
860
|
+
}
|
|
861
|
+
// top right
|
|
862
|
+
else if (!tile[0] % 2 === 0 && tile[1] % 2 === 0) {
|
|
863
|
+
return [(tile[0] - 1) / 2, tile[1] / 2, tile[2] - 1];
|
|
864
|
+
}
|
|
865
|
+
// bottom right
|
|
866
|
+
else {
|
|
867
|
+
return [(tile[0] - 1) / 2, (tile[1] - 1) / 2, tile[2] - 1];
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function getSiblings(tile) {
|
|
871
|
+
return getChildren(getParent(tile));
|
|
872
|
+
}
|
|
873
|
+
function hasSiblings(tile, tiles) {
|
|
874
|
+
var siblings = getSiblings(tile);
|
|
875
|
+
for (var i = 0; i < siblings.length; i++) {
|
|
876
|
+
if (!hasTile(tiles, siblings[i])) return false;
|
|
877
|
+
}
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
880
|
+
function hasTile(tiles, tile) {
|
|
881
|
+
for (var i = 0; i < tiles.length; i++) {
|
|
882
|
+
if (tilesEqual(tiles[i], tile)) return true;
|
|
883
|
+
}
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
function tilesEqual(tile1, tile2) {
|
|
887
|
+
return tile1[0] === tile2[0] && tile1[1] === tile2[1] && tile1[2] === tile2[2];
|
|
888
|
+
}
|
|
889
|
+
function tileToQuadkey(tile) {
|
|
890
|
+
var index = '';
|
|
891
|
+
for (var z = tile[2]; z > 0; z--) {
|
|
892
|
+
var b = 0;
|
|
893
|
+
var mask = 1 << z - 1;
|
|
894
|
+
if ((tile[0] & mask) !== 0) b++;
|
|
895
|
+
if ((tile[1] & mask) !== 0) b += 2;
|
|
896
|
+
index += b.toString();
|
|
897
|
+
}
|
|
898
|
+
return index;
|
|
899
|
+
}
|
|
900
|
+
function quadkeyToTile(quadkey) {
|
|
901
|
+
var x = 0;
|
|
902
|
+
var y = 0;
|
|
903
|
+
var z = quadkey.length;
|
|
904
|
+
for (var i = z; i > 0; i--) {
|
|
905
|
+
var mask = 1 << i - 1;
|
|
906
|
+
switch (quadkey[z - i]) {
|
|
907
|
+
case '0':
|
|
908
|
+
break;
|
|
909
|
+
case '1':
|
|
910
|
+
x |= mask;
|
|
911
|
+
break;
|
|
912
|
+
case '2':
|
|
913
|
+
y |= mask;
|
|
914
|
+
break;
|
|
915
|
+
case '3':
|
|
916
|
+
x |= mask;
|
|
917
|
+
y |= mask;
|
|
918
|
+
break;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return [x, y, z];
|
|
922
|
+
}
|
|
923
|
+
function bboxToTile(bboxCoords) {
|
|
924
|
+
var min = pointToTile(bboxCoords[0], bboxCoords[1], 32);
|
|
925
|
+
var max = pointToTile(bboxCoords[2], bboxCoords[3], 32);
|
|
926
|
+
var bbox = [min[0], min[1], max[0], max[1]];
|
|
927
|
+
var z = getBboxZoom(bbox);
|
|
928
|
+
if (z === 0) return [0, 0, 0];
|
|
929
|
+
var x = bbox[0] >>> 32 - z;
|
|
930
|
+
var y = bbox[1] >>> 32 - z;
|
|
931
|
+
return [x, y, z];
|
|
932
|
+
}
|
|
933
|
+
function getBboxZoom(bbox) {
|
|
934
|
+
var MAX_ZOOM = 28;
|
|
935
|
+
for (var z = 0; z < MAX_ZOOM; z++) {
|
|
936
|
+
var mask = 1 << 32 - (z + 1);
|
|
937
|
+
if ((bbox[0] & mask) != (bbox[2] & mask) || (bbox[1] & mask) != (bbox[3] & mask)) {
|
|
938
|
+
return z;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return MAX_ZOOM;
|
|
942
|
+
}
|
|
943
|
+
function pointToTileFraction(lon, lat, z) {
|
|
944
|
+
var sin = Math.sin(lat * d2r),
|
|
945
|
+
z2 = Math.pow(2, z),
|
|
946
|
+
x = z2 * (lon / 360 + 0.5),
|
|
947
|
+
y = z2 * (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI);
|
|
948
|
+
return [x, y, z];
|
|
949
|
+
}
|
|
950
|
+
var tilebelt = {
|
|
951
|
+
tileToGeoJSON: tileToGeoJSON,
|
|
952
|
+
tileToBBOX: tileToBBOX,
|
|
953
|
+
getChildren: getChildren,
|
|
954
|
+
getParent: getParent,
|
|
955
|
+
getSiblings: getSiblings,
|
|
956
|
+
hasTile: hasTile,
|
|
957
|
+
hasSiblings: hasSiblings,
|
|
958
|
+
tilesEqual: tilesEqual,
|
|
959
|
+
tileToQuadkey: tileToQuadkey,
|
|
960
|
+
quadkeyToTile: quadkeyToTile,
|
|
961
|
+
pointToTile: pointToTile,
|
|
962
|
+
bboxToTile: bboxToTile,
|
|
963
|
+
pointToTileFraction: pointToTileFraction
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Given a geometry, create cells and return them in their raw form,
|
|
968
|
+
* as an array of cell identifiers.
|
|
969
|
+
*
|
|
970
|
+
* @alias tiles
|
|
971
|
+
* @param {Object} geom GeoJSON geometry
|
|
972
|
+
* @param {Object} limits an object with min_zoom and max_zoom properties
|
|
973
|
+
* specifying the minimum and maximum level to be tiled.
|
|
974
|
+
* @returns {Array<Array<number>>} An array of tiles given as [x, y, z] arrays
|
|
975
|
+
*/
|
|
976
|
+
var tiles = getTiles;
|
|
977
|
+
function getTiles(geom, limits) {
|
|
978
|
+
var i,
|
|
979
|
+
tile,
|
|
980
|
+
coords = geom.coordinates,
|
|
981
|
+
maxZoom = limits.max_zoom,
|
|
982
|
+
tileHash = {},
|
|
983
|
+
tiles = [];
|
|
984
|
+
if (geom.type === 'Point') {
|
|
985
|
+
return [tilebelt.pointToTile(coords[0], coords[1], maxZoom)];
|
|
986
|
+
} else if (geom.type === 'MultiPoint') {
|
|
987
|
+
for (i = 0; i < coords.length; i++) {
|
|
988
|
+
tile = tilebelt.pointToTile(coords[i][0], coords[i][1], maxZoom);
|
|
989
|
+
tileHash[toID(tile[0], tile[1], tile[2])] = true;
|
|
990
|
+
}
|
|
991
|
+
} else if (geom.type === 'LineString') {
|
|
992
|
+
lineCover(tileHash, coords, maxZoom);
|
|
993
|
+
} else if (geom.type === 'MultiLineString') {
|
|
994
|
+
for (i = 0; i < coords.length; i++) {
|
|
995
|
+
lineCover(tileHash, coords[i], maxZoom);
|
|
996
|
+
}
|
|
997
|
+
} else if (geom.type === 'Polygon') {
|
|
998
|
+
polygonCover(tileHash, tiles, coords, maxZoom);
|
|
999
|
+
} else if (geom.type === 'MultiPolygon') {
|
|
1000
|
+
for (i = 0; i < coords.length; i++) {
|
|
1001
|
+
polygonCover(tileHash, tiles, coords[i], maxZoom);
|
|
1002
|
+
}
|
|
1003
|
+
} else {
|
|
1004
|
+
throw new Error('Geometry type not implemented');
|
|
1005
|
+
}
|
|
1006
|
+
if (limits.min_zoom !== maxZoom) {
|
|
1007
|
+
// sync tile hash and tile array so that both contain the same tiles
|
|
1008
|
+
var len = tiles.length;
|
|
1009
|
+
appendHashTiles(tileHash, tiles);
|
|
1010
|
+
for (i = 0; i < len; i++) {
|
|
1011
|
+
var t = tiles[i];
|
|
1012
|
+
tileHash[toID(t[0], t[1], t[2])] = true;
|
|
1013
|
+
}
|
|
1014
|
+
return mergeTiles(tileHash, tiles, limits);
|
|
1015
|
+
}
|
|
1016
|
+
appendHashTiles(tileHash, tiles);
|
|
1017
|
+
return tiles;
|
|
1018
|
+
}
|
|
1019
|
+
function mergeTiles(tileHash, tiles, limits) {
|
|
1020
|
+
var mergedTiles = [];
|
|
1021
|
+
for (var z = limits.max_zoom; z > limits.min_zoom; z--) {
|
|
1022
|
+
var parentTileHash = {};
|
|
1023
|
+
var parentTiles = [];
|
|
1024
|
+
for (var i = 0; i < tiles.length; i++) {
|
|
1025
|
+
var t = tiles[i];
|
|
1026
|
+
if (t[0] % 2 === 0 && t[1] % 2 === 0) {
|
|
1027
|
+
var id2 = toID(t[0] + 1, t[1], z),
|
|
1028
|
+
id3 = toID(t[0], t[1] + 1, z),
|
|
1029
|
+
id4 = toID(t[0] + 1, t[1] + 1, z);
|
|
1030
|
+
if (tileHash[id2] && tileHash[id3] && tileHash[id4]) {
|
|
1031
|
+
tileHash[toID(t[0], t[1], t[2])] = false;
|
|
1032
|
+
tileHash[id2] = false;
|
|
1033
|
+
tileHash[id3] = false;
|
|
1034
|
+
tileHash[id4] = false;
|
|
1035
|
+
var parentTile = [t[0] / 2, t[1] / 2, z - 1];
|
|
1036
|
+
if (z - 1 === limits.min_zoom) mergedTiles.push(parentTile);else {
|
|
1037
|
+
parentTileHash[toID(t[0] / 2, t[1] / 2, z - 1)] = true;
|
|
1038
|
+
parentTiles.push(parentTile);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
for (i = 0; i < tiles.length; i++) {
|
|
1044
|
+
t = tiles[i];
|
|
1045
|
+
if (tileHash[toID(t[0], t[1], t[2])]) mergedTiles.push(t);
|
|
1046
|
+
}
|
|
1047
|
+
tileHash = parentTileHash;
|
|
1048
|
+
tiles = parentTiles;
|
|
1049
|
+
}
|
|
1050
|
+
return mergedTiles;
|
|
1051
|
+
}
|
|
1052
|
+
function polygonCover(tileHash, tileArray, geom, zoom) {
|
|
1053
|
+
var intersections = [];
|
|
1054
|
+
for (var i = 0; i < geom.length; i++) {
|
|
1055
|
+
var ring = [];
|
|
1056
|
+
lineCover(tileHash, geom[i], zoom, ring);
|
|
1057
|
+
for (var j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
|
|
1058
|
+
var m = (j + 1) % len;
|
|
1059
|
+
var y = ring[j][1];
|
|
1060
|
+
|
|
1061
|
+
// add interesction if it's not local extremum or duplicate
|
|
1062
|
+
if ((y > ring[k][1] || y > ring[m][1]) && (
|
|
1063
|
+
// not local minimum
|
|
1064
|
+
y < ring[k][1] || y < ring[m][1]) &&
|
|
1065
|
+
// not local maximum
|
|
1066
|
+
y !== ring[m][1]) intersections.push(ring[j]);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
intersections.sort(compareTiles); // sort by y, then x
|
|
1070
|
+
|
|
1071
|
+
for (i = 0; i < intersections.length; i += 2) {
|
|
1072
|
+
// fill tiles between pairs of intersections
|
|
1073
|
+
y = intersections[i][1];
|
|
1074
|
+
for (var x = intersections[i][0] + 1; x < intersections[i + 1][0]; x++) {
|
|
1075
|
+
var id = toID(x, y, zoom);
|
|
1076
|
+
if (!tileHash[id]) {
|
|
1077
|
+
tileArray.push([x, y, zoom]);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
function compareTiles(a, b) {
|
|
1083
|
+
return a[1] - b[1] || a[0] - b[0];
|
|
1084
|
+
}
|
|
1085
|
+
function lineCover(tileHash, coords, maxZoom, ring) {
|
|
1086
|
+
var prevX, prevY;
|
|
1087
|
+
for (var i = 0; i < coords.length - 1; i++) {
|
|
1088
|
+
var start = tilebelt.pointToTileFraction(coords[i][0], coords[i][1], maxZoom),
|
|
1089
|
+
stop = tilebelt.pointToTileFraction(coords[i + 1][0], coords[i + 1][1], maxZoom),
|
|
1090
|
+
x0 = start[0],
|
|
1091
|
+
y0 = start[1],
|
|
1092
|
+
x1 = stop[0],
|
|
1093
|
+
y1 = stop[1],
|
|
1094
|
+
dx = x1 - x0,
|
|
1095
|
+
dy = y1 - y0;
|
|
1096
|
+
if (dy === 0 && dx === 0) continue;
|
|
1097
|
+
var sx = dx > 0 ? 1 : -1,
|
|
1098
|
+
sy = dy > 0 ? 1 : -1,
|
|
1099
|
+
x = Math.floor(x0),
|
|
1100
|
+
y = Math.floor(y0),
|
|
1101
|
+
tMaxX = dx === 0 ? Infinity : Math.abs(((dx > 0 ? 1 : 0) + x - x0) / dx),
|
|
1102
|
+
tMaxY = dy === 0 ? Infinity : Math.abs(((dy > 0 ? 1 : 0) + y - y0) / dy),
|
|
1103
|
+
tdx = Math.abs(sx / dx),
|
|
1104
|
+
tdy = Math.abs(sy / dy);
|
|
1105
|
+
if (x !== prevX || y !== prevY) {
|
|
1106
|
+
tileHash[toID(x, y, maxZoom)] = true;
|
|
1107
|
+
if (ring && y !== prevY) ring.push([x, y]);
|
|
1108
|
+
prevX = x;
|
|
1109
|
+
prevY = y;
|
|
1110
|
+
}
|
|
1111
|
+
while (tMaxX < 1 || tMaxY < 1) {
|
|
1112
|
+
if (tMaxX < tMaxY) {
|
|
1113
|
+
tMaxX += tdx;
|
|
1114
|
+
x += sx;
|
|
1115
|
+
} else {
|
|
1116
|
+
tMaxY += tdy;
|
|
1117
|
+
y += sy;
|
|
1118
|
+
}
|
|
1119
|
+
tileHash[toID(x, y, maxZoom)] = true;
|
|
1120
|
+
if (ring && y !== prevY) ring.push([x, y]);
|
|
1121
|
+
prevX = x;
|
|
1122
|
+
prevY = y;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (ring && y === ring[0][1]) ring.pop();
|
|
1126
|
+
}
|
|
1127
|
+
function appendHashTiles(hash, tiles) {
|
|
1128
|
+
var keys = Object.keys(hash);
|
|
1129
|
+
for (var i = 0; i < keys.length; i++) {
|
|
1130
|
+
tiles.push(fromID(+keys[i]));
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
function toID(x, y, z) {
|
|
1134
|
+
var dim = 2 * (1 << z);
|
|
1135
|
+
return (dim * y + x) * 32 + z;
|
|
1136
|
+
}
|
|
1137
|
+
function fromID(id) {
|
|
1138
|
+
var z = id % 32,
|
|
1139
|
+
dim = 2 * (1 << z),
|
|
1140
|
+
xy = (id - z) / 32,
|
|
1141
|
+
x = xy % dim,
|
|
1142
|
+
y = (xy - x) / dim % dim;
|
|
1143
|
+
return [x, y, z];
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const B = [0x5555555555555555n, 0x3333333333333333n, 0x0f0f0f0f0f0f0f0fn, 0x00ff00ff00ff00ffn, 0x0000ffff0000ffffn, 0x00000000ffffffffn];
|
|
1147
|
+
const S = [0n, 1n, 2n, 4n, 8n, 16n];
|
|
1148
|
+
function tileToCell(tile) {
|
|
1149
|
+
if (tile.z < 0 || tile.z > 26) {
|
|
1150
|
+
throw new Error('Wrong zoom');
|
|
1151
|
+
}
|
|
1152
|
+
const z = BigInt(tile.z);
|
|
1153
|
+
let x = BigInt(tile.x) << 32n - z;
|
|
1154
|
+
let y = BigInt(tile.y) << 32n - z;
|
|
1155
|
+
for (let i = 0; i < 5; i++) {
|
|
1156
|
+
const s = S[5 - i];
|
|
1157
|
+
const b = B[4 - i];
|
|
1158
|
+
x = (x | x << s) & b;
|
|
1159
|
+
y = (y | y << s) & b;
|
|
1160
|
+
}
|
|
1161
|
+
const quadbin = 0x4000000000000000n | 1n << 59n |
|
|
1162
|
+
// | (mode << 59) | (mode_dep << 57)
|
|
1163
|
+
z << 52n | (x | y << 1n) >> 12n | 0xfffffffffffffn >> z * 2n;
|
|
1164
|
+
return quadbin;
|
|
1165
|
+
}
|
|
1166
|
+
function getResolution$1(quadbin) {
|
|
1167
|
+
return quadbin >> 52n & 0x1fn;
|
|
1168
|
+
}
|
|
1169
|
+
function geometryToCells(geometry, resolution) {
|
|
1170
|
+
const zoom = Number(resolution);
|
|
1171
|
+
return tiles(geometry, {
|
|
1172
|
+
min_zoom: zoom,
|
|
1173
|
+
max_zoom: zoom
|
|
1174
|
+
}).map(([x, y, z]) => tileToCell({
|
|
1175
|
+
x,
|
|
1176
|
+
y,
|
|
1177
|
+
z
|
|
1178
|
+
}));
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
function tileFeaturesSpatialIndex(_ref) {
|
|
1182
|
+
let {
|
|
1183
|
+
tiles,
|
|
1184
|
+
spatialFilter,
|
|
1185
|
+
spatialDataColumn,
|
|
1186
|
+
spatialDataType
|
|
1187
|
+
} = _ref;
|
|
1188
|
+
const map = new Map();
|
|
1189
|
+
const spatialIndex = getSpatialIndex(spatialDataType);
|
|
1190
|
+
const resolution = getResolution(tiles, spatialIndex);
|
|
1191
|
+
const spatialIndexIDName = spatialDataColumn ? spatialDataColumn : spatialIndex;
|
|
1192
|
+
if (!resolution) {
|
|
1193
|
+
return [];
|
|
1194
|
+
}
|
|
1195
|
+
const cells = getCellsCoverGeometry(spatialFilter, spatialIndex, resolution);
|
|
1196
|
+
if (!cells?.length) {
|
|
1197
|
+
return [];
|
|
1198
|
+
}
|
|
1199
|
+
// We transform cells to Set to improve the performace
|
|
1200
|
+
const cellsSet = new Set(cells);
|
|
1201
|
+
for (const tile of tiles) {
|
|
1202
|
+
if (tile.isVisible === false || !tile.data) {
|
|
1203
|
+
continue;
|
|
1204
|
+
}
|
|
1205
|
+
tile.data.forEach(d => {
|
|
1206
|
+
if (cellsSet.has(d.id)) {
|
|
1207
|
+
map.set(d.id, {
|
|
1208
|
+
...d.properties,
|
|
1209
|
+
[spatialIndexIDName]: d.id
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
return Array.from(map.values());
|
|
1215
|
+
}
|
|
1216
|
+
function getResolution(tiles, spatialIndex) {
|
|
1217
|
+
const data = tiles.find(tile => tile.data?.length)?.data;
|
|
1218
|
+
if (!data) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (spatialIndex === exports.SpatialIndex.QUADBIN) {
|
|
1222
|
+
return Number(getResolution$1(data[0].id));
|
|
1223
|
+
}
|
|
1224
|
+
if (spatialIndex === exports.SpatialIndex.H3) {
|
|
1225
|
+
return h3Js.getResolution(data[0].id);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
const bboxWest = [-180, -90, 0, 90];
|
|
1229
|
+
const bboxEast = [0, -90, 180, 90];
|
|
1230
|
+
function getCellsCoverGeometry(geometry, spatialIndex, resolution) {
|
|
1231
|
+
if (spatialIndex === exports.SpatialIndex.QUADBIN) {
|
|
1232
|
+
// @ts-expect-error TODO: Probably ought to be stricter about number vs. bigint types in this file.
|
|
1233
|
+
return geometryToCells(geometry, resolution);
|
|
1234
|
+
}
|
|
1235
|
+
if (spatialIndex === exports.SpatialIndex.H3) {
|
|
1236
|
+
// The current H3 polyfill algorithm can't deal with polygon segments of greater than 180 degrees longitude
|
|
1237
|
+
// so we clip the geometry to be sure that none of them is greater than 180 degrees
|
|
1238
|
+
// https://github.com/uber/h3-js/issues/24#issuecomment-431893796
|
|
1239
|
+
return h3Js.polygonToCells(bboxClip(geometry, bboxWest).geometry.coordinates, resolution, true).concat(h3Js.polygonToCells(bboxClip(geometry, bboxEast).geometry.coordinates, resolution, true));
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
function getSpatialIndex(spatialDataType) {
|
|
1243
|
+
switch (spatialDataType) {
|
|
1244
|
+
case 'h3':
|
|
1245
|
+
return exports.SpatialIndex.H3;
|
|
1246
|
+
case 'quadbin':
|
|
1247
|
+
return exports.SpatialIndex.QUADBIN;
|
|
1248
|
+
default:
|
|
1249
|
+
throw new Error('Unexpected spatial data type');
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Current version of @carto/api-client.
|
|
1255
|
+
* @internal
|
|
1256
|
+
*/
|
|
1257
|
+
/** @internal */
|
|
1258
|
+
const V3_MINOR_VERSION = '3.4';
|
|
1259
|
+
/** @privateRemarks Source: @carto/constants, @deck.gl/carto */
|
|
1260
|
+
const DEFAULT_GEO_COLUMN = 'geom';
|
|
1261
|
+
/**
|
|
1262
|
+
* Fastly default limit is 8192; leave some padding.
|
|
1263
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
1264
|
+
*/
|
|
1265
|
+
const DEFAULT_MAX_LENGTH_URL = 7000;
|
|
1266
|
+
/** @privateRemarks Source: @deck.gl/carto */
|
|
1267
|
+
const DEFAULT_TILE_RESOLUTION = 0.5;
|
|
1268
|
+
/**
|
|
1269
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
1270
|
+
* @internal
|
|
1271
|
+
*/
|
|
1272
|
+
const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
1273
|
+
/**
|
|
1274
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
1275
|
+
* @internal
|
|
1276
|
+
*/
|
|
1277
|
+
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
1278
|
+
|
|
1279
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1280
|
+
function tileFeatures(_ref) {
|
|
1281
|
+
let {
|
|
1282
|
+
tiles,
|
|
1283
|
+
spatialFilter,
|
|
1284
|
+
uniqueIdProperty,
|
|
1285
|
+
tileFormat,
|
|
1286
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1287
|
+
spatialDataType,
|
|
1288
|
+
options = {}
|
|
1289
|
+
} = _ref;
|
|
1290
|
+
if (spatialDataType !== 'geo') {
|
|
1291
|
+
return tileFeaturesSpatialIndex({
|
|
1292
|
+
tiles: tiles,
|
|
1293
|
+
spatialFilter,
|
|
1294
|
+
spatialDataColumn,
|
|
1295
|
+
spatialDataType
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
return tileFeaturesGeometries({
|
|
1299
|
+
tiles,
|
|
1300
|
+
tileFormat,
|
|
1301
|
+
spatialFilter,
|
|
1302
|
+
uniqueIdProperty,
|
|
1303
|
+
options
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* deck.gl's DataFilterExtension supports GPU filtering with 1–4 values. We
|
|
1309
|
+
* allocate filters[0] to generic filters and filters[1] to time filters.
|
|
1310
|
+
*
|
|
1311
|
+
* getFilterValue() _must_ return an array of the same size as the filterSize
|
|
1312
|
+
* used to initialize the DataFilterExtension. We document that users must use
|
|
1313
|
+
* filterSize=4 for compatibility with @link {getDataFilterExtensionProps}.
|
|
1314
|
+
*/
|
|
1315
|
+
const DEFAULT_FILTER_SIZE = 4;
|
|
1316
|
+
/**
|
|
1317
|
+
* Creates props for DataFilterExtension, from `@deck.gl/extensions`, given
|
|
1318
|
+
* a set of filters. Requires that DataFilterExtension is initialized with
|
|
1319
|
+
* filterSize=4, where the CARTO filters will occupy the first two slots.
|
|
1320
|
+
*
|
|
1321
|
+
* @example To create a deck.gl layer with GPU data filtering:
|
|
1322
|
+
* ```typescript
|
|
1323
|
+
* import {DataFilterExtension} from '@deck.gl/extensions';
|
|
1324
|
+
* import {VectorTileLayer} from '@deck.gl/layers';
|
|
1325
|
+
* import {getDataFilterExtensionProps} from '@carto/api-client';
|
|
1326
|
+
*
|
|
1327
|
+
* const layer = new VectorTileLayer({
|
|
1328
|
+
* data: data,
|
|
1329
|
+
* extensions: [new DataFilterExtension({filterSize: 4})],
|
|
1330
|
+
* ...getDataFilterExtensionProps(filters),
|
|
1331
|
+
* });
|
|
1332
|
+
* ```
|
|
1333
|
+
*/
|
|
1334
|
+
function getDataFilterExtensionProps(filters, filtersLogicalOperator) {
|
|
1335
|
+
const {
|
|
1336
|
+
filtersWithoutTimeType,
|
|
1337
|
+
timeColumn,
|
|
1338
|
+
timeFilter
|
|
1339
|
+
} = getFiltersByType(filters);
|
|
1340
|
+
return {
|
|
1341
|
+
filterRange: getFilterRange(timeFilter, DEFAULT_FILTER_SIZE),
|
|
1342
|
+
updateTriggers: getUpdateTriggers(filtersWithoutTimeType, timeColumn, timeFilter),
|
|
1343
|
+
getFilterValue: getFilterValue(filtersWithoutTimeType, timeColumn, timeFilter, DEFAULT_FILTER_SIZE, filtersLogicalOperator)
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
/** @internal */
|
|
1347
|
+
function getFiltersByType(filters) {
|
|
1348
|
+
const filtersWithoutTimeType = {};
|
|
1349
|
+
let timeColumn = null;
|
|
1350
|
+
let timeFilter = null;
|
|
1351
|
+
for (const [column, columnData] of Object.entries(filters)) {
|
|
1352
|
+
for (const [type, typeData] of Object.entries(columnData)) {
|
|
1353
|
+
if (type === exports.FilterType.TIME) {
|
|
1354
|
+
timeColumn = column;
|
|
1355
|
+
timeFilter = typeData;
|
|
1356
|
+
} else {
|
|
1357
|
+
filtersWithoutTimeType[column] = {
|
|
1358
|
+
[type]: typeData
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return {
|
|
1364
|
+
filtersWithoutTimeType,
|
|
1365
|
+
timeColumn,
|
|
1366
|
+
timeFilter
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
/** @internal */
|
|
1370
|
+
function getFilterRange(timeFilter, filterSize) {
|
|
1371
|
+
const result = Array(filterSize).fill([0, 0]);
|
|
1372
|
+
// According to getFilterValue all filters are resolved as 0 or 1 in the first position of the array
|
|
1373
|
+
// except the time filter value that is resolved with the real value of the feature in the second position of the array
|
|
1374
|
+
result[0] = [1, 1];
|
|
1375
|
+
if (timeFilter) {
|
|
1376
|
+
const offsetBy = timeFilter.params?.offsetBy || 0;
|
|
1377
|
+
result[1] = timeFilter.values[0].map(v => v - offsetBy);
|
|
1378
|
+
}
|
|
1379
|
+
return result;
|
|
1380
|
+
}
|
|
1381
|
+
/** @internal */
|
|
1382
|
+
function getUpdateTriggers(filtersWithoutTimeType, timeColumn, timeFilter) {
|
|
1383
|
+
const result = {
|
|
1384
|
+
...filtersWithoutTimeType
|
|
1385
|
+
};
|
|
1386
|
+
// We don't want to change the layer UpdateTriggers every time that the time filter changes
|
|
1387
|
+
// because this filter is changed by the time series widget during its animation
|
|
1388
|
+
// so we remove the time filter value from the `updateTriggers`
|
|
1389
|
+
if (timeColumn && timeFilter) {
|
|
1390
|
+
result[timeColumn] = {
|
|
1391
|
+
...result[timeColumn],
|
|
1392
|
+
offsetBy: timeFilter.params?.offsetBy,
|
|
1393
|
+
[exports.FilterType.TIME]: {} // Allows working with other filters, without an impact on performance.
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
return {
|
|
1397
|
+
getFilterValue: JSON.stringify(result)
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
/** @internal */
|
|
1401
|
+
function getFilterValue(filtersWithoutTimeType, timeColumn, timeFilter, filterSize, filtersLogicalOperator) {
|
|
1402
|
+
const result = Array(filterSize).fill(0);
|
|
1403
|
+
const featureFilter = _buildFeatureFilter({
|
|
1404
|
+
filters: filtersWithoutTimeType,
|
|
1405
|
+
type: 'number',
|
|
1406
|
+
filtersLogicalOperator
|
|
1407
|
+
});
|
|
1408
|
+
// We evaluate all filters except the time filter using _buildFeatureFilter function.
|
|
1409
|
+
// For the time filter, we return the value of the feature and we will change the getFilterRange result
|
|
1410
|
+
// every time this filter changes
|
|
1411
|
+
return feature => {
|
|
1412
|
+
result[0] = featureFilter(feature);
|
|
1413
|
+
if (timeColumn && timeFilter) {
|
|
1414
|
+
const offsetBy = timeFilter.params?.offsetBy || 0;
|
|
1415
|
+
const f = feature.properties || feature;
|
|
1416
|
+
result[1] = f[timeColumn] - offsetBy;
|
|
1417
|
+
}
|
|
1418
|
+
return result;
|
|
1419
|
+
};
|
|
29
1420
|
}
|
|
30
1421
|
|
|
31
|
-
/**
|
|
32
|
-
* Defines a comparator used when matching a column's values against given filter values.
|
|
33
|
-
*
|
|
34
|
-
* Example:
|
|
35
|
-
*
|
|
36
|
-
* ```javascript
|
|
37
|
-
* import { FilterType } from '@carto/api-client';
|
|
38
|
-
* const filters = {
|
|
39
|
-
* column_name: { [FilterType.IN]: { values: ['a', 'b', 'c'] } }
|
|
40
|
-
* };
|
|
41
|
-
* ```
|
|
42
|
-
*
|
|
43
|
-
* @internalRemarks Source: @carto/react-api, @deck.gl/carto
|
|
44
|
-
*/
|
|
45
|
-
exports.FilterType = void 0;
|
|
46
|
-
(function (FilterType) {
|
|
47
|
-
FilterType["IN"] = "in";
|
|
48
|
-
/** [a, b] both are included. */
|
|
49
|
-
FilterType["BETWEEN"] = "between";
|
|
50
|
-
/** [a, b) a is included, b is not. */
|
|
51
|
-
FilterType["CLOSED_OPEN"] = "closed_open";
|
|
52
|
-
FilterType["TIME"] = "time";
|
|
53
|
-
FilterType["STRING_SEARCH"] = "stringSearch";
|
|
54
|
-
})(exports.FilterType || (exports.FilterType = {}));
|
|
55
|
-
/** @internalRemarks Source: @carto/constants */
|
|
56
|
-
exports.ApiVersion = void 0;
|
|
57
|
-
(function (ApiVersion) {
|
|
58
|
-
ApiVersion["V1"] = "v1";
|
|
59
|
-
ApiVersion["V2"] = "v2";
|
|
60
|
-
ApiVersion["V3"] = "v3";
|
|
61
|
-
})(exports.ApiVersion || (exports.ApiVersion = {}));
|
|
62
|
-
/** @internalRemarks Source: @carto/constants, @deck.gl/carto */
|
|
63
|
-
const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
|
|
64
|
-
|
|
65
1422
|
const FILTER_TYPES = new Set(Object.values(exports.FilterType));
|
|
66
1423
|
const isFilterType = type => FILTER_TYPES.has(type);
|
|
67
1424
|
/**
|
|
@@ -88,7 +1445,7 @@ function getApplicableFilters(owner, filters) {
|
|
|
88
1445
|
* Due to each data warehouse having its own behavior with columns,
|
|
89
1446
|
* we need to normalize them and transform every key to lowercase.
|
|
90
1447
|
*
|
|
91
|
-
* @
|
|
1448
|
+
* @privateRemarks Source: @carto/react-widgets
|
|
92
1449
|
* @internal
|
|
93
1450
|
*/
|
|
94
1451
|
function normalizeObjectKeys(el) {
|
|
@@ -103,14 +1460,14 @@ function normalizeObjectKeys(el) {
|
|
|
103
1460
|
return acc;
|
|
104
1461
|
}, {});
|
|
105
1462
|
}
|
|
106
|
-
/** @
|
|
1463
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
107
1464
|
function assert(condition, message) {
|
|
108
1465
|
if (!condition) {
|
|
109
1466
|
throw new Error(message);
|
|
110
1467
|
}
|
|
111
1468
|
}
|
|
112
1469
|
/**
|
|
113
|
-
* @
|
|
1470
|
+
* @privateRemarks Source: @carto/react-core
|
|
114
1471
|
* @internal
|
|
115
1472
|
*/
|
|
116
1473
|
class InvalidColumnError extends Error {
|
|
@@ -254,7 +1611,7 @@ function createPolygonSpatialFilter(spatialFilter) {
|
|
|
254
1611
|
* Check if a viewport is large enough to represent a global coverage.
|
|
255
1612
|
* In this case the spatial filter parameter for widget calculation is removed.
|
|
256
1613
|
*
|
|
257
|
-
* @
|
|
1614
|
+
* @privateRemarks Source: @carto/react-core
|
|
258
1615
|
*/
|
|
259
1616
|
function _isGlobalViewport(viewport) {
|
|
260
1617
|
const [minx, miny, maxx, maxy] = viewport;
|
|
@@ -267,7 +1624,7 @@ function _isGlobalViewport(viewport) {
|
|
|
267
1624
|
*
|
|
268
1625
|
* It results in a Polygon or MultiPolygon strictly inside the validity range.
|
|
269
1626
|
*
|
|
270
|
-
* @
|
|
1627
|
+
* @privateRemarks Source: @carto/react-core
|
|
271
1628
|
*/
|
|
272
1629
|
function _normalizeGeometry(geometry) {
|
|
273
1630
|
const WORLD = [-180, -90, +180, +90];
|
|
@@ -293,17 +1650,17 @@ function _normalizeGeometry(geometry) {
|
|
|
293
1650
|
}
|
|
294
1651
|
return result;
|
|
295
1652
|
}
|
|
296
|
-
/** @
|
|
1653
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
297
1654
|
function _cleanPolygonCoords(cc) {
|
|
298
1655
|
const coords = cc.filter(c => c.length > 0);
|
|
299
1656
|
return coords.length > 0 ? coords : null;
|
|
300
1657
|
}
|
|
301
|
-
/** @
|
|
1658
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
302
1659
|
function _cleanMultiPolygonCoords(ccc) {
|
|
303
1660
|
const coords = ccc.map(_cleanPolygonCoords).filter(cc => cc);
|
|
304
1661
|
return coords.length > 0 ? coords : null;
|
|
305
1662
|
}
|
|
306
|
-
/** @
|
|
1663
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
307
1664
|
function _clean(geometry) {
|
|
308
1665
|
if (!geometry) {
|
|
309
1666
|
return null;
|
|
@@ -318,19 +1675,19 @@ function _clean(geometry) {
|
|
|
318
1675
|
}
|
|
319
1676
|
return null;
|
|
320
1677
|
}
|
|
321
|
-
/** @
|
|
1678
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
322
1679
|
function _txContourCoords(cc, distance) {
|
|
323
1680
|
return cc.map(c => [c[0] + distance, c[1]]);
|
|
324
1681
|
}
|
|
325
|
-
/** @
|
|
1682
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
326
1683
|
function _txPolygonCoords(ccc, distance) {
|
|
327
1684
|
return ccc.map(cc => _txContourCoords(cc, distance));
|
|
328
1685
|
}
|
|
329
|
-
/** @
|
|
1686
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
330
1687
|
function _txMultiPolygonCoords(cccc, distance) {
|
|
331
1688
|
return cccc.map(ccc => _txPolygonCoords(ccc, distance));
|
|
332
1689
|
}
|
|
333
|
-
/** @
|
|
1690
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
334
1691
|
function _tx(geometry, distance) {
|
|
335
1692
|
if (geometry && invariant.getType(geometry) === 'Polygon') {
|
|
336
1693
|
const coords = _txPolygonCoords(geometry.coordinates, distance);
|
|
@@ -349,32 +1706,6 @@ function _isMultiPolygon(geometry) {
|
|
|
349
1706
|
return invariant.getType(geometry) === 'MultiPolygon';
|
|
350
1707
|
}
|
|
351
1708
|
|
|
352
|
-
/**
|
|
353
|
-
* Current version of @carto/api-client.
|
|
354
|
-
* @internal
|
|
355
|
-
*/
|
|
356
|
-
/** @internal */
|
|
357
|
-
const V3_MINOR_VERSION = '3.4';
|
|
358
|
-
/** @internalRemarks Source: @carto/constants, @deck.gl/carto */
|
|
359
|
-
const DEFAULT_GEO_COLUMN = 'geom';
|
|
360
|
-
/**
|
|
361
|
-
* Fastly default limit is 8192; leave some padding.
|
|
362
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
363
|
-
*/
|
|
364
|
-
const DEFAULT_MAX_LENGTH_URL = 7000;
|
|
365
|
-
/** @internalRemarks Source: @deck.gl/carto */
|
|
366
|
-
const DEFAULT_TILE_RESOLUTION = 0.5;
|
|
367
|
-
/**
|
|
368
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
369
|
-
* @internal
|
|
370
|
-
*/
|
|
371
|
-
const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
372
|
-
/**
|
|
373
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
374
|
-
* @internal
|
|
375
|
-
*/
|
|
376
|
-
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
377
|
-
|
|
378
1709
|
// deck.gl
|
|
379
1710
|
// SPDX-License-Identifier: MIT
|
|
380
1711
|
// Copyright (c) vis.gl contributors
|
|
@@ -767,12 +2098,106 @@ const boundaryTableSource = function (options) {
|
|
|
767
2098
|
}
|
|
768
2099
|
};
|
|
769
2100
|
|
|
2101
|
+
const DEFAULT_TILE_SIZE = 512;
|
|
2102
|
+
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
2103
|
+
function getSpatialFiltersResolution(source, viewState) {
|
|
2104
|
+
const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
|
|
2105
|
+
const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
|
|
2106
|
+
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
2107
|
+
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
2108
|
+
if (source.spatialDataType === 'h3') {
|
|
2109
|
+
const tileSize = DEFAULT_TILE_SIZE;
|
|
2110
|
+
const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref => {
|
|
2111
|
+
let [zoom] = _ref;
|
|
2112
|
+
return zoom === currentZoomInt;
|
|
2113
|
+
})?.[1] ?? Math.max(0, currentZoomInt - 3);
|
|
2114
|
+
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
2115
|
+
const hexagonResolution = _getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
2116
|
+
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
2117
|
+
}
|
|
2118
|
+
if (source.spatialDataType === 'quadbin') {
|
|
2119
|
+
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
2120
|
+
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
2121
|
+
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
2122
|
+
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
2123
|
+
}
|
|
2124
|
+
return undefined;
|
|
2125
|
+
}
|
|
2126
|
+
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]];
|
|
2127
|
+
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
2128
|
+
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
2129
|
+
const BIAS = 2;
|
|
2130
|
+
/**
|
|
2131
|
+
* Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
2132
|
+
* a H3 resolution such that the screen space size of the hexagons is
|
|
2133
|
+
* "similar" to the given tileSize on screen. Intended for use with deck.gl.
|
|
2134
|
+
* @internal
|
|
2135
|
+
*/
|
|
2136
|
+
function _getHexagonResolution(viewport, tileSize) {
|
|
2137
|
+
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
2138
|
+
// expressed as an offset to the viewport zoom.
|
|
2139
|
+
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
2140
|
+
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
2141
|
+
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
2142
|
+
// Clip and bias
|
|
2143
|
+
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
/**
|
|
2147
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
2148
|
+
*
|
|
2149
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
2150
|
+
*/
|
|
2151
|
+
class WidgetSource {
|
|
2152
|
+
constructor(props) {
|
|
2153
|
+
this.props = void 0;
|
|
2154
|
+
this.props = {
|
|
2155
|
+
...WidgetSource.defaultProps,
|
|
2156
|
+
clientId: getClient(),
|
|
2157
|
+
// Refresh clientId, default may have changed.
|
|
2158
|
+
...props
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
_getModelSource(filters, filterOwner) {
|
|
2162
|
+
const props = this.props;
|
|
2163
|
+
return {
|
|
2164
|
+
apiVersion: props.apiVersion,
|
|
2165
|
+
apiBaseUrl: props.apiBaseUrl,
|
|
2166
|
+
clientId: props.clientId,
|
|
2167
|
+
accessToken: props.accessToken,
|
|
2168
|
+
connectionName: props.connectionName,
|
|
2169
|
+
filters: getApplicableFilters(filterOwner, filters || props.filters),
|
|
2170
|
+
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
2171
|
+
spatialDataType: props.spatialDataType,
|
|
2172
|
+
spatialDataColumn: props.spatialDataColumn,
|
|
2173
|
+
dataResolution: props.dataResolution
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
_getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
|
|
2177
|
+
// spatialFiltersResolution applies only to spatial index sources.
|
|
2178
|
+
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
if (!referenceViewState) {
|
|
2182
|
+
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
2183
|
+
}
|
|
2184
|
+
return getSpatialFiltersResolution(source, referenceViewState);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
WidgetSource.defaultProps = {
|
|
2188
|
+
apiVersion: exports.ApiVersion.V3,
|
|
2189
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
2190
|
+
clientId: getClient(),
|
|
2191
|
+
filters: {},
|
|
2192
|
+
filtersLogicalOperator: 'and'
|
|
2193
|
+
};
|
|
2194
|
+
|
|
770
2195
|
/**
|
|
771
2196
|
* Return more descriptive error from API
|
|
772
|
-
* @
|
|
2197
|
+
* @privateRemarks Source: @carto/react-api
|
|
773
2198
|
*/
|
|
774
2199
|
|
|
775
|
-
/** @
|
|
2200
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
776
2201
|
|
|
777
2202
|
function _catch(body, recover) {
|
|
778
2203
|
try {
|
|
@@ -812,13 +2237,14 @@ const makeCall = function (_ref2) {
|
|
|
812
2237
|
Authorization: `Bearer ${accessToken}`,
|
|
813
2238
|
...(isPost && {
|
|
814
2239
|
'Content-Type': 'application/json'
|
|
815
|
-
})
|
|
2240
|
+
}),
|
|
2241
|
+
...opts.headers
|
|
816
2242
|
},
|
|
817
2243
|
...(isPost && {
|
|
818
2244
|
method: opts?.method,
|
|
819
2245
|
body: opts?.body
|
|
820
2246
|
}),
|
|
821
|
-
signal: opts?.
|
|
2247
|
+
signal: opts?.signal,
|
|
822
2248
|
...opts?.otherOptions
|
|
823
2249
|
})).then(function (_fetch) {
|
|
824
2250
|
response = _fetch;
|
|
@@ -852,12 +2278,11 @@ function dealWithApiError(_ref) {
|
|
|
852
2278
|
case 403:
|
|
853
2279
|
throw new Error('Forbidden access to the requested data');
|
|
854
2280
|
default:
|
|
855
|
-
|
|
856
|
-
throw new Error(msg);
|
|
2281
|
+
throw new Error(data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify(data?.hint || data.error?.[0]));
|
|
857
2282
|
}
|
|
858
2283
|
}
|
|
859
2284
|
|
|
860
|
-
/** @
|
|
2285
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
861
2286
|
const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'pick', 'timeseries', 'range', 'scatterplot', 'table'];
|
|
862
2287
|
const {
|
|
863
2288
|
V3
|
|
@@ -865,7 +2290,7 @@ const {
|
|
|
865
2290
|
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
866
2291
|
/**
|
|
867
2292
|
* Execute a SQL model request.
|
|
868
|
-
* @
|
|
2293
|
+
* @privateRemarks Source: @carto/react-api
|
|
869
2294
|
*/
|
|
870
2295
|
function executeModel(props) {
|
|
871
2296
|
assert(props.source, 'executeModel: missing source');
|
|
@@ -961,105 +2386,22 @@ function objectToURLSearchParams(object) {
|
|
|
961
2386
|
return params;
|
|
962
2387
|
}
|
|
963
2388
|
|
|
964
|
-
const DEFAULT_TILE_SIZE = 512;
|
|
965
|
-
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
966
|
-
function getSpatialFiltersResolution(source, viewState) {
|
|
967
|
-
const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
|
|
968
|
-
const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
|
|
969
|
-
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
970
|
-
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
971
|
-
if (source.spatialDataType === 'h3') {
|
|
972
|
-
const tileSize = DEFAULT_TILE_SIZE;
|
|
973
|
-
const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref => {
|
|
974
|
-
let [zoom] = _ref;
|
|
975
|
-
return zoom === currentZoomInt;
|
|
976
|
-
})?.[1] ?? Math.max(0, currentZoomInt - 3);
|
|
977
|
-
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
978
|
-
const hexagonResolution = _getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
979
|
-
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
980
|
-
}
|
|
981
|
-
if (source.spatialDataType === 'quadbin') {
|
|
982
|
-
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
983
|
-
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
984
|
-
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
985
|
-
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
986
|
-
}
|
|
987
|
-
return undefined;
|
|
988
|
-
}
|
|
989
|
-
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]];
|
|
990
|
-
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
991
|
-
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
992
|
-
const BIAS = 2;
|
|
993
|
-
/**
|
|
994
|
-
* Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
995
|
-
* a H3 resolution such that the screen space size of the hexagons is
|
|
996
|
-
* "similar" to the given tileSize on screen. Intended for use with deck.gl.
|
|
997
|
-
* @internal
|
|
998
|
-
*/
|
|
999
|
-
function _getHexagonResolution(viewport, tileSize) {
|
|
1000
|
-
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
1001
|
-
// expressed as an offset to the viewport zoom.
|
|
1002
|
-
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
1003
|
-
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
1004
|
-
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
1005
|
-
// Clip and bias
|
|
1006
|
-
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
2389
|
/**
|
|
1010
|
-
* Source for Widget API requests
|
|
2390
|
+
* Source for Widget API requests.
|
|
1011
2391
|
*
|
|
1012
2392
|
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
1013
2393
|
*/
|
|
1014
|
-
class
|
|
1015
|
-
constructor(props) {
|
|
1016
|
-
this.props = void 0;
|
|
1017
|
-
this.props = {
|
|
1018
|
-
...WidgetBaseSource.defaultProps,
|
|
1019
|
-
...props
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
_getModelSource(owner) {
|
|
1023
|
-
const props = this.props;
|
|
1024
|
-
return {
|
|
1025
|
-
apiVersion: props.apiVersion,
|
|
1026
|
-
apiBaseUrl: props.apiBaseUrl,
|
|
1027
|
-
clientId: props.clientId,
|
|
1028
|
-
accessToken: props.accessToken,
|
|
1029
|
-
connectionName: props.connectionName,
|
|
1030
|
-
filters: getApplicableFilters(owner, props.filters),
|
|
1031
|
-
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
1032
|
-
spatialDataType: props.spatialDataType,
|
|
1033
|
-
spatialDataColumn: props.spatialDataColumn,
|
|
1034
|
-
dataResolution: props.dataResolution
|
|
1035
|
-
};
|
|
1036
|
-
}
|
|
1037
|
-
_getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
|
|
1038
|
-
// spatialFiltersResolution applies only to spatial index sources.
|
|
1039
|
-
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
if (!referenceViewState) {
|
|
1043
|
-
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
1044
|
-
}
|
|
1045
|
-
return getSpatialFiltersResolution(source, referenceViewState);
|
|
1046
|
-
}
|
|
1047
|
-
/****************************************************************************
|
|
1048
|
-
* CATEGORIES
|
|
1049
|
-
*/
|
|
1050
|
-
/**
|
|
1051
|
-
* Returns a list of labeled datapoints for categorical data. Suitable for
|
|
1052
|
-
* charts including grouped bar charts, pie charts, and tree charts.
|
|
1053
|
-
*/
|
|
2394
|
+
class WidgetRemoteSource extends WidgetSource {
|
|
1054
2395
|
getCategories(options) {
|
|
1055
2396
|
try {
|
|
1056
2397
|
const _this = this;
|
|
1057
2398
|
const {
|
|
2399
|
+
signal,
|
|
2400
|
+
filters = _this.props.filters,
|
|
1058
2401
|
filterOwner,
|
|
1059
2402
|
spatialFilter,
|
|
1060
2403
|
spatialFiltersMode,
|
|
1061
2404
|
spatialIndexReferenceViewState,
|
|
1062
|
-
abortController,
|
|
1063
2405
|
...params
|
|
1064
2406
|
} = options;
|
|
1065
2407
|
const {
|
|
@@ -1067,7 +2409,7 @@ class WidgetBaseSource {
|
|
|
1067
2409
|
operation,
|
|
1068
2410
|
operationColumn
|
|
1069
2411
|
} = params;
|
|
1070
|
-
const source = _this.getModelSource(filterOwner);
|
|
2412
|
+
const source = _this.getModelSource(filters, filterOwner);
|
|
1071
2413
|
const spatialFiltersResolution = _this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1072
2414
|
return Promise.resolve(executeModel({
|
|
1073
2415
|
model: 'category',
|
|
@@ -1083,33 +2425,25 @@ class WidgetBaseSource {
|
|
|
1083
2425
|
operationColumn: operationColumn || column
|
|
1084
2426
|
},
|
|
1085
2427
|
opts: {
|
|
1086
|
-
|
|
2428
|
+
signal,
|
|
2429
|
+
headers: _this.props.headers
|
|
1087
2430
|
}
|
|
1088
2431
|
}).then(res => normalizeObjectKeys(res.rows)));
|
|
1089
2432
|
} catch (e) {
|
|
1090
2433
|
return Promise.reject(e);
|
|
1091
2434
|
}
|
|
1092
2435
|
}
|
|
1093
|
-
/****************************************************************************
|
|
1094
|
-
* FEATURES
|
|
1095
|
-
*/
|
|
1096
|
-
/**
|
|
1097
|
-
* Given a list of feature IDs (as found in `_carto_feature_id`) returns all
|
|
1098
|
-
* matching features. In datasets containing features with duplicate geometries,
|
|
1099
|
-
* feature IDs may be duplicated (IDs are a hash of geometry) and so more
|
|
1100
|
-
* results may be returned than IDs in the request.
|
|
1101
|
-
* @internal
|
|
1102
|
-
* @experimental
|
|
1103
|
-
*/
|
|
1104
2436
|
getFeatures(options) {
|
|
1105
2437
|
try {
|
|
1106
2438
|
const _this2 = this;
|
|
1107
2439
|
const {
|
|
2440
|
+
abortController,
|
|
2441
|
+
signal = abortController?.signal,
|
|
2442
|
+
filters = _this2.props.filters,
|
|
1108
2443
|
filterOwner,
|
|
1109
2444
|
spatialFilter,
|
|
1110
2445
|
spatialFiltersMode,
|
|
1111
2446
|
spatialIndexReferenceViewState,
|
|
1112
|
-
abortController,
|
|
1113
2447
|
...params
|
|
1114
2448
|
} = options;
|
|
1115
2449
|
const {
|
|
@@ -1120,7 +2454,7 @@ class WidgetBaseSource {
|
|
|
1120
2454
|
limit,
|
|
1121
2455
|
tileResolution
|
|
1122
2456
|
} = params;
|
|
1123
|
-
const source = _this2.getModelSource(filterOwner);
|
|
2457
|
+
const source = _this2.getModelSource(filters, filterOwner);
|
|
1124
2458
|
const spatialFiltersResolution = _this2._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1125
2459
|
return Promise.resolve(executeModel({
|
|
1126
2460
|
model: 'pick',
|
|
@@ -1139,7 +2473,8 @@ class WidgetBaseSource {
|
|
|
1139
2473
|
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
1140
2474
|
},
|
|
1141
2475
|
opts: {
|
|
1142
|
-
|
|
2476
|
+
signal,
|
|
2477
|
+
headers: _this2.props.headers
|
|
1143
2478
|
}
|
|
1144
2479
|
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1145
2480
|
}).then(_ref => {
|
|
@@ -1154,22 +2489,17 @@ class WidgetBaseSource {
|
|
|
1154
2489
|
return Promise.reject(e);
|
|
1155
2490
|
}
|
|
1156
2491
|
}
|
|
1157
|
-
/****************************************************************************
|
|
1158
|
-
* FORMULA
|
|
1159
|
-
*/
|
|
1160
|
-
/**
|
|
1161
|
-
* Returns a scalar numerical statistic over all matching data. Suitable
|
|
1162
|
-
* for 'headline' or 'scorecard' figures such as counts and sums.
|
|
1163
|
-
*/
|
|
1164
2492
|
getFormula(options) {
|
|
1165
2493
|
try {
|
|
1166
2494
|
const _this3 = this;
|
|
1167
2495
|
const {
|
|
2496
|
+
abortController,
|
|
2497
|
+
signal = abortController?.signal,
|
|
2498
|
+
filters = _this3.props.filters,
|
|
1168
2499
|
filterOwner,
|
|
1169
2500
|
spatialFilter,
|
|
1170
2501
|
spatialFiltersMode,
|
|
1171
2502
|
spatialIndexReferenceViewState,
|
|
1172
|
-
abortController,
|
|
1173
2503
|
operationExp,
|
|
1174
2504
|
...params
|
|
1175
2505
|
} = options;
|
|
@@ -1177,7 +2507,7 @@ class WidgetBaseSource {
|
|
|
1177
2507
|
column,
|
|
1178
2508
|
operation
|
|
1179
2509
|
} = params;
|
|
1180
|
-
const source = _this3.getModelSource(filterOwner);
|
|
2510
|
+
const source = _this3.getModelSource(filters, filterOwner);
|
|
1181
2511
|
const spatialFiltersResolution = _this3._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1182
2512
|
return Promise.resolve(executeModel({
|
|
1183
2513
|
model: 'formula',
|
|
@@ -1189,33 +2519,29 @@ class WidgetBaseSource {
|
|
|
1189
2519
|
},
|
|
1190
2520
|
params: {
|
|
1191
2521
|
column: column ?? '*',
|
|
1192
|
-
operation,
|
|
2522
|
+
operation: operation ?? 'count',
|
|
1193
2523
|
operationExp
|
|
1194
2524
|
},
|
|
1195
2525
|
opts: {
|
|
1196
|
-
|
|
2526
|
+
signal,
|
|
2527
|
+
headers: _this3.props.headers
|
|
1197
2528
|
}
|
|
1198
2529
|
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
1199
2530
|
} catch (e) {
|
|
1200
2531
|
return Promise.reject(e);
|
|
1201
2532
|
}
|
|
1202
2533
|
}
|
|
1203
|
-
/****************************************************************************
|
|
1204
|
-
* HISTOGRAM
|
|
1205
|
-
*/
|
|
1206
|
-
/**
|
|
1207
|
-
* Returns a list of labeled datapoints for 'bins' of data defined as ticks
|
|
1208
|
-
* over a numerical range. Suitable for histogram charts.
|
|
1209
|
-
*/
|
|
1210
2534
|
getHistogram(options) {
|
|
1211
2535
|
try {
|
|
1212
2536
|
const _this4 = this;
|
|
1213
2537
|
const {
|
|
2538
|
+
abortController,
|
|
2539
|
+
signal = abortController?.signal,
|
|
2540
|
+
filters = _this4.props.filters,
|
|
1214
2541
|
filterOwner,
|
|
1215
2542
|
spatialFilter,
|
|
1216
2543
|
spatialFiltersMode,
|
|
1217
2544
|
spatialIndexReferenceViewState,
|
|
1218
|
-
abortController,
|
|
1219
2545
|
...params
|
|
1220
2546
|
} = options;
|
|
1221
2547
|
const {
|
|
@@ -1223,7 +2549,7 @@ class WidgetBaseSource {
|
|
|
1223
2549
|
operation,
|
|
1224
2550
|
ticks
|
|
1225
2551
|
} = params;
|
|
1226
|
-
const source = _this4.getModelSource(filterOwner);
|
|
2552
|
+
const source = _this4.getModelSource(filters, filterOwner);
|
|
1227
2553
|
const spatialFiltersResolution = _this4._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1228
2554
|
return Promise.resolve(executeModel({
|
|
1229
2555
|
model: 'histogram',
|
|
@@ -1239,7 +2565,8 @@ class WidgetBaseSource {
|
|
|
1239
2565
|
ticks
|
|
1240
2566
|
},
|
|
1241
2567
|
opts: {
|
|
1242
|
-
|
|
2568
|
+
signal,
|
|
2569
|
+
headers: _this4.props.headers
|
|
1243
2570
|
}
|
|
1244
2571
|
}).then(res => normalizeObjectKeys(res.rows))).then(function (data) {
|
|
1245
2572
|
if (data.length) {
|
|
@@ -1261,29 +2588,23 @@ class WidgetBaseSource {
|
|
|
1261
2588
|
return Promise.reject(e);
|
|
1262
2589
|
}
|
|
1263
2590
|
}
|
|
1264
|
-
/****************************************************************************
|
|
1265
|
-
* RANGE
|
|
1266
|
-
*/
|
|
1267
|
-
/**
|
|
1268
|
-
* Returns a range (min and max) for a numerical column of matching rows.
|
|
1269
|
-
* Suitable for displaying certain 'headline' or 'scorecard' statistics,
|
|
1270
|
-
* or rendering a range slider UI for filtering.
|
|
1271
|
-
*/
|
|
1272
2591
|
getRange(options) {
|
|
1273
2592
|
try {
|
|
1274
2593
|
const _this5 = this;
|
|
1275
2594
|
const {
|
|
2595
|
+
abortController,
|
|
2596
|
+
signal = abortController?.signal,
|
|
2597
|
+
filters = _this5.props.filters,
|
|
1276
2598
|
filterOwner,
|
|
1277
2599
|
spatialFilter,
|
|
1278
2600
|
spatialFiltersMode,
|
|
1279
2601
|
spatialIndexReferenceViewState,
|
|
1280
|
-
abortController,
|
|
1281
2602
|
...params
|
|
1282
2603
|
} = options;
|
|
1283
2604
|
const {
|
|
1284
2605
|
column
|
|
1285
2606
|
} = params;
|
|
1286
|
-
const source = _this5.getModelSource(filterOwner);
|
|
2607
|
+
const source = _this5.getModelSource(filters, filterOwner);
|
|
1287
2608
|
const spatialFiltersResolution = _this5._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1288
2609
|
return Promise.resolve(executeModel({
|
|
1289
2610
|
model: 'range',
|
|
@@ -1297,29 +2618,25 @@ class WidgetBaseSource {
|
|
|
1297
2618
|
column
|
|
1298
2619
|
},
|
|
1299
2620
|
opts: {
|
|
1300
|
-
|
|
2621
|
+
signal,
|
|
2622
|
+
headers: _this5.props.headers
|
|
1301
2623
|
}
|
|
1302
2624
|
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
1303
2625
|
} catch (e) {
|
|
1304
2626
|
return Promise.reject(e);
|
|
1305
2627
|
}
|
|
1306
2628
|
}
|
|
1307
|
-
/****************************************************************************
|
|
1308
|
-
* SCATTER
|
|
1309
|
-
*/
|
|
1310
|
-
/**
|
|
1311
|
-
* Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
|
|
1312
|
-
* values. Suitable for rendering scatter plots.
|
|
1313
|
-
*/
|
|
1314
2629
|
getScatter(options) {
|
|
1315
2630
|
try {
|
|
1316
2631
|
const _this6 = this;
|
|
1317
2632
|
const {
|
|
2633
|
+
abortController,
|
|
2634
|
+
signal = abortController?.signal,
|
|
2635
|
+
filters = _this6.props.filters,
|
|
1318
2636
|
filterOwner,
|
|
1319
2637
|
spatialFilter,
|
|
1320
2638
|
spatialFiltersMode,
|
|
1321
2639
|
spatialIndexReferenceViewState,
|
|
1322
|
-
abortController,
|
|
1323
2640
|
...params
|
|
1324
2641
|
} = options;
|
|
1325
2642
|
const {
|
|
@@ -1328,7 +2645,7 @@ class WidgetBaseSource {
|
|
|
1328
2645
|
yAxisColumn,
|
|
1329
2646
|
yAxisJoinOperation
|
|
1330
2647
|
} = params;
|
|
1331
|
-
const source = _this6.getModelSource(filterOwner);
|
|
2648
|
+
const source = _this6.getModelSource(filters, filterOwner);
|
|
1332
2649
|
const spatialFiltersResolution = _this6._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1333
2650
|
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
1334
2651
|
const HARD_LIMIT = 500;
|
|
@@ -1348,7 +2665,8 @@ class WidgetBaseSource {
|
|
|
1348
2665
|
limit: HARD_LIMIT
|
|
1349
2666
|
},
|
|
1350
2667
|
opts: {
|
|
1351
|
-
|
|
2668
|
+
signal,
|
|
2669
|
+
headers: _this6.props.headers
|
|
1352
2670
|
}
|
|
1353
2671
|
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(_ref3 => {
|
|
1354
2672
|
let {
|
|
@@ -1361,22 +2679,17 @@ class WidgetBaseSource {
|
|
|
1361
2679
|
return Promise.reject(e);
|
|
1362
2680
|
}
|
|
1363
2681
|
}
|
|
1364
|
-
/****************************************************************************
|
|
1365
|
-
* TABLE
|
|
1366
|
-
*/
|
|
1367
|
-
/**
|
|
1368
|
-
* Returns a list of arbitrary data rows, with support for pagination and
|
|
1369
|
-
* sorting. Suitable for displaying tables and lists.
|
|
1370
|
-
*/
|
|
1371
2682
|
getTable(options) {
|
|
1372
2683
|
try {
|
|
1373
2684
|
const _this7 = this;
|
|
1374
2685
|
const {
|
|
2686
|
+
abortController,
|
|
2687
|
+
signal = abortController?.signal,
|
|
2688
|
+
filters = _this7.props.filters,
|
|
1375
2689
|
filterOwner,
|
|
1376
2690
|
spatialFilter,
|
|
1377
2691
|
spatialFiltersMode,
|
|
1378
2692
|
spatialIndexReferenceViewState,
|
|
1379
|
-
abortController,
|
|
1380
2693
|
...params
|
|
1381
2694
|
} = options;
|
|
1382
2695
|
const {
|
|
@@ -1386,7 +2699,7 @@ class WidgetBaseSource {
|
|
|
1386
2699
|
offset = 0,
|
|
1387
2700
|
limit = 10
|
|
1388
2701
|
} = params;
|
|
1389
|
-
const source = _this7.getModelSource(filterOwner);
|
|
2702
|
+
const source = _this7.getModelSource(filters, filterOwner);
|
|
1390
2703
|
const spatialFiltersResolution = _this7._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1391
2704
|
return Promise.resolve(executeModel({
|
|
1392
2705
|
model: 'table',
|
|
@@ -1404,7 +2717,8 @@ class WidgetBaseSource {
|
|
|
1404
2717
|
offset
|
|
1405
2718
|
},
|
|
1406
2719
|
opts: {
|
|
1407
|
-
|
|
2720
|
+
signal,
|
|
2721
|
+
headers: _this7.props.headers
|
|
1408
2722
|
}
|
|
1409
2723
|
}).then(res => ({
|
|
1410
2724
|
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
@@ -1415,19 +2729,14 @@ class WidgetBaseSource {
|
|
|
1415
2729
|
return Promise.reject(e);
|
|
1416
2730
|
}
|
|
1417
2731
|
}
|
|
1418
|
-
/****************************************************************************
|
|
1419
|
-
* TIME SERIES
|
|
1420
|
-
*/
|
|
1421
|
-
/**
|
|
1422
|
-
* Returns a series of labeled numerical values, grouped into equally-sized
|
|
1423
|
-
* time intervals. Suitable for rendering time series charts.
|
|
1424
|
-
*/
|
|
1425
2732
|
getTimeSeries(options) {
|
|
1426
2733
|
try {
|
|
1427
2734
|
const _this8 = this;
|
|
1428
2735
|
const {
|
|
1429
|
-
filterOwner,
|
|
1430
2736
|
abortController,
|
|
2737
|
+
signal = abortController?.signal,
|
|
2738
|
+
filters = _this8.props.filters,
|
|
2739
|
+
filterOwner,
|
|
1431
2740
|
spatialFilter,
|
|
1432
2741
|
spatialFiltersMode,
|
|
1433
2742
|
spatialIndexReferenceViewState,
|
|
@@ -1444,7 +2753,7 @@ class WidgetBaseSource {
|
|
|
1444
2753
|
splitByCategoryLimit,
|
|
1445
2754
|
splitByCategoryValues
|
|
1446
2755
|
} = params;
|
|
1447
|
-
const source = _this8.getModelSource(filterOwner);
|
|
2756
|
+
const source = _this8.getModelSource(filters, filterOwner);
|
|
1448
2757
|
const spatialFiltersResolution = _this8._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1449
2758
|
return Promise.resolve(executeModel({
|
|
1450
2759
|
model: 'timeseries',
|
|
@@ -1466,7 +2775,8 @@ class WidgetBaseSource {
|
|
|
1466
2775
|
splitByCategoryValues
|
|
1467
2776
|
},
|
|
1468
2777
|
opts: {
|
|
1469
|
-
|
|
2778
|
+
signal,
|
|
2779
|
+
headers: _this8.props.headers
|
|
1470
2780
|
}
|
|
1471
2781
|
}).then(res => ({
|
|
1472
2782
|
rows: normalizeObjectKeys(res.rows),
|
|
@@ -1477,13 +2787,6 @@ class WidgetBaseSource {
|
|
|
1477
2787
|
}
|
|
1478
2788
|
}
|
|
1479
2789
|
}
|
|
1480
|
-
WidgetBaseSource.defaultProps = {
|
|
1481
|
-
apiVersion: exports.ApiVersion.V3,
|
|
1482
|
-
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1483
|
-
clientId: getClient(),
|
|
1484
|
-
filters: {},
|
|
1485
|
-
filtersLogicalOperator: 'and'
|
|
1486
|
-
};
|
|
1487
2790
|
|
|
1488
2791
|
/**
|
|
1489
2792
|
* Source for Widget API requests on a data source defined by a SQL query.
|
|
@@ -1507,10 +2810,10 @@ WidgetBaseSource.defaultProps = {
|
|
|
1507
2810
|
* const { widgetSource } = await data;
|
|
1508
2811
|
* ```
|
|
1509
2812
|
*/
|
|
1510
|
-
class WidgetQuerySource extends
|
|
1511
|
-
getModelSource(
|
|
2813
|
+
class WidgetQuerySource extends WidgetRemoteSource {
|
|
2814
|
+
getModelSource(filters, filterOwner) {
|
|
1512
2815
|
return {
|
|
1513
|
-
...super._getModelSource(
|
|
2816
|
+
...super._getModelSource(filters, filterOwner),
|
|
1514
2817
|
type: 'query',
|
|
1515
2818
|
data: this.props.sqlQuery,
|
|
1516
2819
|
queryParameters: this.props.queryParameters
|
|
@@ -1540,20 +2843,787 @@ class WidgetQuerySource extends WidgetBaseSource {
|
|
|
1540
2843
|
* const { widgetSource } = await data;
|
|
1541
2844
|
* ```
|
|
1542
2845
|
*/
|
|
1543
|
-
class WidgetTableSource extends
|
|
1544
|
-
getModelSource(
|
|
2846
|
+
class WidgetTableSource extends WidgetRemoteSource {
|
|
2847
|
+
getModelSource(filters, filterOwner) {
|
|
1545
2848
|
return {
|
|
1546
|
-
...super._getModelSource(
|
|
2849
|
+
...super._getModelSource(filters, filterOwner),
|
|
1547
2850
|
type: 'table',
|
|
1548
2851
|
data: this.props.tableName
|
|
1549
2852
|
};
|
|
1550
2853
|
}
|
|
1551
2854
|
}
|
|
1552
2855
|
|
|
2856
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2857
|
+
const aggregationFunctions = {
|
|
2858
|
+
count: values => values.length,
|
|
2859
|
+
min: function () {
|
|
2860
|
+
return applyAggregationFunction(min, ...[].slice.call(arguments));
|
|
2861
|
+
},
|
|
2862
|
+
max: function () {
|
|
2863
|
+
return applyAggregationFunction(max, ...[].slice.call(arguments));
|
|
2864
|
+
},
|
|
2865
|
+
sum: function () {
|
|
2866
|
+
return applyAggregationFunction(sum, ...[].slice.call(arguments));
|
|
2867
|
+
},
|
|
2868
|
+
avg: function () {
|
|
2869
|
+
return applyAggregationFunction(avg, ...[].slice.call(arguments));
|
|
2870
|
+
}
|
|
2871
|
+
};
|
|
2872
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2873
|
+
function aggregate(feature, keys, operation) {
|
|
2874
|
+
if (!keys?.length) {
|
|
2875
|
+
throw new Error('Cannot aggregate a feature without having keys');
|
|
2876
|
+
} else if (keys.length === 1) {
|
|
2877
|
+
const value = feature[keys[0]];
|
|
2878
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
2879
|
+
}
|
|
2880
|
+
const aggregationFn = aggregationFunctions[operation];
|
|
2881
|
+
if (!aggregationFn) {
|
|
2882
|
+
throw new Error(`${operation} isn't a valid aggregation function`);
|
|
2883
|
+
}
|
|
2884
|
+
return aggregationFn(keys.map(column => {
|
|
2885
|
+
const value = feature[column];
|
|
2886
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
2887
|
+
}));
|
|
2888
|
+
}
|
|
2889
|
+
/*
|
|
2890
|
+
* Forced casting to Number (just of non empty strings) allows to work-around
|
|
2891
|
+
* some specific situations, where a big numeric field is transformed into a string when generating the tileset(eg.PG)
|
|
2892
|
+
*/
|
|
2893
|
+
function isPotentiallyValidNumber(value) {
|
|
2894
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
2895
|
+
}
|
|
2896
|
+
const applyAggregationFunction = (aggFn, values, keys, operation) => {
|
|
2897
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2898
|
+
const elements = (normalizedKeys?.length || 0) <= 1 ? filterFalsyElements(values, normalizedKeys || []) : values;
|
|
2899
|
+
return aggFn(elements, keys, operation);
|
|
2900
|
+
};
|
|
2901
|
+
function filterFalsyElements(values, keys) {
|
|
2902
|
+
const filterFn = value => value !== null && value !== undefined;
|
|
2903
|
+
if (!keys?.length) {
|
|
2904
|
+
return values.filter(filterFn);
|
|
2905
|
+
}
|
|
2906
|
+
return values.filter(v => filterFn(v[keys[0]]));
|
|
2907
|
+
}
|
|
2908
|
+
// Aggregation functions
|
|
2909
|
+
function avg(values, keys, joinOperation) {
|
|
2910
|
+
return sum(values, keys, joinOperation) / (values.length || 1);
|
|
2911
|
+
}
|
|
2912
|
+
function sum(values, keys, joinOperation) {
|
|
2913
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2914
|
+
if (normalizedKeys) {
|
|
2915
|
+
return values.reduce((a, b) => a + aggregate(b, normalizedKeys, joinOperation), 0);
|
|
2916
|
+
}
|
|
2917
|
+
return values.reduce((a, b) => a + b, 0);
|
|
2918
|
+
}
|
|
2919
|
+
function min(values, keys, joinOperation) {
|
|
2920
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2921
|
+
if (normalizedKeys) {
|
|
2922
|
+
return values.reduce((a, b) => Math.min(a, aggregate(b, normalizedKeys, joinOperation)), Infinity);
|
|
2923
|
+
}
|
|
2924
|
+
return Math.min(...values);
|
|
2925
|
+
}
|
|
2926
|
+
function max(values, keys, joinOperation) {
|
|
2927
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
2928
|
+
if (normalizedKeys) {
|
|
2929
|
+
return values.reduce((a, b) => Math.max(a, aggregate(b, normalizedKeys, joinOperation)), -Infinity);
|
|
2930
|
+
}
|
|
2931
|
+
return Math.max(...values);
|
|
2932
|
+
}
|
|
2933
|
+
// Aux
|
|
2934
|
+
// Keys can come as a string (one column) or a strings array (multiple column)
|
|
2935
|
+
// Use always an array to make the code easier
|
|
2936
|
+
function normalizeKeys(keys) {
|
|
2937
|
+
return Array.isArray(keys) ? keys : typeof keys === 'string' ? [keys] : undefined;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
/***
|
|
2941
|
+
Copyright 2013 Teun Duynstee
|
|
2942
|
+
|
|
2943
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
2944
|
+
you may not use this file except in compliance with the License.
|
|
2945
|
+
You may obtain a copy of the License at
|
|
2946
|
+
|
|
2947
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
2948
|
+
|
|
2949
|
+
Unless required by applicable law or agreed to in writing, software
|
|
2950
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
2951
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2952
|
+
See the License for the specific language governing permissions and
|
|
2953
|
+
limitations under the License.
|
|
2954
|
+
*/
|
|
2955
|
+
var thenBy_module = function () {
|
|
2956
|
+
function identity(v) {
|
|
2957
|
+
return v;
|
|
2958
|
+
}
|
|
2959
|
+
function ignoreCase(v) {
|
|
2960
|
+
return typeof v === "string" ? v.toLowerCase() : v;
|
|
2961
|
+
}
|
|
2962
|
+
function makeCompareFunction(f, opt) {
|
|
2963
|
+
opt = typeof opt === "object" ? opt : {
|
|
2964
|
+
direction: opt
|
|
2965
|
+
};
|
|
2966
|
+
if (typeof f != "function") {
|
|
2967
|
+
var prop = f;
|
|
2968
|
+
// make unary function
|
|
2969
|
+
f = function (v1) {
|
|
2970
|
+
return !!v1[prop] ? v1[prop] : "";
|
|
2971
|
+
};
|
|
2972
|
+
}
|
|
2973
|
+
if (f.length === 1) {
|
|
2974
|
+
// f is a unary function mapping a single item to its sort score
|
|
2975
|
+
var uf = f;
|
|
2976
|
+
var preprocess = opt.ignoreCase ? ignoreCase : identity;
|
|
2977
|
+
var cmp = opt.cmp || function (v1, v2) {
|
|
2978
|
+
return v1 < v2 ? -1 : v1 > v2 ? 1 : 0;
|
|
2979
|
+
};
|
|
2980
|
+
f = function (v1, v2) {
|
|
2981
|
+
return cmp(preprocess(uf(v1)), preprocess(uf(v2)));
|
|
2982
|
+
};
|
|
2983
|
+
}
|
|
2984
|
+
const descTokens = {
|
|
2985
|
+
"-1": '',
|
|
2986
|
+
desc: ''
|
|
2987
|
+
};
|
|
2988
|
+
if (opt.direction in descTokens) return function (v1, v2) {
|
|
2989
|
+
return -f(v1, v2);
|
|
2990
|
+
};
|
|
2991
|
+
return f;
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
/* adds a secondary compare function to the target function (`this` context)
|
|
2995
|
+
which is applied in case the first one returns 0 (equal)
|
|
2996
|
+
returns a new compare function, which has a `thenBy` method as well */
|
|
2997
|
+
function tb(func, opt) {
|
|
2998
|
+
/* should get value false for the first call. This can be done by calling the
|
|
2999
|
+
exported function, or the firstBy property on it (for es6 module compatibility)
|
|
3000
|
+
*/
|
|
3001
|
+
var x = typeof this == "function" && !this.firstBy ? this : false;
|
|
3002
|
+
var y = makeCompareFunction(func, opt);
|
|
3003
|
+
var f = x ? function (a, b) {
|
|
3004
|
+
return x(a, b) || y(a, b);
|
|
3005
|
+
} : y;
|
|
3006
|
+
f.thenBy = tb;
|
|
3007
|
+
return f;
|
|
3008
|
+
}
|
|
3009
|
+
tb.firstBy = tb;
|
|
3010
|
+
return tb;
|
|
3011
|
+
}();
|
|
3012
|
+
|
|
3013
|
+
/**
|
|
3014
|
+
* Apply sort structure to a collection of features
|
|
3015
|
+
* @param features
|
|
3016
|
+
* @param [sortOptions]
|
|
3017
|
+
* @param [sortOptions.sortBy] - One or more columns to sort by
|
|
3018
|
+
* @param [sortOptions.sortByDirection] - Direction by the columns will be sorted
|
|
3019
|
+
* @param [sortOptions.sortByColumnType] - Column type
|
|
3020
|
+
* @internal
|
|
3021
|
+
* @privateRemarks Source: @carto/react-core
|
|
3022
|
+
*/
|
|
3023
|
+
function applySorting(features, _temp) {
|
|
3024
|
+
let {
|
|
3025
|
+
sortBy,
|
|
3026
|
+
sortByDirection = 'asc',
|
|
3027
|
+
sortByColumnType = 'string'
|
|
3028
|
+
} = _temp === void 0 ? {} : _temp;
|
|
3029
|
+
// If sortBy is undefined, pass all features
|
|
3030
|
+
if (sortBy === undefined) {
|
|
3031
|
+
return features;
|
|
3032
|
+
}
|
|
3033
|
+
// sortOptions exists, but are bad formatted
|
|
3034
|
+
const isValidSortBy = Array.isArray(sortBy) && sortBy.length ||
|
|
3035
|
+
// sortBy can be an array of columns
|
|
3036
|
+
typeof sortBy === 'string'; // or just one column
|
|
3037
|
+
if (!isValidSortBy) {
|
|
3038
|
+
throw new Error('Sorting options are bad formatted');
|
|
3039
|
+
}
|
|
3040
|
+
const sortFn = createSortFn({
|
|
3041
|
+
sortBy,
|
|
3042
|
+
sortByDirection,
|
|
3043
|
+
sortByColumnType: sortByColumnType || 'string'
|
|
3044
|
+
});
|
|
3045
|
+
return features.sort(sortFn);
|
|
3046
|
+
}
|
|
3047
|
+
// Aux
|
|
3048
|
+
function createSortFn(_ref) {
|
|
3049
|
+
let {
|
|
3050
|
+
sortBy,
|
|
3051
|
+
sortByDirection,
|
|
3052
|
+
sortByColumnType
|
|
3053
|
+
} = _ref;
|
|
3054
|
+
const [firstSortOption, ...othersSortOptions] = normalizeSortByOptions({
|
|
3055
|
+
sortBy,
|
|
3056
|
+
sortByDirection,
|
|
3057
|
+
sortByColumnType
|
|
3058
|
+
});
|
|
3059
|
+
let sortFn = thenBy_module.firstBy(...firstSortOption);
|
|
3060
|
+
for (const sortOptions of othersSortOptions) {
|
|
3061
|
+
sortFn = sortFn.thenBy(...sortOptions);
|
|
3062
|
+
}
|
|
3063
|
+
return sortFn;
|
|
3064
|
+
}
|
|
3065
|
+
function normalizeSortByOptions(_ref2) {
|
|
3066
|
+
let {
|
|
3067
|
+
sortBy,
|
|
3068
|
+
sortByDirection,
|
|
3069
|
+
sortByColumnType
|
|
3070
|
+
} = _ref2;
|
|
3071
|
+
const numberFormat = sortByColumnType === 'number' && {
|
|
3072
|
+
cmp: (a, b) => a - b
|
|
3073
|
+
};
|
|
3074
|
+
if (!Array.isArray(sortBy)) {
|
|
3075
|
+
sortBy = [sortBy];
|
|
3076
|
+
}
|
|
3077
|
+
return sortBy.map(sortByEl => {
|
|
3078
|
+
// sortByEl is 'column'
|
|
3079
|
+
if (typeof sortByEl === 'string') {
|
|
3080
|
+
return [sortByEl, {
|
|
3081
|
+
direction: sortByDirection,
|
|
3082
|
+
...numberFormat
|
|
3083
|
+
}];
|
|
3084
|
+
}
|
|
3085
|
+
if (Array.isArray(sortByEl)) {
|
|
3086
|
+
// sortBy is ['column']
|
|
3087
|
+
if (sortByEl[1] === undefined) {
|
|
3088
|
+
return [sortByEl, {
|
|
3089
|
+
direction: sortByDirection,
|
|
3090
|
+
...numberFormat
|
|
3091
|
+
}];
|
|
3092
|
+
}
|
|
3093
|
+
// sortBy is ['column', { ... }]
|
|
3094
|
+
if (typeof sortByEl[1] === 'object') {
|
|
3095
|
+
const othersSortOptions = numberFormat ? {
|
|
3096
|
+
...numberFormat,
|
|
3097
|
+
...sortByEl[1]
|
|
3098
|
+
} : sortByEl[1];
|
|
3099
|
+
return [sortByEl[0], {
|
|
3100
|
+
direction: sortByDirection,
|
|
3101
|
+
...othersSortOptions
|
|
3102
|
+
}];
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
return sortByEl;
|
|
3106
|
+
});
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
3110
|
+
function groupValuesByColumn(_ref) {
|
|
3111
|
+
let {
|
|
3112
|
+
data,
|
|
3113
|
+
valuesColumns,
|
|
3114
|
+
joinOperation,
|
|
3115
|
+
keysColumn,
|
|
3116
|
+
operation
|
|
3117
|
+
} = _ref;
|
|
3118
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
3119
|
+
return null;
|
|
3120
|
+
}
|
|
3121
|
+
const groups = data.reduce((accumulator, item) => {
|
|
3122
|
+
const group = item[keysColumn];
|
|
3123
|
+
const values = accumulator.get(group) || [];
|
|
3124
|
+
accumulator.set(group, values);
|
|
3125
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
3126
|
+
const isValid = (operation === 'count' ? true : aggregatedValue !== null) && aggregatedValue !== undefined;
|
|
3127
|
+
if (isValid) {
|
|
3128
|
+
values.push(aggregatedValue);
|
|
3129
|
+
accumulator.set(group, values);
|
|
3130
|
+
}
|
|
3131
|
+
return accumulator;
|
|
3132
|
+
}, new Map()); // We use a map to be able to maintain the type in the key value
|
|
3133
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3134
|
+
if (targetOperation) {
|
|
3135
|
+
return Array.from(groups).map(_ref2 => {
|
|
3136
|
+
let [name, value] = _ref2;
|
|
3137
|
+
return {
|
|
3138
|
+
name,
|
|
3139
|
+
value: targetOperation(value)
|
|
3140
|
+
};
|
|
3141
|
+
});
|
|
3142
|
+
}
|
|
3143
|
+
return [];
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
/**
|
|
3147
|
+
* Returns midnight (local time) on the Monday preceeding a given date, in
|
|
3148
|
+
* milliseconds since the UNIX epoch.
|
|
3149
|
+
*/
|
|
3150
|
+
/**
|
|
3151
|
+
* Returns midnight (UTC) on the Monday preceeding a given date, in
|
|
3152
|
+
* milliseconds since the UNIX epoch.
|
|
3153
|
+
*/
|
|
3154
|
+
function getUTCMonday(date) {
|
|
3155
|
+
const dateCp = new Date(date);
|
|
3156
|
+
const day = dateCp.getUTCDay();
|
|
3157
|
+
const diff = dateCp.getUTCDate() - day + (day ? 1 : -6); // adjust when day is sunday
|
|
3158
|
+
dateCp.setUTCDate(diff);
|
|
3159
|
+
return Date.UTC(dateCp.getUTCFullYear(), dateCp.getUTCMonth(), dateCp.getUTCDate());
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
const GROUP_KEY_FN_MAPPING = {
|
|
3163
|
+
year: date => Date.UTC(date.getUTCFullYear()),
|
|
3164
|
+
month: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth()),
|
|
3165
|
+
week: date => getUTCMonday(date),
|
|
3166
|
+
day: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
|
|
3167
|
+
hour: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours()),
|
|
3168
|
+
minute: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes()),
|
|
3169
|
+
second: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds())
|
|
3170
|
+
};
|
|
3171
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
3172
|
+
function groupValuesByDateColumn(_ref) {
|
|
3173
|
+
let {
|
|
3174
|
+
data,
|
|
3175
|
+
valuesColumns,
|
|
3176
|
+
joinOperation,
|
|
3177
|
+
keysColumn,
|
|
3178
|
+
groupType,
|
|
3179
|
+
operation
|
|
3180
|
+
} = _ref;
|
|
3181
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
3182
|
+
return null;
|
|
3183
|
+
}
|
|
3184
|
+
const groupKeyFn = GROUP_KEY_FN_MAPPING[groupType];
|
|
3185
|
+
if (!groupKeyFn) {
|
|
3186
|
+
return null;
|
|
3187
|
+
}
|
|
3188
|
+
const groups = data.reduce((acc, item) => {
|
|
3189
|
+
const value = item[keysColumn];
|
|
3190
|
+
const formattedValue = new Date(value);
|
|
3191
|
+
const groupKey = groupKeyFn(formattedValue);
|
|
3192
|
+
if (!isNaN(groupKey)) {
|
|
3193
|
+
let groupedValues = acc.get(groupKey);
|
|
3194
|
+
if (!groupedValues) {
|
|
3195
|
+
groupedValues = [];
|
|
3196
|
+
acc.set(groupKey, groupedValues);
|
|
3197
|
+
}
|
|
3198
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
3199
|
+
const isValid = aggregatedValue !== null && aggregatedValue !== undefined;
|
|
3200
|
+
if (isValid) {
|
|
3201
|
+
groupedValues.push(aggregatedValue);
|
|
3202
|
+
acc.set(groupKey, groupedValues);
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
return acc;
|
|
3206
|
+
}, new Map());
|
|
3207
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3208
|
+
return [...groups.entries()].map(_ref2 => {
|
|
3209
|
+
let [name, value] = _ref2;
|
|
3210
|
+
return {
|
|
3211
|
+
name,
|
|
3212
|
+
value: targetOperation(value)
|
|
3213
|
+
};
|
|
3214
|
+
}).sort((a, b) => a.name - b.name);
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
/**
|
|
3218
|
+
* Histogram computation.
|
|
3219
|
+
* @privateRemarks Source: @carto/react-core
|
|
3220
|
+
*/
|
|
3221
|
+
function histogram(_ref) {
|
|
3222
|
+
let {
|
|
3223
|
+
data,
|
|
3224
|
+
valuesColumns,
|
|
3225
|
+
joinOperation,
|
|
3226
|
+
ticks,
|
|
3227
|
+
operation
|
|
3228
|
+
} = _ref;
|
|
3229
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
3230
|
+
return [];
|
|
3231
|
+
}
|
|
3232
|
+
const binsContainer = [Number.MIN_SAFE_INTEGER, ...ticks].map((tick, index, arr) => ({
|
|
3233
|
+
bin: index,
|
|
3234
|
+
start: tick,
|
|
3235
|
+
end: index === arr.length - 1 ? Number.MAX_SAFE_INTEGER : arr[index + 1],
|
|
3236
|
+
values: []
|
|
3237
|
+
}));
|
|
3238
|
+
data.forEach(feature => {
|
|
3239
|
+
const featureValue = aggregate(feature, valuesColumns, joinOperation);
|
|
3240
|
+
const isValid = featureValue !== null && featureValue !== undefined;
|
|
3241
|
+
if (!isValid) {
|
|
3242
|
+
return;
|
|
3243
|
+
}
|
|
3244
|
+
const binContainer = binsContainer.find(bin => bin.start <= featureValue && bin.end > featureValue);
|
|
3245
|
+
if (!binContainer) {
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
binContainer.values.push(featureValue);
|
|
3249
|
+
});
|
|
3250
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3251
|
+
const transformedBins = binsContainer.map(binContainer => binContainer.values);
|
|
3252
|
+
return transformedBins.map(values => values.length ? targetOperation(values) : 0);
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
/**
|
|
3256
|
+
* Filters invalid features and formats data.
|
|
3257
|
+
* @privateRemarks Source: @carto/react-core
|
|
3258
|
+
*/
|
|
3259
|
+
function scatterPlot(_ref) {
|
|
3260
|
+
let {
|
|
3261
|
+
data,
|
|
3262
|
+
xAxisColumns,
|
|
3263
|
+
xAxisJoinOperation,
|
|
3264
|
+
yAxisColumns,
|
|
3265
|
+
yAxisJoinOperation
|
|
3266
|
+
} = _ref;
|
|
3267
|
+
return data.reduce((acc, feature) => {
|
|
3268
|
+
const xValue = aggregate(feature, xAxisColumns, xAxisJoinOperation);
|
|
3269
|
+
const xIsValid = xValue !== null && xValue !== undefined;
|
|
3270
|
+
const yValue = aggregate(feature, yAxisColumns, yAxisJoinOperation);
|
|
3271
|
+
const yIsValid = yValue !== null && yValue !== undefined;
|
|
3272
|
+
if (xIsValid && yIsValid) {
|
|
3273
|
+
acc.push([xValue, yValue]);
|
|
3274
|
+
}
|
|
3275
|
+
return acc;
|
|
3276
|
+
}, []);
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
/**
|
|
3280
|
+
* Source for Widget API requests on a data source defined by a tileset.
|
|
3281
|
+
*
|
|
3282
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
3283
|
+
* {@link vectorTilesetSource}, {@link h3TilesetSource}, or {@link quadbinTilesetSource},
|
|
3284
|
+
* which can be shared with map layers. Sources contain a `widgetSource`
|
|
3285
|
+
* property, for use by widget implementations.
|
|
3286
|
+
*
|
|
3287
|
+
* Example:
|
|
3288
|
+
*
|
|
3289
|
+
* ```javascript
|
|
3290
|
+
* import { vectorTilesetSource } from '@carto/api-client';
|
|
3291
|
+
*
|
|
3292
|
+
* const data = vectorTilesetSource({
|
|
3293
|
+
* accessToken: '••••',
|
|
3294
|
+
* connectionName: 'carto_dw',
|
|
3295
|
+
* tableName: 'carto-demo-data.demo_rasters.my_tileset_source'
|
|
3296
|
+
* });
|
|
3297
|
+
*
|
|
3298
|
+
* const { widgetSource } = await data;
|
|
3299
|
+
* ```
|
|
3300
|
+
*/
|
|
3301
|
+
class WidgetTilesetSource extends WidgetSource {
|
|
3302
|
+
constructor() {
|
|
3303
|
+
super(...arguments);
|
|
3304
|
+
this._tiles = [];
|
|
3305
|
+
this._features = [];
|
|
3306
|
+
this._tileFeatureExtractOptions = {};
|
|
3307
|
+
this._tileFeatureExtractPreviousInputs = {};
|
|
3308
|
+
}
|
|
3309
|
+
getModelSource(filters, filterOwner) {
|
|
3310
|
+
return {
|
|
3311
|
+
...super._getModelSource(filters, filterOwner),
|
|
3312
|
+
type: 'tileset',
|
|
3313
|
+
data: this.props.tableName
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
/**
|
|
3317
|
+
* Loads features as a list of tiles (typically provided by deck.gl).
|
|
3318
|
+
* After tiles are loaded, {@link extractTileFeatures} must be called
|
|
3319
|
+
* before computing statistics on the tiles.
|
|
3320
|
+
*/
|
|
3321
|
+
loadTiles(tiles) {
|
|
3322
|
+
this._tiles = tiles;
|
|
3323
|
+
this._features.length = 0;
|
|
3324
|
+
}
|
|
3325
|
+
/** Configures options used to extract features from tiles. */
|
|
3326
|
+
setTileFeatureExtractOptions(options) {
|
|
3327
|
+
this._tileFeatureExtractOptions = options;
|
|
3328
|
+
this._features.length = 0;
|
|
3329
|
+
}
|
|
3330
|
+
_extractTileFeatures(spatialFilter) {
|
|
3331
|
+
// When spatial filter has not changed, don't redo extraction. If tiles or
|
|
3332
|
+
// tile extract options change, features will have been cleared already.
|
|
3333
|
+
const prevInputs = this._tileFeatureExtractPreviousInputs;
|
|
3334
|
+
if (this._features.length && prevInputs.spatialFilter && booleanEqual.booleanEqual(prevInputs.spatialFilter, spatialFilter)) {
|
|
3335
|
+
return;
|
|
3336
|
+
}
|
|
3337
|
+
this._features = tileFeatures({
|
|
3338
|
+
tiles: this._tiles,
|
|
3339
|
+
tileFormat: this.props.tileFormat,
|
|
3340
|
+
...this._tileFeatureExtractOptions,
|
|
3341
|
+
spatialFilter,
|
|
3342
|
+
spatialDataColumn: this.props.spatialDataColumn,
|
|
3343
|
+
spatialDataType: this.props.spatialDataType
|
|
3344
|
+
});
|
|
3345
|
+
prevInputs.spatialFilter = spatialFilter;
|
|
3346
|
+
}
|
|
3347
|
+
/**
|
|
3348
|
+
* Loads features as GeoJSON (used for testing).
|
|
3349
|
+
* @experimental
|
|
3350
|
+
* @internal Not for public use. Spatial filters in other method calls will be ignored.
|
|
3351
|
+
*/
|
|
3352
|
+
loadGeoJSON(_ref) {
|
|
3353
|
+
let {
|
|
3354
|
+
geojson,
|
|
3355
|
+
spatialFilter
|
|
3356
|
+
} = _ref;
|
|
3357
|
+
this._features = geojsonFeatures({
|
|
3358
|
+
geojson,
|
|
3359
|
+
spatialFilter,
|
|
3360
|
+
...this._tileFeatureExtractOptions
|
|
3361
|
+
});
|
|
3362
|
+
this._tileFeatureExtractPreviousInputs.spatialFilter = spatialFilter;
|
|
3363
|
+
}
|
|
3364
|
+
getFeatures() {
|
|
3365
|
+
try {
|
|
3366
|
+
throw new Error('getFeatures not supported for tilesets');
|
|
3367
|
+
return Promise.resolve();
|
|
3368
|
+
} catch (e) {
|
|
3369
|
+
return Promise.reject(e);
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
getFormula(_ref2) {
|
|
3373
|
+
let {
|
|
3374
|
+
column = '*',
|
|
3375
|
+
operation = 'count',
|
|
3376
|
+
joinOperation,
|
|
3377
|
+
filters,
|
|
3378
|
+
filterOwner,
|
|
3379
|
+
spatialFilter
|
|
3380
|
+
} = _ref2;
|
|
3381
|
+
try {
|
|
3382
|
+
const _this = this;
|
|
3383
|
+
if (operation === 'custom') {
|
|
3384
|
+
throw new Error('Custom aggregation not supported for tilesets');
|
|
3385
|
+
}
|
|
3386
|
+
// Column is required except when operation is 'count'.
|
|
3387
|
+
if (column && column !== '*' || operation !== 'count') {
|
|
3388
|
+
assertColumn(_this._features, column);
|
|
3389
|
+
}
|
|
3390
|
+
const filteredFeatures = _this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3391
|
+
if (filteredFeatures.length === 0 && operation !== 'count') {
|
|
3392
|
+
return Promise.resolve({
|
|
3393
|
+
value: null
|
|
3394
|
+
});
|
|
3395
|
+
}
|
|
3396
|
+
const targetOperation = aggregationFunctions[operation];
|
|
3397
|
+
return Promise.resolve({
|
|
3398
|
+
value: targetOperation(filteredFeatures, column, joinOperation)
|
|
3399
|
+
});
|
|
3400
|
+
} catch (e) {
|
|
3401
|
+
return Promise.reject(e);
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
getHistogram(_ref3) {
|
|
3405
|
+
let {
|
|
3406
|
+
operation = 'count',
|
|
3407
|
+
ticks,
|
|
3408
|
+
column,
|
|
3409
|
+
joinOperation,
|
|
3410
|
+
filters,
|
|
3411
|
+
filterOwner,
|
|
3412
|
+
spatialFilter
|
|
3413
|
+
} = _ref3;
|
|
3414
|
+
try {
|
|
3415
|
+
const _this2 = this;
|
|
3416
|
+
const filteredFeatures = _this2._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3417
|
+
assertColumn(_this2._features, column);
|
|
3418
|
+
if (!_this2._features.length) {
|
|
3419
|
+
return Promise.resolve([]);
|
|
3420
|
+
}
|
|
3421
|
+
return Promise.resolve(histogram({
|
|
3422
|
+
data: filteredFeatures,
|
|
3423
|
+
valuesColumns: normalizeColumns(column),
|
|
3424
|
+
joinOperation,
|
|
3425
|
+
ticks,
|
|
3426
|
+
operation
|
|
3427
|
+
}));
|
|
3428
|
+
} catch (e) {
|
|
3429
|
+
return Promise.reject(e);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
getCategories(_ref4) {
|
|
3433
|
+
let {
|
|
3434
|
+
column,
|
|
3435
|
+
operation = 'count',
|
|
3436
|
+
operationColumn,
|
|
3437
|
+
joinOperation,
|
|
3438
|
+
filters,
|
|
3439
|
+
filterOwner,
|
|
3440
|
+
spatialFilter
|
|
3441
|
+
} = _ref4;
|
|
3442
|
+
try {
|
|
3443
|
+
const _this3 = this;
|
|
3444
|
+
const filteredFeatures = _this3._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3445
|
+
if (!filteredFeatures.length) {
|
|
3446
|
+
return Promise.resolve([]);
|
|
3447
|
+
}
|
|
3448
|
+
assertColumn(_this3._features, column, operationColumn);
|
|
3449
|
+
const groups = groupValuesByColumn({
|
|
3450
|
+
data: filteredFeatures,
|
|
3451
|
+
valuesColumns: normalizeColumns(operationColumn || column),
|
|
3452
|
+
joinOperation,
|
|
3453
|
+
keysColumn: column,
|
|
3454
|
+
operation
|
|
3455
|
+
});
|
|
3456
|
+
return Promise.resolve(groups || []);
|
|
3457
|
+
} catch (e) {
|
|
3458
|
+
return Promise.reject(e);
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
getScatter(_ref5) {
|
|
3462
|
+
let {
|
|
3463
|
+
xAxisColumn,
|
|
3464
|
+
yAxisColumn,
|
|
3465
|
+
xAxisJoinOperation,
|
|
3466
|
+
yAxisJoinOperation,
|
|
3467
|
+
filters,
|
|
3468
|
+
filterOwner,
|
|
3469
|
+
spatialFilter
|
|
3470
|
+
} = _ref5;
|
|
3471
|
+
try {
|
|
3472
|
+
const _this4 = this;
|
|
3473
|
+
const filteredFeatures = _this4._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3474
|
+
if (!filteredFeatures.length) {
|
|
3475
|
+
return Promise.resolve([]);
|
|
3476
|
+
}
|
|
3477
|
+
assertColumn(_this4._features, xAxisColumn, yAxisColumn);
|
|
3478
|
+
return Promise.resolve(scatterPlot({
|
|
3479
|
+
data: filteredFeatures,
|
|
3480
|
+
xAxisColumns: normalizeColumns(xAxisColumn),
|
|
3481
|
+
xAxisJoinOperation,
|
|
3482
|
+
yAxisColumns: normalizeColumns(yAxisColumn),
|
|
3483
|
+
yAxisJoinOperation
|
|
3484
|
+
}));
|
|
3485
|
+
} catch (e) {
|
|
3486
|
+
return Promise.reject(e);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
getTable(_ref6) {
|
|
3490
|
+
let {
|
|
3491
|
+
columns,
|
|
3492
|
+
searchFilterColumn,
|
|
3493
|
+
searchFilterText,
|
|
3494
|
+
sortBy,
|
|
3495
|
+
sortDirection,
|
|
3496
|
+
sortByColumnType,
|
|
3497
|
+
offset = 0,
|
|
3498
|
+
limit = 10,
|
|
3499
|
+
filters,
|
|
3500
|
+
filterOwner,
|
|
3501
|
+
spatialFilter
|
|
3502
|
+
} = _ref6;
|
|
3503
|
+
try {
|
|
3504
|
+
const _this5 = this;
|
|
3505
|
+
// Filter.
|
|
3506
|
+
let filteredFeatures = _this5._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3507
|
+
if (!filteredFeatures.length) {
|
|
3508
|
+
return Promise.resolve({
|
|
3509
|
+
rows: [],
|
|
3510
|
+
totalCount: 0
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
// Search.
|
|
3514
|
+
if (searchFilterColumn && searchFilterText) {
|
|
3515
|
+
filteredFeatures = filteredFeatures.filter(row => row[searchFilterColumn] && String(row[searchFilterColumn]).toLowerCase().includes(String(searchFilterText).toLowerCase()));
|
|
3516
|
+
}
|
|
3517
|
+
// Sort.
|
|
3518
|
+
let rows = applySorting(filteredFeatures, {
|
|
3519
|
+
sortBy,
|
|
3520
|
+
sortByDirection: sortDirection,
|
|
3521
|
+
sortByColumnType
|
|
3522
|
+
});
|
|
3523
|
+
const totalCount = rows.length;
|
|
3524
|
+
// Offset and limit.
|
|
3525
|
+
rows = rows.slice(Math.min(offset, totalCount), Math.min(offset + limit, totalCount));
|
|
3526
|
+
// Select columns.
|
|
3527
|
+
rows = rows.map(srcRow => {
|
|
3528
|
+
const dstRow = {};
|
|
3529
|
+
for (const column of columns) {
|
|
3530
|
+
dstRow[column] = srcRow[column];
|
|
3531
|
+
}
|
|
3532
|
+
return dstRow;
|
|
3533
|
+
});
|
|
3534
|
+
return Promise.resolve({
|
|
3535
|
+
rows,
|
|
3536
|
+
totalCount
|
|
3537
|
+
});
|
|
3538
|
+
} catch (e) {
|
|
3539
|
+
return Promise.reject(e);
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
getTimeSeries(_ref7) {
|
|
3543
|
+
let {
|
|
3544
|
+
column,
|
|
3545
|
+
stepSize,
|
|
3546
|
+
operation,
|
|
3547
|
+
operationColumn,
|
|
3548
|
+
joinOperation,
|
|
3549
|
+
filters,
|
|
3550
|
+
filterOwner,
|
|
3551
|
+
spatialFilter
|
|
3552
|
+
} = _ref7;
|
|
3553
|
+
try {
|
|
3554
|
+
const _this6 = this;
|
|
3555
|
+
const filteredFeatures = _this6._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3556
|
+
if (!filteredFeatures.length) {
|
|
3557
|
+
return Promise.resolve({
|
|
3558
|
+
rows: []
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
assertColumn(_this6._features, column, operationColumn);
|
|
3562
|
+
const rows = groupValuesByDateColumn({
|
|
3563
|
+
data: filteredFeatures,
|
|
3564
|
+
valuesColumns: normalizeColumns(operationColumn || column),
|
|
3565
|
+
keysColumn: column,
|
|
3566
|
+
groupType: stepSize,
|
|
3567
|
+
operation,
|
|
3568
|
+
joinOperation
|
|
3569
|
+
}) || [];
|
|
3570
|
+
return Promise.resolve({
|
|
3571
|
+
rows
|
|
3572
|
+
});
|
|
3573
|
+
} catch (e) {
|
|
3574
|
+
return Promise.reject(e);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
getRange(_ref8) {
|
|
3578
|
+
let {
|
|
3579
|
+
column,
|
|
3580
|
+
filters,
|
|
3581
|
+
filterOwner,
|
|
3582
|
+
spatialFilter
|
|
3583
|
+
} = _ref8;
|
|
3584
|
+
try {
|
|
3585
|
+
const _this7 = this;
|
|
3586
|
+
assertColumn(_this7._features, column);
|
|
3587
|
+
const filteredFeatures = _this7._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3588
|
+
if (!_this7._features.length) {
|
|
3589
|
+
// TODO: Is this the only nullable response in the Widgets API? If so,
|
|
3590
|
+
// can we do something more consistent?
|
|
3591
|
+
return Promise.resolve(null);
|
|
3592
|
+
}
|
|
3593
|
+
return Promise.resolve({
|
|
3594
|
+
min: aggregationFunctions.min(filteredFeatures, column),
|
|
3595
|
+
max: aggregationFunctions.max(filteredFeatures, column)
|
|
3596
|
+
});
|
|
3597
|
+
} catch (e) {
|
|
3598
|
+
return Promise.reject(e);
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
/****************************************************************************
|
|
3602
|
+
* INTERNAL
|
|
3603
|
+
*/
|
|
3604
|
+
_getFilteredFeatures(spatialFilter, filters, filterOwner) {
|
|
3605
|
+
assert(spatialFilter, 'spatialFilter required for tilesets');
|
|
3606
|
+
this._extractTileFeatures(spatialFilter);
|
|
3607
|
+
return applyFilters(this._features, getApplicableFilters(filterOwner, filters || this.props.filters), this.props.filtersLogicalOperator || 'and');
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
function assertColumn(features) {
|
|
3611
|
+
// TODO(cleanup): Can drop support for multiple column shapes here?
|
|
3612
|
+
// Due to the multiple column shape, we normalise it as an array with normalizeColumns
|
|
3613
|
+
const columns = Array.from(new Set([].slice.call(arguments, 1).map(normalizeColumns).flat()));
|
|
3614
|
+
const featureKeys = Object.keys(features[0]);
|
|
3615
|
+
const invalidColumns = columns.filter(column => !featureKeys.includes(column));
|
|
3616
|
+
if (invalidColumns.length) {
|
|
3617
|
+
throw new InvalidColumnError(`Missing column(s): ${invalidColumns.join(', ')}`);
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
function normalizeColumns(columns) {
|
|
3621
|
+
return Array.isArray(columns) ? columns : typeof columns === 'string' ? [columns] : [];
|
|
3622
|
+
}
|
|
3623
|
+
|
|
1553
3624
|
// deck.gl
|
|
1554
3625
|
// SPDX-License-Identifier: MIT
|
|
1555
3626
|
// Copyright (c) vis.gl contributors
|
|
1556
|
-
/* eslint-disable camelcase */
|
|
1557
3627
|
const h3QuerySource = function (options) {
|
|
1558
3628
|
try {
|
|
1559
3629
|
const {
|
|
@@ -1597,7 +3667,6 @@ const h3QuerySource = function (options) {
|
|
|
1597
3667
|
// deck.gl
|
|
1598
3668
|
// SPDX-License-Identifier: MIT
|
|
1599
3669
|
// Copyright (c) vis.gl contributors
|
|
1600
|
-
/* eslint-disable camelcase */
|
|
1601
3670
|
const h3TableSource = function (options) {
|
|
1602
3671
|
try {
|
|
1603
3672
|
const {
|
|
@@ -1675,7 +3744,6 @@ const rasterSource = function (options) {
|
|
|
1675
3744
|
// deck.gl
|
|
1676
3745
|
// SPDX-License-Identifier: MIT
|
|
1677
3746
|
// Copyright (c) vis.gl contributors
|
|
1678
|
-
/* eslint-disable camelcase */
|
|
1679
3747
|
const quadbinQuerySource = function (options) {
|
|
1680
3748
|
try {
|
|
1681
3749
|
const {
|
|
@@ -1719,7 +3787,6 @@ const quadbinQuerySource = function (options) {
|
|
|
1719
3787
|
// deck.gl
|
|
1720
3788
|
// SPDX-License-Identifier: MIT
|
|
1721
3789
|
// Copyright (c) vis.gl contributors
|
|
1722
|
-
/* eslint-disable camelcase */
|
|
1723
3790
|
const quadbinTableSource = function (options) {
|
|
1724
3791
|
try {
|
|
1725
3792
|
const {
|
|
@@ -1776,13 +3843,12 @@ const quadbinTilesetSource = function (options) {
|
|
|
1776
3843
|
// deck.gl
|
|
1777
3844
|
// SPDX-License-Identifier: MIT
|
|
1778
3845
|
// Copyright (c) vis.gl contributors
|
|
1779
|
-
/* eslint-disable camelcase */
|
|
1780
3846
|
const vectorQuerySource = function (options) {
|
|
1781
3847
|
try {
|
|
1782
3848
|
const {
|
|
1783
3849
|
columns,
|
|
1784
3850
|
filters,
|
|
1785
|
-
spatialDataColumn =
|
|
3851
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1786
3852
|
sqlQuery,
|
|
1787
3853
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1788
3854
|
queryParameters,
|
|
@@ -1825,13 +3891,12 @@ const vectorQuerySource = function (options) {
|
|
|
1825
3891
|
// deck.gl
|
|
1826
3892
|
// SPDX-License-Identifier: MIT
|
|
1827
3893
|
// Copyright (c) vis.gl contributors
|
|
1828
|
-
/* eslint-disable camelcase */
|
|
1829
3894
|
const vectorTableSource = function (options) {
|
|
1830
3895
|
try {
|
|
1831
3896
|
const {
|
|
1832
3897
|
columns,
|
|
1833
3898
|
filters,
|
|
1834
|
-
spatialDataColumn =
|
|
3899
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1835
3900
|
tableName,
|
|
1836
3901
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
1837
3902
|
aggregationExp
|
|
@@ -1937,25 +4002,41 @@ const query = function (options) {
|
|
|
1937
4002
|
|
|
1938
4003
|
exports.CartoAPIError = CartoAPIError;
|
|
1939
4004
|
exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
|
|
4005
|
+
exports.FEATURE_GEOM_PROPERTY = FEATURE_GEOM_PROPERTY;
|
|
1940
4006
|
exports.SOURCE_DEFAULTS = SOURCE_DEFAULTS;
|
|
1941
|
-
exports.WidgetBaseSource = WidgetBaseSource;
|
|
1942
4007
|
exports.WidgetQuerySource = WidgetQuerySource;
|
|
4008
|
+
exports.WidgetRemoteSource = WidgetRemoteSource;
|
|
4009
|
+
exports.WidgetSource = WidgetSource;
|
|
1943
4010
|
exports.WidgetTableSource = WidgetTableSource;
|
|
4011
|
+
exports.WidgetTilesetSource = WidgetTilesetSource;
|
|
4012
|
+
exports._buildFeatureFilter = _buildFeatureFilter;
|
|
1944
4013
|
exports._getHexagonResolution = _getHexagonResolution;
|
|
1945
4014
|
exports.addFilter = addFilter;
|
|
4015
|
+
exports.aggregate = aggregate;
|
|
4016
|
+
exports.aggregationFunctions = aggregationFunctions;
|
|
4017
|
+
exports.applyFilters = applyFilters;
|
|
4018
|
+
exports.applySorting = applySorting;
|
|
1946
4019
|
exports.boundaryQuerySource = boundaryQuerySource;
|
|
1947
4020
|
exports.boundaryTableSource = boundaryTableSource;
|
|
4021
|
+
exports.buildBinaryFeatureFilter = buildBinaryFeatureFilter;
|
|
1948
4022
|
exports.buildPublicMapUrl = buildPublicMapUrl;
|
|
1949
4023
|
exports.buildStatsUrl = buildStatsUrl;
|
|
1950
4024
|
exports.clearFilters = clearFilters;
|
|
1951
4025
|
exports.createPolygonSpatialFilter = createPolygonSpatialFilter;
|
|
1952
4026
|
exports.createViewportSpatialFilter = createViewportSpatialFilter;
|
|
4027
|
+
exports.filterFunctions = filterFunctions;
|
|
4028
|
+
exports.geojsonFeatures = geojsonFeatures;
|
|
1953
4029
|
exports.getClient = getClient;
|
|
4030
|
+
exports.getDataFilterExtensionProps = getDataFilterExtensionProps;
|
|
1954
4031
|
exports.getFilter = getFilter;
|
|
4032
|
+
exports.groupValuesByColumn = groupValuesByColumn;
|
|
4033
|
+
exports.groupValuesByDateColumn = groupValuesByDateColumn;
|
|
1955
4034
|
exports.h3QuerySource = h3QuerySource;
|
|
1956
4035
|
exports.h3TableSource = h3TableSource;
|
|
1957
4036
|
exports.h3TilesetSource = h3TilesetSource;
|
|
1958
4037
|
exports.hasFilter = hasFilter;
|
|
4038
|
+
exports.histogram = histogram;
|
|
4039
|
+
exports.makeIntervalComplete = makeIntervalComplete;
|
|
1959
4040
|
exports.quadbinQuerySource = quadbinQuerySource;
|
|
1960
4041
|
exports.quadbinTableSource = quadbinTableSource;
|
|
1961
4042
|
exports.quadbinTilesetSource = quadbinTilesetSource;
|
|
@@ -1963,7 +4044,12 @@ exports.query = query;
|
|
|
1963
4044
|
exports.rasterSource = rasterSource;
|
|
1964
4045
|
exports.removeFilter = removeFilter;
|
|
1965
4046
|
exports.requestWithParameters = requestWithParameters;
|
|
4047
|
+
exports.scatterPlot = scatterPlot;
|
|
1966
4048
|
exports.setClient = setClient;
|
|
4049
|
+
exports.tileFeatures = tileFeatures;
|
|
4050
|
+
exports.tileFeaturesGeometries = tileFeaturesGeometries;
|
|
4051
|
+
exports.tileFeaturesSpatialIndex = tileFeaturesSpatialIndex;
|
|
4052
|
+
exports.transformToTileCoords = transformToTileCoords;
|
|
1967
4053
|
exports.vectorQuerySource = vectorQuerySource;
|
|
1968
4054
|
exports.vectorTableSource = vectorTableSource;
|
|
1969
4055
|
exports.vectorTilesetSource = vectorTilesetSource;
|