@carto/api-client 0.5.0-alpha.3 → 0.5.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- 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 +2324 -2188
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +2196 -2055
- 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 +6 -6
- package/build/deck/get-data-filter-extension-props.d.ts +18 -0
- package/build/deck/index.d.ts +1 -0
- package/build/filters/Filter.d.ts +15 -3
- package/build/filters/FilterTypes.d.ts +1 -1
- package/build/filters/geosjonFeatures.d.ts +2 -2
- package/build/filters/tileFeatures.d.ts +8 -8
- package/build/filters/tileFeaturesRaster.d.ts +3 -3
- package/build/filters.d.ts +2 -2
- package/build/geo.d.ts +1 -1
- package/build/index.d.ts +1 -0
- package/build/models/common.d.ts +4 -3
- package/build/models/model.d.ts +2 -2
- package/build/operations/aggregation.d.ts +5 -5
- package/build/operations/applySorting.d.ts +3 -3
- package/build/operations/groupBy.d.ts +4 -4
- package/build/operations/groupByDate.d.ts +1 -1
- package/build/operations/histogram.d.ts +3 -3
- package/build/operations/scatterPlot.d.ts +3 -3
- 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 +2 -2
- 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 +2 -2
- package/build/sources/raster-source.d.ts +2 -2
- 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 +2 -2
- package/build/spatial-index.d.ts +3 -3
- package/build/types-internal.d.ts +5 -5
- package/build/types.d.ts +15 -15
- package/build/utils/makeIntervalComplete.d.ts +1 -1
- package/build/utils.d.ts +3 -3
- package/build/widget-sources/types.d.ts +4 -2
- package/build/widget-sources/widget-query-source.d.ts +2 -1
- package/build/widget-sources/widget-remote-source.d.ts +3 -0
- package/build/widget-sources/widget-source.d.ts +3 -3
- package/build/widget-sources/widget-table-source.d.ts +2 -1
- package/build/widget-sources/widget-tileset-source.d.ts +22 -24
- package/package.json +36 -30
- 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 +6 -6
- package/src/deck/get-data-filter-extension-props.ts +146 -0
- package/src/deck/index.ts +1 -0
- package/src/filters/Filter.ts +18 -8
- package/src/filters/FilterTypes.ts +2 -2
- package/src/filters/geosjonFeatures.ts +2 -2
- package/src/filters/tileFeatures.ts +13 -19
- package/src/filters/tileFeaturesRaster.ts +7 -7
- package/src/filters.ts +4 -4
- package/src/geo.ts +12 -14
- package/src/index.ts +1 -0
- package/src/models/common.ts +9 -7
- package/src/models/model.ts +3 -4
- package/src/operations/aggregation.ts +5 -5
- package/src/operations/applySorting.ts +4 -4
- package/src/operations/groupBy.ts +4 -4
- package/src/operations/groupByDate.ts +1 -1
- package/src/operations/histogram.ts +4 -4
- package/src/operations/scatterPlot.ts +4 -4
- 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 +4 -4
- 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 +4 -4
- package/src/sources/raster-source.ts +8 -4
- package/src/sources/types.ts +3 -3
- package/src/sources/vector-query-source.ts +2 -3
- package/src/sources/vector-table-source.ts +2 -3
- package/src/sources/vector-tileset-source.ts +5 -5
- package/src/spatial-index.ts +4 -4
- package/src/types-internal.ts +5 -5
- package/src/types.ts +15 -15
- package/src/utils/makeIntervalComplete.ts +1 -1
- package/src/utils.ts +3 -3
- package/src/widget-sources/types.ts +5 -3
- package/src/widget-sources/widget-query-source.ts +6 -2
- package/src/widget-sources/widget-remote-source.ts +31 -16
- package/src/widget-sources/widget-source.ts +13 -4
- package/src/widget-sources/widget-table-source.ts +6 -2
- package/src/widget-sources/widget-tileset-source.ts +132 -81
package/build/api-client.cjs
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
var bboxClip = require('@turf/bbox-clip');
|
|
2
|
-
var bboxPolygon = require('@turf/bbox-polygon');
|
|
3
|
-
var union = require('@turf/union');
|
|
4
|
-
var invariant = require('@turf/invariant');
|
|
5
|
-
var helpers = require('@turf/helpers');
|
|
6
1
|
var intersects = require('@turf/boolean-intersects');
|
|
2
|
+
var bboxPolygon = require('@turf/bbox-polygon');
|
|
7
3
|
var booleanWithin = require('@turf/boolean-within');
|
|
8
4
|
var intersect = require('@turf/intersect');
|
|
5
|
+
var helpers = require('@turf/helpers');
|
|
9
6
|
var quadbin = require('quadbin');
|
|
7
|
+
var bboxClip = require('@turf/bbox-clip');
|
|
10
8
|
var h3Js = require('h3-js');
|
|
9
|
+
var union = require('@turf/union');
|
|
10
|
+
var invariant = require('@turf/invariant');
|
|
11
|
+
var booleanEqual = require('@turf/boolean-equal');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @internal
|
|
14
|
-
* @
|
|
15
|
+
* @privateRemarks Source: @carto/react-core, @carto/constants, @deck.gl/carto
|
|
15
16
|
*/
|
|
16
17
|
let client = 'deck-gl-carto';
|
|
17
18
|
/**
|
|
18
19
|
* Returns current client ID, used to categorize API requests. For internal use only.
|
|
19
20
|
*
|
|
20
21
|
* @internal
|
|
21
|
-
* @
|
|
22
|
+
* @privateRemarks Source: @carto/react-core
|
|
22
23
|
*/
|
|
23
24
|
function getClient() {
|
|
24
25
|
return client;
|
|
@@ -27,7 +28,7 @@ function getClient() {
|
|
|
27
28
|
* Sets current client ID, used to categorize API requests. For internal use only.
|
|
28
29
|
*
|
|
29
30
|
* @internal
|
|
30
|
-
* @
|
|
31
|
+
* @privateRemarks Source: @carto/react-core
|
|
31
32
|
*/
|
|
32
33
|
function setClient(c) {
|
|
33
34
|
client = c;
|
|
@@ -45,7 +46,7 @@ function setClient(c) {
|
|
|
45
46
|
* };
|
|
46
47
|
* ```
|
|
47
48
|
*
|
|
48
|
-
* @
|
|
49
|
+
* @privateRemarks Source: @carto/react-api, @deck.gl/carto
|
|
49
50
|
*/
|
|
50
51
|
exports.FilterType = void 0;
|
|
51
52
|
(function (FilterType) {
|
|
@@ -57,16 +58,16 @@ exports.FilterType = void 0;
|
|
|
57
58
|
FilterType["TIME"] = "time";
|
|
58
59
|
FilterType["STRING_SEARCH"] = "stringSearch";
|
|
59
60
|
})(exports.FilterType || (exports.FilterType = {}));
|
|
60
|
-
/** @
|
|
61
|
+
/** @privateRemarks Source: @carto/constants */
|
|
61
62
|
exports.ApiVersion = void 0;
|
|
62
63
|
(function (ApiVersion) {
|
|
63
64
|
ApiVersion["V1"] = "v1";
|
|
64
65
|
ApiVersion["V2"] = "v2";
|
|
65
66
|
ApiVersion["V3"] = "v3";
|
|
66
67
|
})(exports.ApiVersion || (exports.ApiVersion = {}));
|
|
67
|
-
/** @
|
|
68
|
+
/** @privateRemarks Source: @carto/constants, @deck.gl/carto */
|
|
68
69
|
const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
|
|
69
|
-
/** @
|
|
70
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
70
71
|
exports.TileFormat = void 0;
|
|
71
72
|
(function (TileFormat) {
|
|
72
73
|
TileFormat["MVT"] = "mvt";
|
|
@@ -74,14 +75,14 @@ exports.TileFormat = void 0;
|
|
|
74
75
|
TileFormat["GEOJSON"] = "geojson";
|
|
75
76
|
TileFormat["BINARY"] = "binary";
|
|
76
77
|
})(exports.TileFormat || (exports.TileFormat = {}));
|
|
77
|
-
/** @
|
|
78
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
78
79
|
exports.SpatialIndex = void 0;
|
|
79
80
|
(function (SpatialIndex) {
|
|
80
81
|
SpatialIndex["H3"] = "h3";
|
|
81
82
|
SpatialIndex["S2"] = "s2";
|
|
82
83
|
SpatialIndex["QUADBIN"] = "quadbin";
|
|
83
84
|
})(exports.SpatialIndex || (exports.SpatialIndex = {}));
|
|
84
|
-
/** @
|
|
85
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
85
86
|
exports.Provider = void 0;
|
|
86
87
|
(function (Provider) {
|
|
87
88
|
Provider["BIGQUERY"] = "bigquery";
|
|
@@ -92,2312 +93,2438 @@ exports.Provider = void 0;
|
|
|
92
93
|
Provider["DATABRICKS_REST"] = "databricksRest";
|
|
93
94
|
})(exports.Provider || (exports.Provider = {}));
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* @internal
|
|
100
|
-
*/
|
|
101
|
-
function getApplicableFilters(owner, filters) {
|
|
102
|
-
if (!filters) return {};
|
|
103
|
-
const applicableFilters = {};
|
|
104
|
-
for (const column in filters) {
|
|
105
|
-
for (const type in filters[column]) {
|
|
106
|
-
if (!isFilterType(type)) continue;
|
|
107
|
-
const filter = filters[column][type];
|
|
108
|
-
const isApplicable = !owner || !filter?.owner || filter?.owner !== owner;
|
|
109
|
-
if (filter && isApplicable) {
|
|
110
|
-
applicableFilters[column] ||= {};
|
|
111
|
-
applicableFilters[column][type] = filter;
|
|
112
|
-
}
|
|
96
|
+
function makeIntervalComplete(intervals) {
|
|
97
|
+
return intervals.map(val => {
|
|
98
|
+
if (val[0] === undefined || val[0] === null) {
|
|
99
|
+
return [Number.MIN_SAFE_INTEGER, val[1]];
|
|
113
100
|
}
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
if (val[1] === undefined || val[1] === null) {
|
|
102
|
+
return [val[0], Number.MAX_SAFE_INTEGER];
|
|
103
|
+
}
|
|
104
|
+
return val;
|
|
105
|
+
});
|
|
116
106
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
} else if (typeof el !== 'object') {
|
|
128
|
-
return el;
|
|
129
|
-
}
|
|
130
|
-
return Object.entries(el).reduce((acc, _ref) => {
|
|
131
|
-
let [key, value] = _ref;
|
|
132
|
-
acc[key.toLowerCase()] = typeof value === 'object' && value ? normalizeObjectKeys(value) : value;
|
|
133
|
-
return acc;
|
|
134
|
-
}, {});
|
|
107
|
+
|
|
108
|
+
const filterFunctions = {
|
|
109
|
+
[exports.FilterType.IN]: filterIn,
|
|
110
|
+
[exports.FilterType.BETWEEN]: filterBetween,
|
|
111
|
+
[exports.FilterType.TIME]: filterTime,
|
|
112
|
+
[exports.FilterType.CLOSED_OPEN]: filterClosedOpen,
|
|
113
|
+
[exports.FilterType.STRING_SEARCH]: filterStringSearch
|
|
114
|
+
};
|
|
115
|
+
function filterIn(filterValues, featureValue) {
|
|
116
|
+
return filterValues.includes(featureValue);
|
|
135
117
|
}
|
|
136
|
-
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
118
|
+
// FilterTypes.BETWEEN
|
|
119
|
+
function filterBetween(filterValues, featureValue) {
|
|
120
|
+
const checkRange = range => {
|
|
121
|
+
const [lowerBound, upperBound] = range;
|
|
122
|
+
return featureValue >= lowerBound && featureValue <= upperBound;
|
|
123
|
+
};
|
|
124
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
141
125
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
super(`${InvalidColumnError.NAME}: ${message}`);
|
|
149
|
-
this.name = InvalidColumnError.NAME;
|
|
150
|
-
}
|
|
151
|
-
static is(error) {
|
|
152
|
-
return error instanceof InvalidColumnError || error.message?.includes(InvalidColumnError.NAME);
|
|
126
|
+
function filterTime(filterValues, featureValue) {
|
|
127
|
+
const featureValueAsTimestamp = new Date(featureValue).getTime();
|
|
128
|
+
if (isFinite(featureValueAsTimestamp)) {
|
|
129
|
+
return filterBetween(filterValues, featureValueAsTimestamp);
|
|
130
|
+
} else {
|
|
131
|
+
throw new Error(`Column used to filter by time isn't well formatted.`);
|
|
153
132
|
}
|
|
154
133
|
}
|
|
155
|
-
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
134
|
+
// FilterTypes.CLOSED_OPEN
|
|
135
|
+
function filterClosedOpen(filterValues, featureValue) {
|
|
136
|
+
const checkRange = range => {
|
|
137
|
+
const [lowerBound, upperBound] = range;
|
|
138
|
+
return featureValue >= lowerBound && featureValue < upperBound;
|
|
139
|
+
};
|
|
140
|
+
return makeIntervalComplete(filterValues).some(checkRange);
|
|
141
|
+
}
|
|
142
|
+
// FilterTypes.STRING_SEARCH
|
|
143
|
+
function filterStringSearch(filterValues, featureValue, params) {
|
|
144
|
+
if (params === void 0) {
|
|
145
|
+
params = {};
|
|
159
146
|
}
|
|
160
|
-
|
|
147
|
+
const normalizedFeatureValue = normalize(featureValue, params);
|
|
148
|
+
const stringRegExp = params.useRegExp ? filterValues : filterValues.map(filterValue => {
|
|
149
|
+
let stringRegExp = escapeRegExp(normalize(filterValue, params));
|
|
150
|
+
if (params.mustStart) stringRegExp = `^${stringRegExp}`;
|
|
151
|
+
if (params.mustEnd) stringRegExp = `${stringRegExp}$`;
|
|
152
|
+
return stringRegExp;
|
|
153
|
+
});
|
|
154
|
+
const regex = new RegExp(stringRegExp.join('|'), params.caseSensitive ? 'g' : 'gi');
|
|
155
|
+
return !!normalizedFeatureValue.match(regex);
|
|
156
|
+
}
|
|
157
|
+
// Aux
|
|
158
|
+
const specialCharRegExp = /[.*+?^${}()|[\]\\]/g;
|
|
159
|
+
const normalizeRegExp = /\p{Diacritic}/gu;
|
|
160
|
+
function escapeRegExp(value) {
|
|
161
|
+
return value.replace(specialCharRegExp, '\\$&');
|
|
162
|
+
}
|
|
163
|
+
function normalize(data, params) {
|
|
164
|
+
let normalizedData = String(data);
|
|
165
|
+
if (!params.keepSpecialCharacters) normalizedData = normalizedData.normalize('NFD').replace(normalizeRegExp, '');
|
|
166
|
+
return normalizedData;
|
|
161
167
|
}
|
|
162
|
-
/** @internal */
|
|
163
|
-
const isObject = x => x !== null && typeof x === 'object';
|
|
164
|
-
/** @internal */
|
|
165
|
-
const isPureObject = x => isObject(x) && x.constructor === {}.constructor;
|
|
166
168
|
|
|
169
|
+
const LOGICAL_OPERATOR_METHODS = {
|
|
170
|
+
and: 'every',
|
|
171
|
+
or: 'some'
|
|
172
|
+
};
|
|
173
|
+
function passesFilter(columns, filters, feature, filtersLogicalOperator) {
|
|
174
|
+
const method = LOGICAL_OPERATOR_METHODS[filtersLogicalOperator];
|
|
175
|
+
return columns[method](column => {
|
|
176
|
+
const columnFilters = filters[column];
|
|
177
|
+
const columnFilterTypes = Object.keys(columnFilters);
|
|
178
|
+
if (!feature || feature[column] === null || feature[column] === undefined) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return columnFilterTypes.every(filter => {
|
|
182
|
+
const filterFunction = filterFunctions[filter];
|
|
183
|
+
if (!filterFunction) {
|
|
184
|
+
throw new Error(`"${filter}" filter is not implemented.`);
|
|
185
|
+
}
|
|
186
|
+
return filterFunction(columnFilters[filter].values, feature[column], columnFilters[filter].params);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
167
190
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
191
|
+
* @internal
|
|
192
|
+
* @privateRemarks Exported for use in @deck.gl/carto's getDataFilterExtensionProps.
|
|
170
193
|
*/
|
|
171
|
-
function
|
|
194
|
+
function _buildFeatureFilter(_ref) {
|
|
172
195
|
let {
|
|
173
|
-
|
|
174
|
-
type,
|
|
175
|
-
|
|
176
|
-
owner
|
|
196
|
+
filters = {},
|
|
197
|
+
type = 'boolean',
|
|
198
|
+
filtersLogicalOperator = 'and'
|
|
177
199
|
} = _ref;
|
|
178
|
-
|
|
179
|
-
|
|
200
|
+
const columns = Object.keys(filters);
|
|
201
|
+
if (!columns.length) {
|
|
202
|
+
return () => type === 'number' ? 1 : true;
|
|
180
203
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
return feature => {
|
|
205
|
+
const f = feature.properties || feature;
|
|
206
|
+
const featurePassesFilter = passesFilter(columns, filters, f, filtersLogicalOperator);
|
|
207
|
+
return type === 'number' ? Number(featurePassesFilter) : featurePassesFilter;
|
|
184
208
|
};
|
|
185
|
-
filters[column][type] = filter;
|
|
186
|
-
return filters;
|
|
187
209
|
}
|
|
188
210
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* `column` and `owner` are specified, then only filters for that column
|
|
192
|
-
* associated with the owner are removed.
|
|
211
|
+
* Apply certain filters to a collection of features.
|
|
212
|
+
* @internal
|
|
193
213
|
*/
|
|
194
|
-
function
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
const filter = filters[column];
|
|
200
|
-
if (!filter) {
|
|
201
|
-
return filters;
|
|
202
|
-
}
|
|
203
|
-
if (owner) {
|
|
204
|
-
for (const type of Object.values(exports.FilterType)) {
|
|
205
|
-
if (owner === filter[type]?.owner) {
|
|
206
|
-
delete filter[type];
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (!owner || isEmptyObject(filter)) {
|
|
211
|
-
delete filters[column];
|
|
212
|
-
}
|
|
213
|
-
return filters;
|
|
214
|
+
function applyFilters(features, filters, filtersLogicalOperator) {
|
|
215
|
+
return Object.keys(filters).length ? features.filter(_buildFeatureFilter({
|
|
216
|
+
filters,
|
|
217
|
+
filtersLogicalOperator
|
|
218
|
+
})) : features;
|
|
214
219
|
}
|
|
215
220
|
/**
|
|
216
|
-
*
|
|
221
|
+
* Binary.
|
|
222
|
+
* @internal
|
|
217
223
|
*/
|
|
218
|
-
function
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
function buildBinaryFeatureFilter(_ref2) {
|
|
225
|
+
let {
|
|
226
|
+
filters = {}
|
|
227
|
+
} = _ref2;
|
|
228
|
+
const columns = Object.keys(filters);
|
|
229
|
+
if (!columns.length) {
|
|
230
|
+
return () => 1;
|
|
221
231
|
}
|
|
222
|
-
return filters;
|
|
232
|
+
return (featureIdIdx, binaryData) => passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
|
|
223
233
|
}
|
|
224
|
-
function
|
|
234
|
+
function getValueFromNumericProps(featureIdIdx, binaryData, _ref3) {
|
|
225
235
|
let {
|
|
226
|
-
column
|
|
227
|
-
owner
|
|
236
|
+
column
|
|
228
237
|
} = _ref3;
|
|
229
|
-
|
|
230
|
-
if (!filter) {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
if (!owner) {
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
for (const type of Object.values(exports.FilterType)) {
|
|
237
|
-
if (owner === filter[type]?.owner) {
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return false;
|
|
238
|
+
return binaryData.numericProps?.[column]?.value[featureIdIdx];
|
|
242
239
|
}
|
|
243
|
-
function
|
|
240
|
+
function getValueFromProperties(featureIdIdx, binaryData, _ref4) {
|
|
244
241
|
let {
|
|
245
|
-
column
|
|
246
|
-
type,
|
|
247
|
-
owner
|
|
242
|
+
column
|
|
248
243
|
} = _ref4;
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
244
|
+
const propertyIdx = binaryData.featureIds.value[featureIdIdx];
|
|
245
|
+
return binaryData.properties[propertyIdx]?.[column];
|
|
246
|
+
}
|
|
247
|
+
const GET_VALUE_BY_BINARY_PROP = {
|
|
248
|
+
properties: getValueFromProperties,
|
|
249
|
+
numericProps: getValueFromNumericProps
|
|
250
|
+
};
|
|
251
|
+
function getBinaryPropertyByFilterValues(filterValues) {
|
|
252
|
+
return typeof filterValues.flat()[0] === 'string' ? 'properties' : 'numericProps';
|
|
253
|
+
}
|
|
254
|
+
function getFeatureValue(featureIdIdx, binaryData, filter) {
|
|
255
|
+
const {
|
|
256
|
+
column,
|
|
257
|
+
values
|
|
258
|
+
} = filter;
|
|
259
|
+
const binaryProp = getBinaryPropertyByFilterValues(values);
|
|
260
|
+
const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
|
|
261
|
+
return getFeatureValueFn(featureIdIdx, binaryData, {
|
|
262
|
+
column
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData) {
|
|
266
|
+
return columns.every(column => {
|
|
267
|
+
const columnFilters = filters[column];
|
|
268
|
+
return Object.entries(columnFilters).every(_ref5 => {
|
|
269
|
+
let [type, {
|
|
270
|
+
values
|
|
271
|
+
}] = _ref5;
|
|
272
|
+
const filterFn = filterFunctions[type];
|
|
273
|
+
if (!filterFn) {
|
|
274
|
+
throw new Error(`"${type}" filter is not implemented.`);
|
|
275
|
+
}
|
|
276
|
+
if (!values) return 0;
|
|
277
|
+
const featureValue = getFeatureValue(featureIdIdx, binaryData, {
|
|
278
|
+
type: type,
|
|
279
|
+
column,
|
|
280
|
+
values
|
|
281
|
+
});
|
|
282
|
+
if (featureValue === undefined || featureValue === null) return 0;
|
|
283
|
+
return filterFn(values, featureValue);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function geojsonFeatures(_ref) {
|
|
289
|
+
let {
|
|
290
|
+
geojson,
|
|
291
|
+
spatialFilter,
|
|
292
|
+
uniqueIdProperty
|
|
293
|
+
} = _ref;
|
|
294
|
+
let uniqueIdx = 0;
|
|
295
|
+
const map = new Map();
|
|
296
|
+
if (!spatialFilter) {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
for (const feature of geojson.features) {
|
|
300
|
+
const uniqueId = uniqueIdProperty ? feature.properties[uniqueIdProperty] : ++uniqueIdx;
|
|
301
|
+
if (!map.has(uniqueId) && intersects(spatialFilter, feature)) {
|
|
302
|
+
map.set(uniqueId, feature.properties);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return Array.from(map.values());
|
|
257
306
|
}
|
|
258
307
|
|
|
308
|
+
// math.gl
|
|
309
|
+
// SPDX-License-Identifier: MIT
|
|
310
|
+
// Copyright (c) vis.gl contributors
|
|
311
|
+
const DEFAULT_CONFIG = {
|
|
312
|
+
EPSILON: 1e-12,
|
|
313
|
+
debug: false,
|
|
314
|
+
precision: 4,
|
|
315
|
+
printTypes: false,
|
|
316
|
+
printDegrees: false,
|
|
317
|
+
printRowMajor: true,
|
|
318
|
+
_cartographicRadians: false
|
|
319
|
+
};
|
|
320
|
+
// Configuration is truly global as of v3.6 to ensure single config even if multiple copies of math.gl
|
|
321
|
+
// Multiple copies of config can be quite tricky to debug...
|
|
322
|
+
globalThis.mathgl = globalThis.mathgl || {
|
|
323
|
+
config: {
|
|
324
|
+
...DEFAULT_CONFIG
|
|
325
|
+
}
|
|
326
|
+
};
|
|
259
327
|
/**
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
* If the viewport extends beyond longitude range [-180, +180], the polygon
|
|
266
|
-
* may be reformatted for compatibility with CARTO APIs.
|
|
328
|
+
* Check if value is an "array"
|
|
329
|
+
* Returns `true` if value is either an array or a typed array
|
|
330
|
+
* Note: returns `false` for `ArrayBuffer` and `DataView` instances
|
|
331
|
+
* @note isTypedArray and isNumericArray are often more useful in TypeScript
|
|
267
332
|
*/
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
333
|
+
function isArray(value) {
|
|
334
|
+
return Array.isArray(value) || ArrayBuffer.isView(value) && !(value instanceof DataView);
|
|
335
|
+
}
|
|
336
|
+
function lerp(a, b, t) {
|
|
337
|
+
if (isArray(a)) {
|
|
338
|
+
return a.map((ai, i) => lerp(ai, b[i], t));
|
|
339
|
+
}
|
|
340
|
+
return t * b + (1 - t) * a;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Replacement for the external assert method to reduce bundle size
|
|
344
|
+
// Note: We don't use the second "message" argument in calling code,
|
|
345
|
+
// so no need to support it here
|
|
346
|
+
function assert$1(condition, message) {
|
|
347
|
+
if (!condition) {
|
|
348
|
+
throw new Error(message || '@math.gl/web-mercator: assertion failed.');
|
|
271
349
|
}
|
|
272
|
-
return createPolygonSpatialFilter(bboxPolygon(viewport).geometry);
|
|
273
350
|
}
|
|
351
|
+
|
|
352
|
+
// TODO - THE UTILITIES IN THIS FILE SHOULD BE IMPORTED FROM WEB-MERCATOR-VIEWPORT MODULE
|
|
353
|
+
// CONSTANTS
|
|
354
|
+
const PI = Math.PI;
|
|
355
|
+
const PI_4 = PI / 4;
|
|
356
|
+
const DEGREES_TO_RADIANS = PI / 180;
|
|
357
|
+
const RADIANS_TO_DEGREES = 180 / PI;
|
|
358
|
+
const TILE_SIZE = 512;
|
|
274
359
|
/**
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
360
|
+
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
|
|
361
|
+
* Performs the nonlinear part of the web mercator projection.
|
|
362
|
+
* Remaining projection is done with 4x4 matrices which also handles
|
|
363
|
+
* perspective.
|
|
364
|
+
*
|
|
365
|
+
* @param lngLat - [lng, lat] coordinates
|
|
366
|
+
* Specifies a point on the sphere to project onto the map.
|
|
367
|
+
* @return [x,y] coordinates.
|
|
279
368
|
*/
|
|
280
|
-
function
|
|
281
|
-
|
|
369
|
+
function lngLatToWorld(lngLat) {
|
|
370
|
+
const [lng, lat] = lngLat;
|
|
371
|
+
assert$1(Number.isFinite(lng));
|
|
372
|
+
assert$1(Number.isFinite(lat) && lat >= -90 && lat <= 90, 'invalid latitude');
|
|
373
|
+
const lambda2 = lng * DEGREES_TO_RADIANS;
|
|
374
|
+
const phi2 = lat * DEGREES_TO_RADIANS;
|
|
375
|
+
const x = TILE_SIZE * (lambda2 + PI) / (2 * PI);
|
|
376
|
+
const y = TILE_SIZE * (PI + Math.log(Math.tan(PI_4 + phi2 * 0.5))) / (2 * PI);
|
|
377
|
+
return [x, y];
|
|
282
378
|
}
|
|
283
379
|
/**
|
|
284
|
-
*
|
|
285
|
-
* In this case the spatial filter parameter for widget calculation is removed.
|
|
380
|
+
* Unproject world point [x,y] on map onto {lat, lon} on sphere
|
|
286
381
|
*
|
|
287
|
-
* @
|
|
382
|
+
* @param xy - array with [x,y] members
|
|
383
|
+
* representing point on projected map plane
|
|
384
|
+
* @return - array with [x,y] of point on sphere.
|
|
385
|
+
* Has toArray method if you need a GeoJSON Array.
|
|
386
|
+
* Per cartographic tradition, lat and lon are specified as degrees.
|
|
288
387
|
*/
|
|
289
|
-
function
|
|
290
|
-
const [
|
|
291
|
-
|
|
388
|
+
function worldToLngLat(xy) {
|
|
389
|
+
const [x, y] = xy;
|
|
390
|
+
const lambda2 = x / TILE_SIZE * (2 * PI) - PI;
|
|
391
|
+
const phi2 = 2 * (Math.atan(Math.exp(y / TILE_SIZE * (2 * PI) - PI)) - PI_4);
|
|
392
|
+
return [lambda2 * RADIANS_TO_DEGREES, phi2 * RADIANS_TO_DEGREES];
|
|
292
393
|
}
|
|
394
|
+
|
|
395
|
+
const TRANSFORM_FN$1 = {
|
|
396
|
+
Point: transformPoint$1,
|
|
397
|
+
MultiPoint: transformMultiPoint$1,
|
|
398
|
+
LineString: transformLineString$1,
|
|
399
|
+
MultiLineString: transformMultiLineString$1,
|
|
400
|
+
Polygon: transformPolygon$1,
|
|
401
|
+
MultiPolygon: transformMultiPolygon$1
|
|
402
|
+
};
|
|
293
403
|
/**
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* back to the valid range and unioned to the polygons inide that range.
|
|
297
|
-
*
|
|
298
|
-
* It results in a Polygon or MultiPolygon strictly inside the validity range.
|
|
404
|
+
* Transform WGS84 coordinates to tile coords.
|
|
405
|
+
* 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)
|
|
299
406
|
*
|
|
300
|
-
* @
|
|
407
|
+
* @param geometry - any valid geojson geometry
|
|
408
|
+
* @param bbox - geojson bbox
|
|
301
409
|
*/
|
|
302
|
-
function
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const worldWestClip = _clean(bboxClip(geometryTxWest, WORLD).geometry);
|
|
310
|
-
if (worldWestClip) {
|
|
311
|
-
const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldWestClip)]);
|
|
312
|
-
const merged = union(collection);
|
|
313
|
-
result = merged ? _clean(merged.geometry) : result;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (result && geometryTxEast) {
|
|
317
|
-
const worldEastClip = _clean(bboxClip(geometryTxEast, WORLD).geometry);
|
|
318
|
-
if (worldEastClip) {
|
|
319
|
-
const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldEastClip)]);
|
|
320
|
-
const merged = union(collection);
|
|
321
|
-
result = merged ? _clean(merged.geometry) : result;
|
|
322
|
-
}
|
|
410
|
+
function transformToTileCoords(geometry, bbox) {
|
|
411
|
+
const [west, south, east, north] = bbox;
|
|
412
|
+
const nw = projectFlat([west, north]);
|
|
413
|
+
const se = projectFlat([east, south]);
|
|
414
|
+
const projectedBbox = [nw, se];
|
|
415
|
+
if (geometry.type === 'GeometryCollection') {
|
|
416
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
323
417
|
}
|
|
324
|
-
|
|
418
|
+
const transformFn = TRANSFORM_FN$1[geometry.type];
|
|
419
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
420
|
+
return {
|
|
421
|
+
...geometry,
|
|
422
|
+
coordinates
|
|
423
|
+
};
|
|
325
424
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
425
|
+
function transformPoint$1(_ref, _ref2) {
|
|
426
|
+
let [pointX, pointY] = _ref;
|
|
427
|
+
let [nw, se] = _ref2;
|
|
428
|
+
const x = inverseLerp(nw[0], se[0], pointX);
|
|
429
|
+
const y = inverseLerp(nw[1], se[1], pointY);
|
|
430
|
+
return [x, y];
|
|
330
431
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const coords = ccc.map(_cleanPolygonCoords).filter(cc => cc);
|
|
334
|
-
return coords.length > 0 ? coords : null;
|
|
432
|
+
function getPoints$1(geometry, bbox) {
|
|
433
|
+
return geometry.map(g => transformPoint$1(projectFlat(g), bbox));
|
|
335
434
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (!geometry) {
|
|
339
|
-
return null;
|
|
340
|
-
}
|
|
341
|
-
if (_isPolygon(geometry)) {
|
|
342
|
-
const coords = _cleanPolygonCoords(geometry.coordinates);
|
|
343
|
-
return coords ? helpers.polygon(coords).geometry : null;
|
|
344
|
-
}
|
|
345
|
-
if (_isMultiPolygon(geometry)) {
|
|
346
|
-
const coords = _cleanMultiPolygonCoords(geometry.coordinates);
|
|
347
|
-
return coords ? helpers.multiPolygon(coords).geometry : null;
|
|
348
|
-
}
|
|
349
|
-
return null;
|
|
435
|
+
function transformMultiPoint$1(multiPoint, bbox) {
|
|
436
|
+
return getPoints$1(multiPoint, bbox);
|
|
350
437
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return cc.map(c => [c[0] + distance, c[1]]);
|
|
438
|
+
function transformLineString$1(line, bbox) {
|
|
439
|
+
return getPoints$1(line, bbox);
|
|
354
440
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return ccc.map(cc => _txContourCoords(cc, distance));
|
|
441
|
+
function transformMultiLineString$1(multiLineString, bbox) {
|
|
442
|
+
return multiLineString.map(lineString => transformLineString$1(lineString, bbox));
|
|
358
443
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return cccc.map(ccc => _txPolygonCoords(ccc, distance));
|
|
444
|
+
function transformPolygon$1(polygon, bbox) {
|
|
445
|
+
return polygon.map(polygonRing => getPoints$1(polygonRing, bbox));
|
|
362
446
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (geometry && invariant.getType(geometry) === 'Polygon') {
|
|
366
|
-
const coords = _txPolygonCoords(geometry.coordinates, distance);
|
|
367
|
-
return helpers.polygon(coords).geometry;
|
|
368
|
-
} else if (geometry && invariant.getType(geometry) === 'MultiPolygon') {
|
|
369
|
-
const coords = _txMultiPolygonCoords(geometry.coordinates, distance);
|
|
370
|
-
return helpers.multiPolygon(coords).geometry;
|
|
371
|
-
} else {
|
|
372
|
-
return null;
|
|
373
|
-
}
|
|
447
|
+
function transformMultiPolygon$1(multiPolygon, bbox) {
|
|
448
|
+
return multiPolygon.map(polygon => transformPolygon$1(polygon, bbox));
|
|
374
449
|
}
|
|
375
|
-
function
|
|
376
|
-
return
|
|
450
|
+
function projectFlat(xyz) {
|
|
451
|
+
return lngLatToWorld(xyz);
|
|
377
452
|
}
|
|
378
|
-
function
|
|
379
|
-
return
|
|
453
|
+
function inverseLerp(a, b, x) {
|
|
454
|
+
return (x - a) / (b - a);
|
|
380
455
|
}
|
|
381
456
|
|
|
457
|
+
const TRANSFORM_FN = {
|
|
458
|
+
Point: transformPoint,
|
|
459
|
+
MultiPoint: transformMultiPoint,
|
|
460
|
+
LineString: transformLineString,
|
|
461
|
+
MultiLineString: transformMultiLineString,
|
|
462
|
+
Polygon: transformPolygon,
|
|
463
|
+
MultiPolygon: transformMultiPolygon
|
|
464
|
+
};
|
|
382
465
|
/**
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const V3_MINOR_VERSION = '3.4';
|
|
388
|
-
/** @internalRemarks Source: @carto/constants, @deck.gl/carto */
|
|
389
|
-
const DEFAULT_GEO_COLUMN = 'geom';
|
|
390
|
-
/**
|
|
391
|
-
* Fastly default limit is 8192; leave some padding.
|
|
392
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
393
|
-
*/
|
|
394
|
-
const DEFAULT_MAX_LENGTH_URL = 7000;
|
|
395
|
-
/** @internalRemarks Source: @deck.gl/carto */
|
|
396
|
-
const DEFAULT_TILE_RESOLUTION = 0.5;
|
|
397
|
-
/**
|
|
398
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
399
|
-
* @internal
|
|
400
|
-
*/
|
|
401
|
-
const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
402
|
-
/**
|
|
403
|
-
* @internalRemarks Source: @deck.gl/carto
|
|
404
|
-
* @internal
|
|
466
|
+
* Transform tile coords to WGS84 coordinates.
|
|
467
|
+
*
|
|
468
|
+
* @param geometry - any valid geojson geometry
|
|
469
|
+
* @param bbox - geojson bbox
|
|
405
470
|
*/
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
471
|
+
function transformTileCoordsToWGS84(geometry, bbox) {
|
|
472
|
+
const [west, south, east, north] = bbox;
|
|
473
|
+
const nw = lngLatToWorld([west, north]);
|
|
474
|
+
const se = lngLatToWorld([east, south]);
|
|
475
|
+
const projectedBbox = [nw, se];
|
|
476
|
+
if (geometry.type === 'GeometryCollection') {
|
|
477
|
+
throw new Error('Unsupported geometry type GeometryCollection');
|
|
478
|
+
}
|
|
479
|
+
const transformFn = TRANSFORM_FN[geometry.type];
|
|
480
|
+
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
481
|
+
return {
|
|
482
|
+
...geometry,
|
|
483
|
+
coordinates
|
|
484
|
+
};
|
|
413
485
|
}
|
|
414
|
-
function
|
|
415
|
-
|
|
486
|
+
function transformPoint(_ref, _ref2) {
|
|
487
|
+
let [pointX, pointY] = _ref;
|
|
488
|
+
let [nw, se] = _ref2;
|
|
489
|
+
const x = lerp(nw[0], se[0], pointX);
|
|
490
|
+
const y = lerp(nw[1], se[1], pointY);
|
|
491
|
+
return worldToLngLat([x, y]);
|
|
416
492
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
let {
|
|
420
|
-
apiBaseUrl,
|
|
421
|
-
cartoMapId
|
|
422
|
-
} = _ref;
|
|
423
|
-
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
|
|
493
|
+
function getPoints(geometry, bbox) {
|
|
494
|
+
return geometry.map(g => transformPoint(g, bbox));
|
|
424
495
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
let {
|
|
428
|
-
attribute,
|
|
429
|
-
apiBaseUrl,
|
|
430
|
-
connectionName,
|
|
431
|
-
source,
|
|
432
|
-
type
|
|
433
|
-
} = _ref2;
|
|
434
|
-
if (type === 'query') {
|
|
435
|
-
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, attribute);
|
|
436
|
-
}
|
|
437
|
-
// type === 'table'
|
|
438
|
-
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, source, attribute);
|
|
496
|
+
function transformMultiPoint(multiPoint, bbox) {
|
|
497
|
+
return getPoints(multiPoint, bbox);
|
|
439
498
|
}
|
|
440
|
-
function
|
|
441
|
-
|
|
442
|
-
apiBaseUrl,
|
|
443
|
-
connectionName,
|
|
444
|
-
endpoint
|
|
445
|
-
} = _ref3;
|
|
446
|
-
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
499
|
+
function transformLineString(line, bbox) {
|
|
500
|
+
return getPoints(line, bbox);
|
|
447
501
|
}
|
|
448
|
-
function
|
|
449
|
-
|
|
450
|
-
apiBaseUrl,
|
|
451
|
-
connectionName
|
|
452
|
-
} = _ref4;
|
|
453
|
-
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
|
|
502
|
+
function transformMultiLineString(multiLineString, bbox) {
|
|
503
|
+
return multiLineString.map(lineString => transformLineString(lineString, bbox));
|
|
454
504
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
// SPDX-License-Identifier: MIT
|
|
458
|
-
// Copyright (c) vis.gl contributors
|
|
459
|
-
/**
|
|
460
|
-
*
|
|
461
|
-
* Custom error for reported errors in CARTO Maps API.
|
|
462
|
-
* Provides useful debugging information in console and context for applications.
|
|
463
|
-
*
|
|
464
|
-
*/
|
|
465
|
-
class CartoAPIError extends Error {
|
|
466
|
-
constructor(error, errorContext, response, responseJson) {
|
|
467
|
-
let responseString = 'Failed to connect';
|
|
468
|
-
if (response) {
|
|
469
|
-
responseString = 'Server returned: ';
|
|
470
|
-
if (response.status === 400) {
|
|
471
|
-
responseString += 'Bad request';
|
|
472
|
-
} else if (response.status === 401 || response.status === 403) {
|
|
473
|
-
responseString += 'Unauthorized access';
|
|
474
|
-
} else if (response.status === 404) {
|
|
475
|
-
responseString += 'Not found';
|
|
476
|
-
} else {
|
|
477
|
-
responseString += 'Error';
|
|
478
|
-
}
|
|
479
|
-
responseString += ` (${response.status}):`;
|
|
480
|
-
}
|
|
481
|
-
responseString += ` ${error.message || error}`;
|
|
482
|
-
let message = `${errorContext.requestType} API request failed`;
|
|
483
|
-
message += `\n${responseString}`;
|
|
484
|
-
for (const key of Object.keys(errorContext)) {
|
|
485
|
-
if (key === 'requestType') continue;
|
|
486
|
-
message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
|
|
487
|
-
}
|
|
488
|
-
message += '\n';
|
|
489
|
-
super(message);
|
|
490
|
-
/** Source error from server */
|
|
491
|
-
this.error = void 0;
|
|
492
|
-
/** Context (API call & parameters) in which error occured */
|
|
493
|
-
this.errorContext = void 0;
|
|
494
|
-
/** Response from server */
|
|
495
|
-
this.response = void 0;
|
|
496
|
-
/** JSON Response from server */
|
|
497
|
-
this.responseJson = void 0;
|
|
498
|
-
this.name = 'CartoAPIError';
|
|
499
|
-
this.response = response;
|
|
500
|
-
this.responseJson = responseJson;
|
|
501
|
-
this.error = error;
|
|
502
|
-
this.errorContext = errorContext;
|
|
503
|
-
}
|
|
505
|
+
function transformPolygon(polygon, bbox) {
|
|
506
|
+
return polygon.map(polygonRing => getPoints(polygonRing, bbox));
|
|
504
507
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
*/
|
|
508
|
-
function formatErrorKey(key) {
|
|
509
|
-
return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
|
|
508
|
+
function transformMultiPolygon(multiPolygon, bbox) {
|
|
509
|
+
return multiPolygon.map(polygon => transformPolygon(polygon, bbox));
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
// Copyright (c) vis.gl contributors
|
|
515
|
-
const requestWithParameters = function (_ref) {
|
|
512
|
+
const FEATURE_GEOM_PROPERTY = '__geomValue';
|
|
513
|
+
function tileFeaturesGeometries(_ref) {
|
|
516
514
|
let {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
localCache
|
|
515
|
+
tiles,
|
|
516
|
+
tileFormat,
|
|
517
|
+
spatialFilter,
|
|
518
|
+
uniqueIdProperty,
|
|
519
|
+
options
|
|
523
520
|
} = _ref;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
//
|
|
527
|
-
//
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
client: getClient(),
|
|
531
|
-
...(typeof deck !== 'undefined' && deck.VERSION && {
|
|
532
|
-
deckglVersion: deck.VERSION
|
|
533
|
-
}),
|
|
534
|
-
...parameters
|
|
535
|
-
};
|
|
536
|
-
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
537
|
-
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
538
|
-
const {
|
|
539
|
-
cache: REQUEST_CACHE,
|
|
540
|
-
canReadCache,
|
|
541
|
-
canStoreInCache
|
|
542
|
-
} = getCacheSettings(localCache);
|
|
543
|
-
if (canReadCache && REQUEST_CACHE.has(key)) {
|
|
544
|
-
return Promise.resolve(REQUEST_CACHE.get(key));
|
|
521
|
+
const map = new Map();
|
|
522
|
+
for (const tile of tiles) {
|
|
523
|
+
// Discard if it's not a visible tile (only check false value, not undefined)
|
|
524
|
+
// or tile has not data
|
|
525
|
+
if (tile.isVisible === false || !tile.data) {
|
|
526
|
+
continue;
|
|
545
527
|
}
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
528
|
+
const bbox = [tile.bbox.west, tile.bbox.south, tile.bbox.east, tile.bbox.north];
|
|
529
|
+
const bboxToGeom = bboxPolygon(bbox);
|
|
530
|
+
const tileIsFullyVisible = booleanWithin(bboxToGeom, spatialFilter);
|
|
531
|
+
// Clip the geometry to intersect with the tile
|
|
532
|
+
const spatialFilterFeature = {
|
|
533
|
+
type: 'Feature',
|
|
534
|
+
geometry: spatialFilter,
|
|
535
|
+
properties: {}
|
|
550
536
|
};
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
537
|
+
const clippedGeometryToIntersect = intersect(helpers.featureCollection([bboxToGeom, spatialFilterFeature]));
|
|
538
|
+
if (!clippedGeometryToIntersect) {
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
// We assume that MVT tileFormat uses local coordinates so we transform the geometry to intersect to tile coordinates [0..1],
|
|
542
|
+
// while in the case of 'geojson' or binary, the geometries are already in WGS84
|
|
543
|
+
const transformedGeometryToIntersect = tileFormat === exports.TileFormat.MVT ? transformToTileCoords(clippedGeometryToIntersect.geometry, bbox) : clippedGeometryToIntersect.geometry;
|
|
544
|
+
createIndicesForPoints(tile.data.points);
|
|
545
|
+
calculateFeatures({
|
|
546
|
+
map,
|
|
547
|
+
tileIsFullyVisible,
|
|
548
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
549
|
+
data: tile.data.points,
|
|
550
|
+
type: 'Point',
|
|
551
|
+
bbox,
|
|
552
|
+
tileFormat,
|
|
553
|
+
uniqueIdProperty,
|
|
554
|
+
options
|
|
558
555
|
});
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
556
|
+
calculateFeatures({
|
|
557
|
+
map,
|
|
558
|
+
tileIsFullyVisible,
|
|
559
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
560
|
+
data: tile.data.lines,
|
|
561
|
+
type: 'LineString',
|
|
562
|
+
bbox,
|
|
563
|
+
tileFormat,
|
|
564
|
+
uniqueIdProperty,
|
|
565
|
+
options
|
|
566
|
+
});
|
|
567
|
+
calculateFeatures({
|
|
568
|
+
map,
|
|
569
|
+
tileIsFullyVisible,
|
|
570
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
571
|
+
data: tile.data.polygons,
|
|
572
|
+
type: 'Polygon',
|
|
573
|
+
bbox,
|
|
574
|
+
tileFormat,
|
|
575
|
+
uniqueIdProperty,
|
|
576
|
+
options
|
|
575
577
|
});
|
|
576
|
-
if (canStoreInCache) {
|
|
577
|
-
REQUEST_CACHE.set(key, jsonPromise);
|
|
578
|
-
}
|
|
579
|
-
return Promise.resolve(jsonPromise);
|
|
580
|
-
} catch (e) {
|
|
581
|
-
return Promise.reject(e);
|
|
582
578
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
579
|
+
return Array.from(map.values());
|
|
580
|
+
}
|
|
581
|
+
function processTileFeatureProperties(_ref2) {
|
|
582
|
+
let {
|
|
583
|
+
map,
|
|
584
|
+
data,
|
|
585
|
+
startIndex,
|
|
586
|
+
endIndex,
|
|
587
|
+
type,
|
|
588
|
+
bbox,
|
|
589
|
+
tileFormat,
|
|
590
|
+
uniqueIdProperty,
|
|
591
|
+
storeGeometry,
|
|
592
|
+
geometryIntersection
|
|
593
|
+
} = _ref2;
|
|
594
|
+
const tileProps = getPropertiesFromTile(data, startIndex);
|
|
595
|
+
const uniquePropertyValue = getUniquePropertyValue(tileProps, uniqueIdProperty, map);
|
|
596
|
+
if (!uniquePropertyValue || map.has(uniquePropertyValue)) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
let geometry = null;
|
|
600
|
+
// Only calculate geometry if necessary
|
|
601
|
+
if (storeGeometry || geometryIntersection) {
|
|
602
|
+
const {
|
|
603
|
+
positions
|
|
604
|
+
} = data;
|
|
605
|
+
const ringCoordinates = getRingCoordinatesFor(startIndex, endIndex, positions);
|
|
606
|
+
geometry = getFeatureByType(ringCoordinates, type);
|
|
607
|
+
}
|
|
608
|
+
// If intersection is required, check before proceeding
|
|
609
|
+
if (geometry && geometryIntersection && !intersects(geometry, geometryIntersection)) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
const properties = parseProperties(tileProps);
|
|
613
|
+
// Only save geometry if necessary
|
|
614
|
+
if (storeGeometry && geometry) {
|
|
615
|
+
properties[FEATURE_GEOM_PROPERTY] = tileFormat === exports.TileFormat.MVT ? transformTileCoordsToWGS84(geometry, bbox) : geometry;
|
|
616
|
+
}
|
|
617
|
+
map.set(uniquePropertyValue, properties);
|
|
618
|
+
}
|
|
619
|
+
function addIntersectedFeaturesInTile(_ref3) {
|
|
620
|
+
let {
|
|
621
|
+
map,
|
|
622
|
+
data,
|
|
623
|
+
geometryIntersection,
|
|
624
|
+
type,
|
|
625
|
+
bbox,
|
|
626
|
+
tileFormat,
|
|
627
|
+
uniqueIdProperty,
|
|
628
|
+
options
|
|
629
|
+
} = _ref3;
|
|
630
|
+
const indices = getIndices(data);
|
|
631
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
632
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
633
|
+
const startIndex = indices[i];
|
|
634
|
+
const endIndex = indices[i + 1];
|
|
635
|
+
processTileFeatureProperties({
|
|
636
|
+
map,
|
|
637
|
+
data,
|
|
638
|
+
startIndex,
|
|
639
|
+
endIndex,
|
|
640
|
+
type,
|
|
641
|
+
bbox,
|
|
642
|
+
tileFormat,
|
|
643
|
+
uniqueIdProperty,
|
|
644
|
+
storeGeometry,
|
|
645
|
+
geometryIntersection
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function getIndices(data) {
|
|
650
|
+
let indices;
|
|
651
|
+
switch (data.type) {
|
|
652
|
+
case 'Point':
|
|
653
|
+
// @ts-expect-error Missing or changed types?
|
|
654
|
+
indices = data.pointIndices;
|
|
655
|
+
break;
|
|
656
|
+
case 'LineString':
|
|
657
|
+
indices = data.pathIndices;
|
|
658
|
+
break;
|
|
659
|
+
case 'Polygon':
|
|
660
|
+
indices = data.primitivePolygonIndices;
|
|
661
|
+
break;
|
|
662
|
+
default:
|
|
663
|
+
throw new Error(`Unexpected type, "${data.type}"`);
|
|
664
|
+
}
|
|
665
|
+
return indices.value;
|
|
666
|
+
}
|
|
667
|
+
function getFeatureId(data, startIndex) {
|
|
668
|
+
return data.featureIds.value[startIndex];
|
|
669
|
+
}
|
|
670
|
+
function getPropertiesFromTile(data, startIndex) {
|
|
671
|
+
const featureId = getFeatureId(data, startIndex);
|
|
672
|
+
const {
|
|
673
|
+
properties,
|
|
674
|
+
numericProps,
|
|
675
|
+
fields
|
|
676
|
+
} = data;
|
|
677
|
+
const result = {
|
|
678
|
+
uniqueId: fields?.[featureId]?.id,
|
|
679
|
+
properties: properties[featureId],
|
|
680
|
+
numericProps: {}
|
|
597
681
|
};
|
|
682
|
+
for (const key in numericProps) {
|
|
683
|
+
result.numericProps[key] = numericProps[key].value[startIndex];
|
|
684
|
+
}
|
|
685
|
+
return result;
|
|
598
686
|
}
|
|
599
|
-
function
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
});
|
|
605
|
-
const headerEntries = Object.entries(headers).sort((_ref4, _ref5) => {
|
|
606
|
-
let [a] = _ref4;
|
|
607
|
-
let [b] = _ref5;
|
|
608
|
-
return a > b ? 1 : -1;
|
|
609
|
-
});
|
|
610
|
-
return JSON.stringify({
|
|
611
|
-
baseUrl,
|
|
612
|
-
parameters: parameterEntries,
|
|
613
|
-
headers: headerEntries
|
|
614
|
-
});
|
|
687
|
+
function parseProperties(tileProps) {
|
|
688
|
+
const {
|
|
689
|
+
properties,
|
|
690
|
+
numericProps
|
|
691
|
+
} = tileProps;
|
|
692
|
+
return Object.assign({}, properties, numericProps);
|
|
615
693
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
* those already in the URL.
|
|
620
|
-
*/
|
|
621
|
-
function createURLWithParameters(baseUrlString, parameters) {
|
|
622
|
-
const baseUrl = new URL(baseUrlString);
|
|
623
|
-
for (const [key, value] of Object.entries(parameters)) {
|
|
624
|
-
if (isPureObject(value) || Array.isArray(value)) {
|
|
625
|
-
baseUrl.searchParams.set(key, JSON.stringify(value));
|
|
626
|
-
} else {
|
|
627
|
-
baseUrl.searchParams.set(key, value.toString());
|
|
628
|
-
}
|
|
694
|
+
function getUniquePropertyValue(tileProps, uniqueIdProperty, map) {
|
|
695
|
+
if (uniqueIdProperty) {
|
|
696
|
+
return getValueFromTileProps(tileProps, uniqueIdProperty);
|
|
629
697
|
}
|
|
630
|
-
|
|
698
|
+
if (tileProps.uniqueId) {
|
|
699
|
+
return tileProps.uniqueId;
|
|
700
|
+
}
|
|
701
|
+
const artificialId = map.size + 1; // a counter, assumed as a valid new id
|
|
702
|
+
return getValueFromTileProps(tileProps, 'cartodb_id') || getValueFromTileProps(tileProps, 'geoid') || artificialId;
|
|
631
703
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
704
|
+
function getValueFromTileProps(tileProps, propertyName) {
|
|
705
|
+
const {
|
|
706
|
+
properties,
|
|
707
|
+
numericProps
|
|
708
|
+
} = tileProps;
|
|
709
|
+
return numericProps[propertyName] || properties[propertyName];
|
|
710
|
+
}
|
|
711
|
+
function getFeatureByType(coordinates, type) {
|
|
712
|
+
switch (type) {
|
|
713
|
+
case 'Polygon':
|
|
714
|
+
return {
|
|
715
|
+
type: 'Polygon',
|
|
716
|
+
coordinates: [coordinates]
|
|
717
|
+
};
|
|
718
|
+
case 'LineString':
|
|
719
|
+
return {
|
|
720
|
+
type: 'LineString',
|
|
721
|
+
coordinates
|
|
722
|
+
};
|
|
723
|
+
case 'Point':
|
|
724
|
+
return {
|
|
725
|
+
type: 'Point',
|
|
726
|
+
coordinates: coordinates[0]
|
|
727
|
+
};
|
|
728
|
+
default:
|
|
729
|
+
throw new Error('Invalid geometry type');
|
|
641
730
|
}
|
|
642
|
-
return baseUrl.toString();
|
|
643
731
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
source: JSON.stringify(parameters, undefined, 2)
|
|
687
|
-
};
|
|
688
|
-
return Promise.resolve(requestWithParameters({
|
|
689
|
-
baseUrl,
|
|
690
|
-
parameters,
|
|
691
|
-
headers,
|
|
692
|
-
errorContext,
|
|
693
|
-
maxLengthURL,
|
|
694
|
-
localCache
|
|
695
|
-
})).then(function (mapInstantiation) {
|
|
696
|
-
let _exit;
|
|
697
|
-
function _temp2(_result) {
|
|
698
|
-
return _exit ? _result : Promise.resolve(requestWithParameters({
|
|
699
|
-
baseUrl: dataUrl,
|
|
700
|
-
headers,
|
|
701
|
-
errorContext,
|
|
702
|
-
maxLengthURL,
|
|
703
|
-
localCache
|
|
704
|
-
}));
|
|
705
|
-
}
|
|
706
|
-
const dataUrl = mapInstantiation[format].url[0];
|
|
707
|
-
if (cache) {
|
|
708
|
-
cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
|
|
709
|
-
}
|
|
710
|
-
errorContext.requestType = 'Map data';
|
|
711
|
-
const _temp = function () {
|
|
712
|
-
if (format === 'tilejson') {
|
|
713
|
-
return Promise.resolve(requestWithParameters({
|
|
714
|
-
baseUrl: dataUrl,
|
|
715
|
-
headers,
|
|
716
|
-
errorContext,
|
|
717
|
-
maxLengthURL,
|
|
718
|
-
localCache
|
|
719
|
-
})).then(function (json) {
|
|
720
|
-
if (accessToken) {
|
|
721
|
-
json.accessToken = accessToken;
|
|
722
|
-
}
|
|
723
|
-
_exit = 1;
|
|
724
|
-
return json;
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
}();
|
|
728
|
-
return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
|
|
732
|
+
function getRingCoordinatesFor(startIndex, endIndex, positions) {
|
|
733
|
+
const ringCoordinates = [];
|
|
734
|
+
for (let j = startIndex; j < endIndex; j++) {
|
|
735
|
+
ringCoordinates.push(Array.from(positions.value.subarray(j * positions.size, (j + 1) * positions.size)));
|
|
736
|
+
}
|
|
737
|
+
return ringCoordinates;
|
|
738
|
+
}
|
|
739
|
+
function calculateFeatures(_ref4) {
|
|
740
|
+
let {
|
|
741
|
+
map,
|
|
742
|
+
tileIsFullyVisible,
|
|
743
|
+
geometryIntersection,
|
|
744
|
+
data,
|
|
745
|
+
type,
|
|
746
|
+
bbox,
|
|
747
|
+
tileFormat,
|
|
748
|
+
uniqueIdProperty,
|
|
749
|
+
options
|
|
750
|
+
} = _ref4;
|
|
751
|
+
if (!data?.properties.length) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
if (tileIsFullyVisible) {
|
|
755
|
+
addAllFeaturesInTile({
|
|
756
|
+
map,
|
|
757
|
+
data,
|
|
758
|
+
type,
|
|
759
|
+
bbox,
|
|
760
|
+
tileFormat,
|
|
761
|
+
uniqueIdProperty,
|
|
762
|
+
options
|
|
763
|
+
});
|
|
764
|
+
} else {
|
|
765
|
+
addIntersectedFeaturesInTile({
|
|
766
|
+
map,
|
|
767
|
+
data,
|
|
768
|
+
geometryIntersection,
|
|
769
|
+
type,
|
|
770
|
+
bbox,
|
|
771
|
+
tileFormat,
|
|
772
|
+
uniqueIdProperty,
|
|
773
|
+
options
|
|
729
774
|
});
|
|
730
|
-
} catch (e) {
|
|
731
|
-
return Promise.reject(e);
|
|
732
775
|
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}
|
|
761
|
-
if (filters) {
|
|
762
|
-
urlParameters.filters = filters;
|
|
763
|
-
}
|
|
764
|
-
if (queryParameters) {
|
|
765
|
-
urlParameters.queryParameters = queryParameters;
|
|
766
|
-
}
|
|
767
|
-
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
768
|
-
} catch (e) {
|
|
769
|
-
return Promise.reject(e);
|
|
776
|
+
}
|
|
777
|
+
function addAllFeaturesInTile(_ref5) {
|
|
778
|
+
let {
|
|
779
|
+
map,
|
|
780
|
+
data,
|
|
781
|
+
type,
|
|
782
|
+
bbox,
|
|
783
|
+
tileFormat,
|
|
784
|
+
uniqueIdProperty,
|
|
785
|
+
options
|
|
786
|
+
} = _ref5;
|
|
787
|
+
const indices = getIndices(data);
|
|
788
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
789
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
790
|
+
const startIndex = indices[i];
|
|
791
|
+
const endIndex = indices[i + 1];
|
|
792
|
+
processTileFeatureProperties({
|
|
793
|
+
map,
|
|
794
|
+
data,
|
|
795
|
+
startIndex,
|
|
796
|
+
endIndex,
|
|
797
|
+
type,
|
|
798
|
+
bbox,
|
|
799
|
+
tileFormat,
|
|
800
|
+
uniqueIdProperty,
|
|
801
|
+
storeGeometry
|
|
802
|
+
});
|
|
770
803
|
}
|
|
771
|
-
}
|
|
804
|
+
}
|
|
805
|
+
function createIndicesForPoints(data) {
|
|
806
|
+
const featureIds = data.featureIds.value;
|
|
807
|
+
const lastFeatureId = featureIds[featureIds.length - 1];
|
|
808
|
+
const PointIndicesArray = featureIds.constructor;
|
|
809
|
+
const pointIndices = {
|
|
810
|
+
value: new PointIndicesArray(featureIds.length + 1),
|
|
811
|
+
size: 1
|
|
812
|
+
};
|
|
813
|
+
pointIndices.value.set(featureIds);
|
|
814
|
+
pointIndices.value.set([lastFeatureId + 1], featureIds.length);
|
|
815
|
+
// @ts-expect-error Missing or changed types?
|
|
816
|
+
data.pointIndices = pointIndices;
|
|
817
|
+
}
|
|
772
818
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
propertiesTableName
|
|
787
|
-
};
|
|
788
|
-
if (columns) {
|
|
789
|
-
urlParameters.columns = columns.join(',');
|
|
790
|
-
}
|
|
791
|
-
if (filters) {
|
|
792
|
-
urlParameters.filters = filters;
|
|
793
|
-
}
|
|
794
|
-
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
795
|
-
} catch (e) {
|
|
796
|
-
return Promise.reject(e);
|
|
819
|
+
function tileFeaturesSpatialIndex(_ref) {
|
|
820
|
+
let {
|
|
821
|
+
tiles,
|
|
822
|
+
spatialFilter,
|
|
823
|
+
spatialDataColumn,
|
|
824
|
+
spatialDataType
|
|
825
|
+
} = _ref;
|
|
826
|
+
const map = new Map();
|
|
827
|
+
const spatialIndex = getSpatialIndex(spatialDataType);
|
|
828
|
+
const resolution = getResolution(tiles, spatialIndex);
|
|
829
|
+
const spatialIndexIDName = spatialDataColumn ? spatialDataColumn : spatialIndex;
|
|
830
|
+
if (!resolution) {
|
|
831
|
+
return [];
|
|
797
832
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
802
|
-
function getSpatialFiltersResolution(source, viewState) {
|
|
803
|
-
const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
|
|
804
|
-
const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
|
|
805
|
-
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
806
|
-
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
807
|
-
if (source.spatialDataType === 'h3') {
|
|
808
|
-
const tileSize = DEFAULT_TILE_SIZE;
|
|
809
|
-
const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref => {
|
|
810
|
-
let [zoom] = _ref;
|
|
811
|
-
return zoom === currentZoomInt;
|
|
812
|
-
})?.[1] ?? Math.max(0, currentZoomInt - 3);
|
|
813
|
-
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
814
|
-
const hexagonResolution = _getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
815
|
-
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
833
|
+
const cells = getCellsCoverGeometry(spatialFilter, spatialIndex, resolution);
|
|
834
|
+
if (!cells?.length) {
|
|
835
|
+
return [];
|
|
816
836
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
837
|
+
// We transform cells to Set to improve the performace
|
|
838
|
+
const cellsSet = new Set(cells);
|
|
839
|
+
for (const tile of tiles) {
|
|
840
|
+
if (tile.isVisible === false || !tile.data) {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
tile.data.forEach(d => {
|
|
844
|
+
if (cellsSet.has(d.id)) {
|
|
845
|
+
map.set(d.id, {
|
|
846
|
+
...d.properties,
|
|
847
|
+
[spatialIndexIDName]: d.id
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
});
|
|
822
851
|
}
|
|
823
|
-
return
|
|
824
|
-
}
|
|
825
|
-
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]];
|
|
826
|
-
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
827
|
-
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
828
|
-
const BIAS = 2;
|
|
829
|
-
/**
|
|
830
|
-
* Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
831
|
-
* a H3 resolution such that the screen space size of the hexagons is
|
|
832
|
-
* "similar" to the given tileSize on screen. Intended for use with deck.gl.
|
|
833
|
-
* @internal
|
|
834
|
-
*/
|
|
835
|
-
function _getHexagonResolution(viewport, tileSize) {
|
|
836
|
-
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
837
|
-
// expressed as an offset to the viewport zoom.
|
|
838
|
-
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
839
|
-
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
840
|
-
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
841
|
-
// Clip and bias
|
|
842
|
-
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
852
|
+
return Array.from(map.values());
|
|
843
853
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
849
|
-
*/
|
|
850
|
-
class WidgetSource {
|
|
851
|
-
constructor(props) {
|
|
852
|
-
this.props = void 0;
|
|
853
|
-
this.props = {
|
|
854
|
-
...WidgetSource.defaultProps,
|
|
855
|
-
...props
|
|
856
|
-
};
|
|
854
|
+
function getResolution(tiles, spatialIndex) {
|
|
855
|
+
const data = tiles.find(tile => tile.data?.length)?.data;
|
|
856
|
+
if (!data) {
|
|
857
|
+
return;
|
|
857
858
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
return {
|
|
861
|
-
apiVersion: props.apiVersion,
|
|
862
|
-
apiBaseUrl: props.apiBaseUrl,
|
|
863
|
-
clientId: props.clientId,
|
|
864
|
-
accessToken: props.accessToken,
|
|
865
|
-
connectionName: props.connectionName,
|
|
866
|
-
filters: getApplicableFilters(owner, props.filters),
|
|
867
|
-
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
868
|
-
spatialDataType: props.spatialDataType,
|
|
869
|
-
spatialDataColumn: props.spatialDataColumn,
|
|
870
|
-
dataResolution: props.dataResolution
|
|
871
|
-
};
|
|
859
|
+
if (spatialIndex === exports.SpatialIndex.QUADBIN) {
|
|
860
|
+
return Number(quadbin.getResolution(data[0].id));
|
|
872
861
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
if (!referenceViewState) {
|
|
879
|
-
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
880
|
-
}
|
|
881
|
-
return getSpatialFiltersResolution(source, referenceViewState);
|
|
862
|
+
if (spatialIndex === exports.SpatialIndex.H3) {
|
|
863
|
+
return h3Js.getResolution(data[0].id);
|
|
882
864
|
}
|
|
883
865
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
};
|
|
891
|
-
|
|
892
|
-
/**
|
|
893
|
-
* Return more descriptive error from API
|
|
894
|
-
* @internalRemarks Source: @carto/react-api
|
|
895
|
-
*/
|
|
896
|
-
|
|
897
|
-
/** @internalRemarks Source: @carto/react-api */
|
|
898
|
-
|
|
899
|
-
function _catch(body, recover) {
|
|
900
|
-
try {
|
|
901
|
-
var result = body();
|
|
902
|
-
} catch (e) {
|
|
903
|
-
return recover(e);
|
|
866
|
+
const bboxWest = [-180, -90, 0, 90];
|
|
867
|
+
const bboxEast = [0, -90, 180, 90];
|
|
868
|
+
function getCellsCoverGeometry(geometry, spatialIndex, resolution) {
|
|
869
|
+
if (spatialIndex === exports.SpatialIndex.QUADBIN) {
|
|
870
|
+
// @ts-expect-error TODO: Probably ought to be stricter about number vs. bigint types in this file.
|
|
871
|
+
return quadbin.geometryToCells(geometry, resolution);
|
|
904
872
|
}
|
|
905
|
-
if (
|
|
906
|
-
|
|
873
|
+
if (spatialIndex === exports.SpatialIndex.H3) {
|
|
874
|
+
// The current H3 polyfill algorithm can't deal with polygon segments of greater than 180 degrees longitude
|
|
875
|
+
// so we clip the geometry to be sure that none of them is greater than 180 degrees
|
|
876
|
+
// https://github.com/uber/h3-js/issues/24#issuecomment-431893796
|
|
877
|
+
return h3Js.polygonToCells(bboxClip(geometry, bboxWest).geometry.coordinates, resolution, true).concat(h3Js.polygonToCells(bboxClip(geometry, bboxEast).geometry.coordinates, resolution, true));
|
|
907
878
|
}
|
|
908
|
-
return result;
|
|
909
879
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
try {
|
|
917
|
-
let _exit;
|
|
918
|
-
function _temp2(_result) {
|
|
919
|
-
if (_exit) ;
|
|
920
|
-
if (!response.ok) {
|
|
921
|
-
dealWithApiError({
|
|
922
|
-
response,
|
|
923
|
-
data
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
return data;
|
|
927
|
-
}
|
|
928
|
-
let response;
|
|
929
|
-
let data;
|
|
930
|
-
const isPost = opts?.method === 'POST';
|
|
931
|
-
const _temp = _catch(function () {
|
|
932
|
-
return Promise.resolve(fetch(url.toString(), {
|
|
933
|
-
headers: {
|
|
934
|
-
Authorization: `Bearer ${accessToken}`,
|
|
935
|
-
...(isPost && {
|
|
936
|
-
'Content-Type': 'application/json'
|
|
937
|
-
})
|
|
938
|
-
},
|
|
939
|
-
...(isPost && {
|
|
940
|
-
method: opts?.method,
|
|
941
|
-
body: opts?.body
|
|
942
|
-
}),
|
|
943
|
-
signal: opts?.abortController?.signal,
|
|
944
|
-
...opts?.otherOptions
|
|
945
|
-
})).then(function (_fetch) {
|
|
946
|
-
response = _fetch;
|
|
947
|
-
return Promise.resolve(response.json()).then(function (_response$json) {
|
|
948
|
-
data = _response$json;
|
|
949
|
-
});
|
|
950
|
-
});
|
|
951
|
-
}, function (error) {
|
|
952
|
-
if (error.name === 'AbortError') throw error;
|
|
953
|
-
throw new Error(`Failed request: ${error}`);
|
|
954
|
-
});
|
|
955
|
-
return Promise.resolve(_temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp));
|
|
956
|
-
} catch (e) {
|
|
957
|
-
return Promise.reject(e);
|
|
958
|
-
}
|
|
959
|
-
};
|
|
960
|
-
function dealWithApiError(_ref) {
|
|
961
|
-
let {
|
|
962
|
-
response,
|
|
963
|
-
data
|
|
964
|
-
} = _ref;
|
|
965
|
-
if (data.error === 'Column not found') {
|
|
966
|
-
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
967
|
-
}
|
|
968
|
-
if (typeof data.error === 'string' && data.error?.includes('Missing columns')) {
|
|
969
|
-
throw new InvalidColumnError(data.error);
|
|
970
|
-
}
|
|
971
|
-
switch (response.status) {
|
|
972
|
-
case 401:
|
|
973
|
-
throw new Error('Unauthorized access. Invalid credentials');
|
|
974
|
-
case 403:
|
|
975
|
-
throw new Error('Forbidden access to the requested data');
|
|
880
|
+
function getSpatialIndex(spatialDataType) {
|
|
881
|
+
switch (spatialDataType) {
|
|
882
|
+
case 'h3':
|
|
883
|
+
return exports.SpatialIndex.H3;
|
|
884
|
+
case 'quadbin':
|
|
885
|
+
return exports.SpatialIndex.QUADBIN;
|
|
976
886
|
default:
|
|
977
|
-
|
|
978
|
-
throw new Error(msg);
|
|
887
|
+
throw new Error('Unexpected spatial data type');
|
|
979
888
|
}
|
|
980
889
|
}
|
|
981
890
|
|
|
982
|
-
/** @internalRemarks Source: @carto/react-api */
|
|
983
|
-
const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'pick', 'timeseries', 'range', 'scatterplot', 'table'];
|
|
984
|
-
const {
|
|
985
|
-
V3
|
|
986
|
-
} = exports.ApiVersion;
|
|
987
|
-
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
988
891
|
/**
|
|
989
|
-
*
|
|
990
|
-
* @
|
|
892
|
+
* Current version of @carto/api-client.
|
|
893
|
+
* @internal
|
|
991
894
|
*/
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
let
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
} = source;
|
|
1024
|
-
const queryParams = {
|
|
1025
|
-
type,
|
|
1026
|
-
client: clientId,
|
|
1027
|
-
source: data,
|
|
1028
|
-
params,
|
|
1029
|
-
queryParameters: source.queryParameters || '',
|
|
1030
|
-
filters,
|
|
1031
|
-
filtersLogicalOperator
|
|
1032
|
-
};
|
|
1033
|
-
const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
|
|
1034
|
-
// Picking Model API requires 'spatialDataColumn'.
|
|
1035
|
-
if (model === 'pick') {
|
|
1036
|
-
queryParams.spatialDataColumn = spatialDataColumn;
|
|
895
|
+
/** @internal */
|
|
896
|
+
const V3_MINOR_VERSION = '3.4';
|
|
897
|
+
/** @privateRemarks Source: @carto/constants, @deck.gl/carto */
|
|
898
|
+
const DEFAULT_GEO_COLUMN = 'geom';
|
|
899
|
+
/**
|
|
900
|
+
* Fastly default limit is 8192; leave some padding.
|
|
901
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
902
|
+
*/
|
|
903
|
+
const DEFAULT_MAX_LENGTH_URL = 7000;
|
|
904
|
+
/** @privateRemarks Source: @deck.gl/carto */
|
|
905
|
+
const DEFAULT_TILE_RESOLUTION = 0.5;
|
|
906
|
+
/**
|
|
907
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
908
|
+
* @internal
|
|
909
|
+
*/
|
|
910
|
+
const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
|
|
911
|
+
/**
|
|
912
|
+
* @privateRemarks Source: @deck.gl/carto
|
|
913
|
+
* @internal
|
|
914
|
+
*/
|
|
915
|
+
const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
|
|
916
|
+
|
|
917
|
+
function tileFeaturesRaster(_ref) {
|
|
918
|
+
let {
|
|
919
|
+
tiles,
|
|
920
|
+
...options
|
|
921
|
+
} = _ref;
|
|
922
|
+
// Cache band metadata for faster lookup while iterating over pixels.
|
|
923
|
+
const bandMetadataByName = {};
|
|
924
|
+
for (const band of options.rasterMetadata.bands) {
|
|
925
|
+
bandMetadataByName[band.name] = band;
|
|
1037
926
|
}
|
|
1038
|
-
//
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
})
|
|
1067
|
-
}
|
|
1068
|
-
});
|
|
1069
|
-
}
|
|
1070
|
-
function objectToURLSearchParams(object) {
|
|
1071
|
-
const params = new URLSearchParams();
|
|
1072
|
-
for (const key in object) {
|
|
1073
|
-
if (isPureObject(object[key])) {
|
|
1074
|
-
params.append(key, JSON.stringify(object[key]));
|
|
1075
|
-
} else if (Array.isArray(object[key])) {
|
|
1076
|
-
params.append(key, JSON.stringify(object[key]));
|
|
1077
|
-
} else if (object[key] === null) {
|
|
1078
|
-
params.append(key, 'null');
|
|
1079
|
-
} else if (object[key] !== undefined) {
|
|
1080
|
-
params.append(key, String(object[key]));
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
return params;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
/**
|
|
1087
|
-
* Source for Widget API requests.
|
|
1088
|
-
*
|
|
1089
|
-
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
1090
|
-
*/
|
|
1091
|
-
class WidgetRemoteSource extends WidgetSource {
|
|
1092
|
-
getCategories(options) {
|
|
1093
|
-
try {
|
|
1094
|
-
const _this = this;
|
|
1095
|
-
const {
|
|
1096
|
-
filterOwner,
|
|
1097
|
-
spatialFilter,
|
|
1098
|
-
spatialFiltersMode,
|
|
1099
|
-
spatialIndexReferenceViewState,
|
|
1100
|
-
abortController,
|
|
1101
|
-
...params
|
|
1102
|
-
} = options;
|
|
1103
|
-
const {
|
|
1104
|
-
column,
|
|
1105
|
-
operation,
|
|
1106
|
-
operationColumn
|
|
1107
|
-
} = params;
|
|
1108
|
-
const source = _this.getModelSource(filterOwner);
|
|
1109
|
-
const spatialFiltersResolution = _this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1110
|
-
return Promise.resolve(executeModel({
|
|
1111
|
-
model: 'category',
|
|
1112
|
-
source: {
|
|
1113
|
-
...source,
|
|
1114
|
-
spatialFiltersResolution,
|
|
1115
|
-
spatialFiltersMode,
|
|
1116
|
-
spatialFilter
|
|
1117
|
-
},
|
|
1118
|
-
params: {
|
|
1119
|
-
column,
|
|
1120
|
-
operation,
|
|
1121
|
-
operationColumn: operationColumn || column
|
|
1122
|
-
},
|
|
1123
|
-
opts: {
|
|
1124
|
-
abortController
|
|
1125
|
-
}
|
|
1126
|
-
}).then(res => normalizeObjectKeys(res.rows)));
|
|
1127
|
-
} catch (e) {
|
|
1128
|
-
return Promise.reject(e);
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
getFeatures(options) {
|
|
1132
|
-
try {
|
|
1133
|
-
const _this2 = this;
|
|
1134
|
-
const {
|
|
1135
|
-
filterOwner,
|
|
1136
|
-
spatialFilter,
|
|
1137
|
-
spatialFiltersMode,
|
|
1138
|
-
spatialIndexReferenceViewState,
|
|
1139
|
-
abortController,
|
|
1140
|
-
...params
|
|
1141
|
-
} = options;
|
|
1142
|
-
const {
|
|
1143
|
-
columns,
|
|
1144
|
-
dataType,
|
|
1145
|
-
featureIds,
|
|
1146
|
-
z,
|
|
1147
|
-
limit,
|
|
1148
|
-
tileResolution
|
|
1149
|
-
} = params;
|
|
1150
|
-
const source = _this2.getModelSource(filterOwner);
|
|
1151
|
-
const spatialFiltersResolution = _this2._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1152
|
-
return Promise.resolve(executeModel({
|
|
1153
|
-
model: 'pick',
|
|
1154
|
-
source: {
|
|
1155
|
-
...source,
|
|
1156
|
-
spatialFiltersResolution,
|
|
1157
|
-
spatialFiltersMode,
|
|
1158
|
-
spatialFilter
|
|
1159
|
-
},
|
|
1160
|
-
params: {
|
|
1161
|
-
columns,
|
|
1162
|
-
dataType,
|
|
1163
|
-
featureIds,
|
|
1164
|
-
z,
|
|
1165
|
-
limit: limit || 1000,
|
|
1166
|
-
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
1167
|
-
},
|
|
1168
|
-
opts: {
|
|
1169
|
-
abortController
|
|
1170
|
-
}
|
|
1171
|
-
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1172
|
-
}).then(_ref => {
|
|
1173
|
-
let {
|
|
1174
|
-
rows
|
|
1175
|
-
} = _ref;
|
|
1176
|
-
return {
|
|
1177
|
-
rows
|
|
1178
|
-
};
|
|
1179
|
-
}));
|
|
1180
|
-
} catch (e) {
|
|
1181
|
-
return Promise.reject(e);
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
getFormula(options) {
|
|
1185
|
-
try {
|
|
1186
|
-
const _this3 = this;
|
|
1187
|
-
const {
|
|
1188
|
-
filterOwner,
|
|
1189
|
-
spatialFilter,
|
|
1190
|
-
spatialFiltersMode,
|
|
1191
|
-
spatialIndexReferenceViewState,
|
|
1192
|
-
abortController,
|
|
1193
|
-
operationExp,
|
|
1194
|
-
...params
|
|
1195
|
-
} = options;
|
|
1196
|
-
const {
|
|
1197
|
-
column,
|
|
1198
|
-
operation
|
|
1199
|
-
} = params;
|
|
1200
|
-
const source = _this3.getModelSource(filterOwner);
|
|
1201
|
-
const spatialFiltersResolution = _this3._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1202
|
-
return Promise.resolve(executeModel({
|
|
1203
|
-
model: 'formula',
|
|
1204
|
-
source: {
|
|
1205
|
-
...source,
|
|
1206
|
-
spatialFiltersResolution,
|
|
1207
|
-
spatialFiltersMode,
|
|
1208
|
-
spatialFilter
|
|
1209
|
-
},
|
|
1210
|
-
params: {
|
|
1211
|
-
column: column ?? '*',
|
|
1212
|
-
operation: operation ?? 'count',
|
|
1213
|
-
operationExp
|
|
1214
|
-
},
|
|
1215
|
-
opts: {
|
|
1216
|
-
abortController
|
|
1217
|
-
}
|
|
1218
|
-
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
1219
|
-
} catch (e) {
|
|
1220
|
-
return Promise.reject(e);
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
getHistogram(options) {
|
|
1224
|
-
try {
|
|
1225
|
-
const _this4 = this;
|
|
1226
|
-
const {
|
|
1227
|
-
filterOwner,
|
|
1228
|
-
spatialFilter,
|
|
1229
|
-
spatialFiltersMode,
|
|
1230
|
-
spatialIndexReferenceViewState,
|
|
1231
|
-
abortController,
|
|
1232
|
-
...params
|
|
1233
|
-
} = options;
|
|
1234
|
-
const {
|
|
1235
|
-
column,
|
|
1236
|
-
operation,
|
|
1237
|
-
ticks
|
|
1238
|
-
} = params;
|
|
1239
|
-
const source = _this4.getModelSource(filterOwner);
|
|
1240
|
-
const spatialFiltersResolution = _this4._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1241
|
-
return Promise.resolve(executeModel({
|
|
1242
|
-
model: 'histogram',
|
|
1243
|
-
source: {
|
|
1244
|
-
...source,
|
|
1245
|
-
spatialFiltersResolution,
|
|
1246
|
-
spatialFiltersMode,
|
|
1247
|
-
spatialFilter
|
|
1248
|
-
},
|
|
1249
|
-
params: {
|
|
1250
|
-
column,
|
|
1251
|
-
operation,
|
|
1252
|
-
ticks
|
|
1253
|
-
},
|
|
1254
|
-
opts: {
|
|
1255
|
-
abortController
|
|
1256
|
-
}
|
|
1257
|
-
}).then(res => normalizeObjectKeys(res.rows))).then(function (data) {
|
|
1258
|
-
if (data.length) {
|
|
1259
|
-
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
1260
|
-
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
1261
|
-
const result = Array(ticks.length + 1).fill(0);
|
|
1262
|
-
data.forEach(_ref2 => {
|
|
1263
|
-
let {
|
|
1264
|
-
tick,
|
|
1265
|
-
value
|
|
1266
|
-
} = _ref2;
|
|
1267
|
-
return result[tick] = value;
|
|
1268
|
-
});
|
|
1269
|
-
return result;
|
|
1270
|
-
}
|
|
1271
|
-
return [];
|
|
1272
|
-
});
|
|
1273
|
-
} catch (e) {
|
|
1274
|
-
return Promise.reject(e);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
getRange(options) {
|
|
1278
|
-
try {
|
|
1279
|
-
const _this5 = this;
|
|
1280
|
-
const {
|
|
1281
|
-
filterOwner,
|
|
1282
|
-
spatialFilter,
|
|
1283
|
-
spatialFiltersMode,
|
|
1284
|
-
spatialIndexReferenceViewState,
|
|
1285
|
-
abortController,
|
|
1286
|
-
...params
|
|
1287
|
-
} = options;
|
|
1288
|
-
const {
|
|
1289
|
-
column
|
|
1290
|
-
} = params;
|
|
1291
|
-
const source = _this5.getModelSource(filterOwner);
|
|
1292
|
-
const spatialFiltersResolution = _this5._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1293
|
-
return Promise.resolve(executeModel({
|
|
1294
|
-
model: 'range',
|
|
1295
|
-
source: {
|
|
1296
|
-
...source,
|
|
1297
|
-
spatialFiltersResolution,
|
|
1298
|
-
spatialFiltersMode,
|
|
1299
|
-
spatialFilter
|
|
1300
|
-
},
|
|
1301
|
-
params: {
|
|
1302
|
-
column
|
|
1303
|
-
},
|
|
1304
|
-
opts: {
|
|
1305
|
-
abortController
|
|
1306
|
-
}
|
|
1307
|
-
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
1308
|
-
} catch (e) {
|
|
1309
|
-
return Promise.reject(e);
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
getScatter(options) {
|
|
1313
|
-
try {
|
|
1314
|
-
const _this6 = this;
|
|
1315
|
-
const {
|
|
1316
|
-
filterOwner,
|
|
1317
|
-
spatialFilter,
|
|
1318
|
-
spatialFiltersMode,
|
|
1319
|
-
spatialIndexReferenceViewState,
|
|
1320
|
-
abortController,
|
|
1321
|
-
...params
|
|
1322
|
-
} = options;
|
|
1323
|
-
const {
|
|
1324
|
-
xAxisColumn,
|
|
1325
|
-
xAxisJoinOperation,
|
|
1326
|
-
yAxisColumn,
|
|
1327
|
-
yAxisJoinOperation
|
|
1328
|
-
} = params;
|
|
1329
|
-
const source = _this6.getModelSource(filterOwner);
|
|
1330
|
-
const spatialFiltersResolution = _this6._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1331
|
-
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
1332
|
-
const HARD_LIMIT = 500;
|
|
1333
|
-
return Promise.resolve(executeModel({
|
|
1334
|
-
model: 'scatterplot',
|
|
1335
|
-
source: {
|
|
1336
|
-
...source,
|
|
1337
|
-
spatialFiltersResolution,
|
|
1338
|
-
spatialFiltersMode,
|
|
1339
|
-
spatialFilter
|
|
1340
|
-
},
|
|
1341
|
-
params: {
|
|
1342
|
-
xAxisColumn,
|
|
1343
|
-
xAxisJoinOperation,
|
|
1344
|
-
yAxisColumn,
|
|
1345
|
-
yAxisJoinOperation,
|
|
1346
|
-
limit: HARD_LIMIT
|
|
1347
|
-
},
|
|
1348
|
-
opts: {
|
|
1349
|
-
abortController
|
|
927
|
+
// Omit empty and invisible tiles for simpler processing and types.
|
|
928
|
+
tiles = tiles.filter(isRasterTileVisible);
|
|
929
|
+
if (tiles.length === 0) return [];
|
|
930
|
+
// Raster tiles, and all pixels, are quadbin cells. Resolution of a pixel is
|
|
931
|
+
// the resolution of the tile, plus the number of subdivisions. Block size
|
|
932
|
+
// must be square, N x N, where N is a power of two.
|
|
933
|
+
const tileResolution = quadbin.getResolution(tiles[0].index.q);
|
|
934
|
+
const tileBlockSize = tiles[0].data.blockSize;
|
|
935
|
+
const cellResolution = tileResolution + BigInt(Math.log2(tileBlockSize));
|
|
936
|
+
// Compute covering cells for the spatial filter, at same resolution as the
|
|
937
|
+
// raster pixels, to be used as a mask.
|
|
938
|
+
const spatialFilterCells = new Set(quadbin.geometryToCells(options.spatialFilter, cellResolution));
|
|
939
|
+
const data = new Map();
|
|
940
|
+
for (const tile of tiles) {
|
|
941
|
+
const parent = tile.index.q;
|
|
942
|
+
const children = cellToChildrenSorted(parent, cellResolution);
|
|
943
|
+
// For each pixel/cell within the spatial filter, create a FeatureData.
|
|
944
|
+
// Order is row-major, starting from NW and ending at SE.
|
|
945
|
+
for (let i = 0; i < children.length; i++) {
|
|
946
|
+
if (!spatialFilterCells.has(children[i])) continue;
|
|
947
|
+
const cellData = {};
|
|
948
|
+
let cellDataExists = false;
|
|
949
|
+
for (const band in tile.data.cells.numericProps) {
|
|
950
|
+
const value = tile.data.cells.numericProps[band].value[i];
|
|
951
|
+
// TODO(cleanup): nodata should be a number, not a string.
|
|
952
|
+
if (Number(bandMetadataByName[band].nodata) !== value) {
|
|
953
|
+
cellData[band] = tile.data.cells.numericProps[band].value[i];
|
|
954
|
+
cellDataExists = true;
|
|
1350
955
|
}
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
} = _ref3;
|
|
1356
|
-
return [x, y];
|
|
1357
|
-
})));
|
|
1358
|
-
} catch (e) {
|
|
1359
|
-
return Promise.reject(e);
|
|
956
|
+
}
|
|
957
|
+
if (cellDataExists) {
|
|
958
|
+
data.set(children[i], cellData);
|
|
959
|
+
}
|
|
1360
960
|
}
|
|
1361
961
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
...source,
|
|
1386
|
-
spatialFiltersResolution,
|
|
1387
|
-
spatialFiltersMode,
|
|
1388
|
-
spatialFilter
|
|
1389
|
-
},
|
|
1390
|
-
params: {
|
|
1391
|
-
column: columns,
|
|
1392
|
-
sortBy,
|
|
1393
|
-
sortDirection,
|
|
1394
|
-
limit,
|
|
1395
|
-
offset
|
|
1396
|
-
},
|
|
1397
|
-
opts: {
|
|
1398
|
-
abortController
|
|
1399
|
-
}
|
|
1400
|
-
}).then(res => ({
|
|
1401
|
-
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
1402
|
-
rows: res.rows ?? res.ROWS,
|
|
1403
|
-
totalCount: res.metadata?.total ?? res.METADATA?.TOTAL
|
|
1404
|
-
})));
|
|
1405
|
-
} catch (e) {
|
|
1406
|
-
return Promise.reject(e);
|
|
962
|
+
return Array.from(data.values());
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Detects whether a given {@link Tile} is a {@link RasterTile}.
|
|
966
|
+
* @privateRemarks Method of detection is arbitrary, and may be changed.
|
|
967
|
+
*/
|
|
968
|
+
function isRasterTile(tile) {
|
|
969
|
+
return !!tile.data?.cells;
|
|
970
|
+
}
|
|
971
|
+
function isRasterTileVisible(tile) {
|
|
972
|
+
return !!(tile.isVisible && tile.data?.cells?.numericProps);
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* For the raster format, children are sorted in row-major order, starting from
|
|
976
|
+
* NW and ending at SE. Order returned by quadbin's cellToChildren() is not
|
|
977
|
+
* defined (and not related to the raster format), so sort explicitly here.
|
|
978
|
+
*/
|
|
979
|
+
function cellToChildrenSorted(parent, resolution) {
|
|
980
|
+
return quadbin.cellToChildren(parent, resolution).sort((cellA, cellB) => {
|
|
981
|
+
const tileA = quadbin.cellToTile(cellA);
|
|
982
|
+
const tileB = quadbin.cellToTile(cellB);
|
|
983
|
+
if (tileA.y !== tileB.y) {
|
|
984
|
+
return tileA.y > tileB.y ? 1 : -1;
|
|
1407
985
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
} = params;
|
|
1431
|
-
const source = _this8.getModelSource(filterOwner);
|
|
1432
|
-
const spatialFiltersResolution = _this8._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
1433
|
-
return Promise.resolve(executeModel({
|
|
1434
|
-
model: 'timeseries',
|
|
1435
|
-
source: {
|
|
1436
|
-
...source,
|
|
1437
|
-
spatialFiltersResolution,
|
|
1438
|
-
spatialFiltersMode,
|
|
1439
|
-
spatialFilter
|
|
1440
|
-
},
|
|
1441
|
-
params: {
|
|
1442
|
-
column,
|
|
1443
|
-
stepSize,
|
|
1444
|
-
stepMultiplier,
|
|
1445
|
-
operationColumn: operationColumn || column,
|
|
1446
|
-
joinOperation,
|
|
1447
|
-
operation,
|
|
1448
|
-
splitByCategory,
|
|
1449
|
-
splitByCategoryLimit,
|
|
1450
|
-
splitByCategoryValues
|
|
1451
|
-
},
|
|
1452
|
-
opts: {
|
|
1453
|
-
abortController
|
|
1454
|
-
}
|
|
1455
|
-
}).then(res => ({
|
|
1456
|
-
rows: normalizeObjectKeys(res.rows),
|
|
1457
|
-
categories: res.metadata?.categories
|
|
1458
|
-
})));
|
|
1459
|
-
} catch (e) {
|
|
1460
|
-
return Promise.reject(e);
|
|
986
|
+
return tileA.x > tileB.x ? 1 : -1;
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
const FILTER_TYPES = new Set(Object.values(exports.FilterType));
|
|
991
|
+
const isFilterType = type => FILTER_TYPES.has(type);
|
|
992
|
+
/**
|
|
993
|
+
* @privateRemarks Source: @carto/react-widgets
|
|
994
|
+
* @internal
|
|
995
|
+
*/
|
|
996
|
+
function getApplicableFilters(owner, filters) {
|
|
997
|
+
if (!filters) return {};
|
|
998
|
+
const applicableFilters = {};
|
|
999
|
+
for (const column in filters) {
|
|
1000
|
+
for (const type in filters[column]) {
|
|
1001
|
+
if (!isFilterType(type)) continue;
|
|
1002
|
+
const filter = filters[column][type];
|
|
1003
|
+
const isApplicable = !owner || !filter?.owner || filter?.owner !== owner;
|
|
1004
|
+
if (filter && isApplicable) {
|
|
1005
|
+
applicableFilters[column] ||= {};
|
|
1006
|
+
applicableFilters[column][type] = filter;
|
|
1007
|
+
}
|
|
1461
1008
|
}
|
|
1462
1009
|
}
|
|
1010
|
+
return applicableFilters;
|
|
1463
1011
|
}
|
|
1464
|
-
|
|
1465
1012
|
/**
|
|
1466
|
-
*
|
|
1467
|
-
*
|
|
1468
|
-
* Generally not intended to be constructed directly. Instead, call
|
|
1469
|
-
* {@link vectorQuerySource}, {@link h3QuerySource}, or {@link quadbinQuerySource},
|
|
1470
|
-
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
1471
|
-
* for use by widget implementations.
|
|
1472
|
-
*
|
|
1473
|
-
* Example:
|
|
1474
|
-
*
|
|
1475
|
-
* ```javascript
|
|
1476
|
-
* import { vectorQuerySource } from '@carto/api-client';
|
|
1477
|
-
*
|
|
1478
|
-
* const data = vectorQuerySource({
|
|
1479
|
-
* accessToken: '••••',
|
|
1480
|
-
* connectionName: 'carto_dw',
|
|
1481
|
-
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
1482
|
-
* });
|
|
1013
|
+
* Due to each data warehouse having its own behavior with columns,
|
|
1014
|
+
* we need to normalize them and transform every key to lowercase.
|
|
1483
1015
|
*
|
|
1484
|
-
*
|
|
1485
|
-
*
|
|
1016
|
+
* @privateRemarks Source: @carto/react-widgets
|
|
1017
|
+
* @internal
|
|
1486
1018
|
*/
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
return
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1019
|
+
function normalizeObjectKeys(el) {
|
|
1020
|
+
if (Array.isArray(el)) {
|
|
1021
|
+
return el.map(value => normalizeObjectKeys(value));
|
|
1022
|
+
} else if (typeof el !== 'object') {
|
|
1023
|
+
return el;
|
|
1024
|
+
}
|
|
1025
|
+
return Object.entries(el).reduce((acc, _ref) => {
|
|
1026
|
+
let [key, value] = _ref;
|
|
1027
|
+
acc[key.toLowerCase()] = typeof value === 'object' && value ? normalizeObjectKeys(value) : value;
|
|
1028
|
+
return acc;
|
|
1029
|
+
}, {});
|
|
1030
|
+
}
|
|
1031
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1032
|
+
function assert(condition, message) {
|
|
1033
|
+
if (!condition) {
|
|
1034
|
+
throw new Error(message);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* @privateRemarks Source: @carto/react-core
|
|
1039
|
+
* @internal
|
|
1040
|
+
*/
|
|
1041
|
+
class InvalidColumnError extends Error {
|
|
1042
|
+
constructor(message) {
|
|
1043
|
+
super(`${InvalidColumnError.NAME}: ${message}`);
|
|
1044
|
+
this.name = InvalidColumnError.NAME;
|
|
1045
|
+
}
|
|
1046
|
+
static is(error) {
|
|
1047
|
+
return error instanceof InvalidColumnError || error.message?.includes(InvalidColumnError.NAME);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
InvalidColumnError.NAME = 'InvalidColumnError';
|
|
1051
|
+
function isEmptyObject(object) {
|
|
1052
|
+
for (const _ in object) {
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
return true;
|
|
1056
|
+
}
|
|
1057
|
+
/** @internal */
|
|
1058
|
+
const isObject = x => x !== null && typeof x === 'object';
|
|
1059
|
+
/** @internal */
|
|
1060
|
+
const isPureObject = x => isObject(x) && x.constructor === {}.constructor;
|
|
1061
|
+
|
|
1062
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1063
|
+
function tileFeatures(_ref) {
|
|
1064
|
+
let {
|
|
1065
|
+
tiles,
|
|
1066
|
+
spatialFilter,
|
|
1067
|
+
uniqueIdProperty,
|
|
1068
|
+
tileFormat,
|
|
1069
|
+
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
1070
|
+
spatialDataType,
|
|
1071
|
+
rasterMetadata,
|
|
1072
|
+
options = {}
|
|
1073
|
+
} = _ref;
|
|
1074
|
+
if (spatialDataType === 'geo') {
|
|
1075
|
+
return tileFeaturesGeometries({
|
|
1076
|
+
tiles,
|
|
1077
|
+
tileFormat,
|
|
1078
|
+
spatialFilter,
|
|
1079
|
+
uniqueIdProperty,
|
|
1080
|
+
options
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
if (tiles.some(isRasterTile)) {
|
|
1084
|
+
assert(rasterMetadata, 'Missing raster metadata');
|
|
1085
|
+
return tileFeaturesRaster({
|
|
1086
|
+
tiles: tiles,
|
|
1087
|
+
spatialFilter,
|
|
1088
|
+
spatialDataColumn,
|
|
1089
|
+
spatialDataType,
|
|
1090
|
+
rasterMetadata
|
|
1091
|
+
});
|
|
1495
1092
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
return [Number.MIN_SAFE_INTEGER, val[1]];
|
|
1502
|
-
}
|
|
1503
|
-
if (val[1] === undefined || val[1] === null) {
|
|
1504
|
-
return [val[0], Number.MAX_SAFE_INTEGER];
|
|
1505
|
-
}
|
|
1506
|
-
return val;
|
|
1093
|
+
return tileFeaturesSpatialIndex({
|
|
1094
|
+
tiles: tiles,
|
|
1095
|
+
spatialFilter,
|
|
1096
|
+
spatialDataColumn,
|
|
1097
|
+
spatialDataType
|
|
1507
1098
|
});
|
|
1508
1099
|
}
|
|
1509
1100
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1101
|
+
/**
|
|
1102
|
+
* Creates props for DataFilterExtension, from `@deck.gl/extensions`, given
|
|
1103
|
+
* a set of filters.
|
|
1104
|
+
*
|
|
1105
|
+
* @privateRemarks DataFilterExtension accepts up to 4 values to filter. This
|
|
1106
|
+
* implementation uses the 1st for all filters except the time filter, and the
|
|
1107
|
+
* 2nd for the time filter.
|
|
1108
|
+
*/
|
|
1109
|
+
function getDataFilterExtensionProps(filters, filtersLogicalOperator, filterSize) {
|
|
1110
|
+
filterSize ??= 4;
|
|
1111
|
+
const {
|
|
1112
|
+
filtersWithoutTimeType,
|
|
1113
|
+
timeColumn,
|
|
1114
|
+
timeFilter
|
|
1115
|
+
} = getFiltersByType(filters);
|
|
1116
|
+
return {
|
|
1117
|
+
filterRange: getFilterRange(timeFilter, filterSize),
|
|
1118
|
+
updateTriggers: getUpdateTriggers(filtersWithoutTimeType, timeColumn, timeFilter),
|
|
1119
|
+
getFilterValue: getFilterValue(filtersWithoutTimeType, timeColumn, timeFilter, filterSize, filtersLogicalOperator)
|
|
1525
1120
|
};
|
|
1526
|
-
return makeIntervalComplete(filterValues).some(checkRange);
|
|
1527
1121
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1122
|
+
/** @internal */
|
|
1123
|
+
function getFiltersByType(filters) {
|
|
1124
|
+
const filtersWithoutTimeType = {};
|
|
1125
|
+
let timeColumn = null;
|
|
1126
|
+
let timeFilter = null;
|
|
1127
|
+
for (const [column, columnData] of Object.entries(filters)) {
|
|
1128
|
+
for (const [type, typeData] of Object.entries(columnData)) {
|
|
1129
|
+
if (type === exports.FilterType.TIME) {
|
|
1130
|
+
timeColumn = column;
|
|
1131
|
+
timeFilter = typeData;
|
|
1132
|
+
} else {
|
|
1133
|
+
filtersWithoutTimeType[column] = {
|
|
1134
|
+
[type]: typeData
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1534
1138
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
const [lowerBound, upperBound] = range;
|
|
1540
|
-
return featureValue >= lowerBound && featureValue < upperBound;
|
|
1139
|
+
return {
|
|
1140
|
+
filtersWithoutTimeType,
|
|
1141
|
+
timeColumn,
|
|
1142
|
+
timeFilter
|
|
1541
1143
|
};
|
|
1542
|
-
return makeIntervalComplete(filterValues).some(checkRange);
|
|
1543
1144
|
}
|
|
1544
|
-
|
|
1545
|
-
function
|
|
1546
|
-
|
|
1547
|
-
|
|
1145
|
+
/** @internal */
|
|
1146
|
+
function getFilterRange(timeFilter, filterSize) {
|
|
1147
|
+
const result = Array(filterSize).fill([0, 0]);
|
|
1148
|
+
// According to getFilterValue all filters are resolved as 0 or 1 in the first position of the array
|
|
1149
|
+
// except the time filter value that is resolved with the real value of the feature in the second position of the array
|
|
1150
|
+
result[0] = [1, 1];
|
|
1151
|
+
if (timeFilter) {
|
|
1152
|
+
const offsetBy = timeFilter.params?.offsetBy || 0;
|
|
1153
|
+
result[1] = timeFilter.values[0].map(v => v - offsetBy);
|
|
1548
1154
|
}
|
|
1549
|
-
|
|
1550
|
-
const stringRegExp = params.useRegExp ? filterValues : filterValues.map(filterValue => {
|
|
1551
|
-
let stringRegExp = escapeRegExp(normalize(filterValue, params));
|
|
1552
|
-
if (params.mustStart) stringRegExp = `^${stringRegExp}`;
|
|
1553
|
-
if (params.mustEnd) stringRegExp = `${stringRegExp}$`;
|
|
1554
|
-
return stringRegExp;
|
|
1555
|
-
});
|
|
1556
|
-
const regex = new RegExp(stringRegExp.join('|'), params.caseSensitive ? 'g' : 'gi');
|
|
1557
|
-
return !!normalizedFeatureValue.match(regex);
|
|
1558
|
-
}
|
|
1559
|
-
// Aux
|
|
1560
|
-
const specialCharRegExp = /[.*+?^${}()|[\]\\]/g;
|
|
1561
|
-
const normalizeRegExp = /\p{Diacritic}/gu;
|
|
1562
|
-
function escapeRegExp(value) {
|
|
1563
|
-
return value.replace(specialCharRegExp, '\\$&');
|
|
1564
|
-
}
|
|
1565
|
-
function normalize(data, params) {
|
|
1566
|
-
let normalizedData = String(data);
|
|
1567
|
-
if (!params.keepSpecialCharacters) normalizedData = normalizedData.normalize('NFD').replace(normalizeRegExp, '');
|
|
1568
|
-
return normalizedData;
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
const LOGICAL_OPERATOR_METHODS = {
|
|
1572
|
-
and: 'every',
|
|
1573
|
-
or: 'some'
|
|
1574
|
-
};
|
|
1575
|
-
function passesFilter(columns, filters, feature, filtersLogicalOperator) {
|
|
1576
|
-
const method = LOGICAL_OPERATOR_METHODS[filtersLogicalOperator];
|
|
1577
|
-
return columns[method](column => {
|
|
1578
|
-
const columnFilters = filters[column];
|
|
1579
|
-
const columnFilterTypes = Object.keys(columnFilters);
|
|
1580
|
-
if (!feature || feature[column] === null || feature[column] === undefined) {
|
|
1581
|
-
return false;
|
|
1582
|
-
}
|
|
1583
|
-
return columnFilterTypes.every(filter => {
|
|
1584
|
-
const filterFunction = filterFunctions[filter];
|
|
1585
|
-
if (!filterFunction) {
|
|
1586
|
-
throw new Error(`"${filter}" filter is not implemented.`);
|
|
1587
|
-
}
|
|
1588
|
-
return filterFunction(columnFilters[filter].values, feature[column], columnFilters[filter].params);
|
|
1589
|
-
});
|
|
1590
|
-
});
|
|
1155
|
+
return result;
|
|
1591
1156
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1157
|
+
/** @internal */
|
|
1158
|
+
function getUpdateTriggers(filtersWithoutTimeType, timeColumn, timeFilter) {
|
|
1159
|
+
const result = {
|
|
1160
|
+
...filtersWithoutTimeType
|
|
1161
|
+
};
|
|
1162
|
+
// We don't want to change the layer UpdateTriggers every time that the time filter changes
|
|
1163
|
+
// because this filter is changed by the time series widget during its animation
|
|
1164
|
+
// so we remove the time filter value from the `updateTriggers`
|
|
1165
|
+
if (timeColumn && timeFilter) {
|
|
1166
|
+
result[timeColumn] = {
|
|
1167
|
+
...result[timeColumn],
|
|
1168
|
+
offsetBy: timeFilter.params?.offsetBy,
|
|
1169
|
+
[exports.FilterType.TIME]: {} // Allows working with other filters, without an impact on performance.
|
|
1170
|
+
};
|
|
1601
1171
|
}
|
|
1602
|
-
return
|
|
1603
|
-
|
|
1604
|
-
const featurePassesFilter = passesFilter(columns, filters, f, filtersLogicalOperator);
|
|
1605
|
-
return type === 'number' ? Number(featurePassesFilter) : featurePassesFilter;
|
|
1172
|
+
return {
|
|
1173
|
+
getFilterValue: JSON.stringify(result)
|
|
1606
1174
|
};
|
|
1607
1175
|
}
|
|
1608
|
-
|
|
1609
|
-
function
|
|
1610
|
-
|
|
1611
|
-
|
|
1176
|
+
/** @internal */
|
|
1177
|
+
function getFilterValue(filtersWithoutTimeType, timeColumn, timeFilter, filterSize, filtersLogicalOperator) {
|
|
1178
|
+
const result = Array(filterSize).fill(0);
|
|
1179
|
+
const featureFilter = _buildFeatureFilter({
|
|
1180
|
+
filters: filtersWithoutTimeType,
|
|
1181
|
+
type: 'number',
|
|
1612
1182
|
filtersLogicalOperator
|
|
1613
|
-
})) : features;
|
|
1614
|
-
}
|
|
1615
|
-
// Binary
|
|
1616
|
-
function buildBinaryFeatureFilter(_ref2) {
|
|
1617
|
-
let {
|
|
1618
|
-
filters = {}
|
|
1619
|
-
} = _ref2;
|
|
1620
|
-
const columns = Object.keys(filters);
|
|
1621
|
-
if (!columns.length) {
|
|
1622
|
-
return () => 1;
|
|
1623
|
-
}
|
|
1624
|
-
return (featureIdIdx, binaryData) => passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
|
|
1625
|
-
}
|
|
1626
|
-
function getValueFromNumericProps(featureIdIdx, binaryData, _ref3) {
|
|
1627
|
-
let {
|
|
1628
|
-
column
|
|
1629
|
-
} = _ref3;
|
|
1630
|
-
return binaryData.numericProps?.[column]?.value[featureIdIdx];
|
|
1631
|
-
}
|
|
1632
|
-
function getValueFromProperties(featureIdIdx, binaryData, _ref4) {
|
|
1633
|
-
let {
|
|
1634
|
-
column
|
|
1635
|
-
} = _ref4;
|
|
1636
|
-
const propertyIdx = binaryData.featureIds.value[featureIdIdx];
|
|
1637
|
-
return binaryData.properties[propertyIdx]?.[column];
|
|
1638
|
-
}
|
|
1639
|
-
const GET_VALUE_BY_BINARY_PROP = {
|
|
1640
|
-
properties: getValueFromProperties,
|
|
1641
|
-
numericProps: getValueFromNumericProps
|
|
1642
|
-
};
|
|
1643
|
-
function getBinaryPropertyByFilterValues(filterValues) {
|
|
1644
|
-
return typeof filterValues.flat()[0] === 'string' ? 'properties' : 'numericProps';
|
|
1645
|
-
}
|
|
1646
|
-
function getFeatureValue(featureIdIdx, binaryData, filter) {
|
|
1647
|
-
const {
|
|
1648
|
-
column,
|
|
1649
|
-
values
|
|
1650
|
-
} = filter;
|
|
1651
|
-
const binaryProp = getBinaryPropertyByFilterValues(values);
|
|
1652
|
-
const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
|
|
1653
|
-
return getFeatureValueFn(featureIdIdx, binaryData, {
|
|
1654
|
-
column
|
|
1655
|
-
});
|
|
1656
|
-
}
|
|
1657
|
-
function passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData) {
|
|
1658
|
-
return columns.every(column => {
|
|
1659
|
-
const columnFilters = filters[column];
|
|
1660
|
-
return Object.entries(columnFilters).every(_ref5 => {
|
|
1661
|
-
let [type, {
|
|
1662
|
-
values
|
|
1663
|
-
}] = _ref5;
|
|
1664
|
-
const filterFn = filterFunctions[type];
|
|
1665
|
-
if (!filterFn) {
|
|
1666
|
-
throw new Error(`"${type}" filter is not implemented.`);
|
|
1667
|
-
}
|
|
1668
|
-
if (!values) return 0;
|
|
1669
|
-
const featureValue = getFeatureValue(featureIdIdx, binaryData, {
|
|
1670
|
-
type: type,
|
|
1671
|
-
column,
|
|
1672
|
-
values
|
|
1673
|
-
});
|
|
1674
|
-
if (featureValue === undefined || featureValue === null) return 0;
|
|
1675
|
-
return filterFn(values, featureValue);
|
|
1676
|
-
});
|
|
1677
1183
|
});
|
|
1184
|
+
// We evaluate all filters except the time filter using _buildFeatureFilter function.
|
|
1185
|
+
// For the time filter, we return the value of the feature and we will change the getFilterRange result
|
|
1186
|
+
// every time this filter changes
|
|
1187
|
+
return feature => {
|
|
1188
|
+
result[0] = featureFilter(feature);
|
|
1189
|
+
if (timeColumn && timeFilter) {
|
|
1190
|
+
const offsetBy = timeFilter.params?.offsetBy || 0;
|
|
1191
|
+
const f = feature.properties || feature;
|
|
1192
|
+
result[1] = f[timeColumn] - offsetBy;
|
|
1193
|
+
}
|
|
1194
|
+
return result;
|
|
1195
|
+
};
|
|
1678
1196
|
}
|
|
1679
1197
|
|
|
1680
|
-
|
|
1198
|
+
/**
|
|
1199
|
+
* Adds a {@link Filter} to the filter set. Any previous filters with the same
|
|
1200
|
+
* `column` and `type` will be replaced.
|
|
1201
|
+
*/
|
|
1202
|
+
function addFilter(filters, _ref) {
|
|
1681
1203
|
let {
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1204
|
+
column,
|
|
1205
|
+
type,
|
|
1206
|
+
values,
|
|
1207
|
+
owner
|
|
1685
1208
|
} = _ref;
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
if (!spatialFilter) {
|
|
1689
|
-
return [];
|
|
1690
|
-
}
|
|
1691
|
-
for (const feature of geojson.features) {
|
|
1692
|
-
const uniqueId = uniqueIdProperty ? feature.properties[uniqueIdProperty] : ++uniqueIdx;
|
|
1693
|
-
if (!map.has(uniqueId) && intersects(spatialFilter, feature)) {
|
|
1694
|
-
map.set(uniqueId, feature.properties);
|
|
1695
|
-
}
|
|
1209
|
+
if (!filters[column]) {
|
|
1210
|
+
filters[column] = {};
|
|
1696
1211
|
}
|
|
1697
|
-
|
|
1212
|
+
const filter = {
|
|
1213
|
+
values,
|
|
1214
|
+
owner
|
|
1215
|
+
};
|
|
1216
|
+
filters[column][type] = filter;
|
|
1217
|
+
return filters;
|
|
1698
1218
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
// Multiple copies of config can be quite tricky to debug...
|
|
1714
|
-
globalThis.mathgl = globalThis.mathgl || {
|
|
1715
|
-
config: {
|
|
1716
|
-
...DEFAULT_CONFIG
|
|
1219
|
+
/**
|
|
1220
|
+
* Removes one or more {@link Filter filters} from the filter set. If only
|
|
1221
|
+
* `column` is specified, then all filters on that column are removed. If both
|
|
1222
|
+
* `column` and `owner` are specified, then only filters for that column
|
|
1223
|
+
* associated with the owner are removed.
|
|
1224
|
+
*/
|
|
1225
|
+
function removeFilter(filters, _ref2) {
|
|
1226
|
+
let {
|
|
1227
|
+
column,
|
|
1228
|
+
owner
|
|
1229
|
+
} = _ref2;
|
|
1230
|
+
const filter = filters[column];
|
|
1231
|
+
if (!filter) {
|
|
1232
|
+
return filters;
|
|
1717
1233
|
}
|
|
1718
|
-
|
|
1234
|
+
if (owner) {
|
|
1235
|
+
for (const type of Object.values(exports.FilterType)) {
|
|
1236
|
+
if (owner === filter[type]?.owner) {
|
|
1237
|
+
delete filter[type];
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
if (!owner || isEmptyObject(filter)) {
|
|
1242
|
+
delete filters[column];
|
|
1243
|
+
}
|
|
1244
|
+
return filters;
|
|
1245
|
+
}
|
|
1719
1246
|
/**
|
|
1720
|
-
*
|
|
1721
|
-
* Returns `true` if value is either an array or a typed array
|
|
1722
|
-
* Note: returns `false` for `ArrayBuffer` and `DataView` instances
|
|
1723
|
-
* @note isTypedArray and isNumericArray are often more useful in TypeScript
|
|
1247
|
+
* Clears all {@link Filter filters} from the filter set.
|
|
1724
1248
|
*/
|
|
1725
|
-
function
|
|
1726
|
-
|
|
1249
|
+
function clearFilters(filters) {
|
|
1250
|
+
for (const column of Object.keys(filters)) {
|
|
1251
|
+
delete filters[column];
|
|
1252
|
+
}
|
|
1253
|
+
return filters;
|
|
1727
1254
|
}
|
|
1728
|
-
function
|
|
1729
|
-
|
|
1730
|
-
|
|
1255
|
+
function hasFilter(filters, _ref3) {
|
|
1256
|
+
let {
|
|
1257
|
+
column,
|
|
1258
|
+
owner
|
|
1259
|
+
} = _ref3;
|
|
1260
|
+
const filter = filters[column];
|
|
1261
|
+
if (!filter) {
|
|
1262
|
+
return false;
|
|
1731
1263
|
}
|
|
1732
|
-
|
|
1264
|
+
if (!owner) {
|
|
1265
|
+
return true;
|
|
1266
|
+
}
|
|
1267
|
+
for (const type of Object.values(exports.FilterType)) {
|
|
1268
|
+
if (owner === filter[type]?.owner) {
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return false;
|
|
1733
1273
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1274
|
+
function getFilter(filters, _ref4) {
|
|
1275
|
+
let {
|
|
1276
|
+
column,
|
|
1277
|
+
type,
|
|
1278
|
+
owner
|
|
1279
|
+
} = _ref4;
|
|
1280
|
+
const filter = filters[column];
|
|
1281
|
+
if (!filter) {
|
|
1282
|
+
return null;
|
|
1741
1283
|
}
|
|
1284
|
+
if (!owner || owner === filter[type]?.owner) {
|
|
1285
|
+
return filter[type] || null;
|
|
1286
|
+
}
|
|
1287
|
+
return null;
|
|
1742
1288
|
}
|
|
1743
1289
|
|
|
1744
|
-
// TODO - THE UTILITIES IN THIS FILE SHOULD BE IMPORTED FROM WEB-MERCATOR-VIEWPORT MODULE
|
|
1745
|
-
// CONSTANTS
|
|
1746
|
-
const PI = Math.PI;
|
|
1747
|
-
const PI_4 = PI / 4;
|
|
1748
|
-
const DEGREES_TO_RADIANS = PI / 180;
|
|
1749
|
-
const RADIANS_TO_DEGREES = 180 / PI;
|
|
1750
|
-
const TILE_SIZE = 512;
|
|
1751
1290
|
/**
|
|
1752
|
-
*
|
|
1753
|
-
*
|
|
1754
|
-
*
|
|
1755
|
-
*
|
|
1291
|
+
* Returns a {@link SpatialFilter} for a given viewport, typically obtained
|
|
1292
|
+
* from deck.gl's `viewport.getBounds()` method ([west, south, east, north]).
|
|
1293
|
+
* If the viewport covers the entire world (to some margin of error in Web
|
|
1294
|
+
* Mercator space), `undefined` is returned instead.
|
|
1756
1295
|
*
|
|
1757
|
-
*
|
|
1758
|
-
*
|
|
1759
|
-
* @return [x,y] coordinates.
|
|
1296
|
+
* If the viewport extends beyond longitude range [-180, +180], the polygon
|
|
1297
|
+
* may be reformatted for compatibility with CARTO APIs.
|
|
1760
1298
|
*/
|
|
1761
|
-
function
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
const phi2 = lat * DEGREES_TO_RADIANS;
|
|
1767
|
-
const x = TILE_SIZE * (lambda2 + PI) / (2 * PI);
|
|
1768
|
-
const y = TILE_SIZE * (PI + Math.log(Math.tan(PI_4 + phi2 * 0.5))) / (2 * PI);
|
|
1769
|
-
return [x, y];
|
|
1299
|
+
function createViewportSpatialFilter(viewport) {
|
|
1300
|
+
if (_isGlobalViewport(viewport)) {
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
return createPolygonSpatialFilter(bboxPolygon(viewport).geometry);
|
|
1770
1304
|
}
|
|
1771
1305
|
/**
|
|
1772
|
-
*
|
|
1306
|
+
* Returns a {@link SpatialFilter} for a given {@link Polygon} or
|
|
1307
|
+
* {@link MultiPolygon}. If the polygon(s) extend outside longitude
|
|
1308
|
+
* range [-180, +180], the result may be reformatted for compatibility
|
|
1309
|
+
* with CARTO APIs.
|
|
1310
|
+
*/
|
|
1311
|
+
function createPolygonSpatialFilter(spatialFilter) {
|
|
1312
|
+
return spatialFilter && _normalizeGeometry(spatialFilter) || undefined;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Check if a viewport is large enough to represent a global coverage.
|
|
1316
|
+
* In this case the spatial filter parameter for widget calculation is removed.
|
|
1773
1317
|
*
|
|
1774
|
-
* @
|
|
1775
|
-
* representing point on projected map plane
|
|
1776
|
-
* @return - array with [x,y] of point on sphere.
|
|
1777
|
-
* Has toArray method if you need a GeoJSON Array.
|
|
1778
|
-
* Per cartographic tradition, lat and lon are specified as degrees.
|
|
1318
|
+
* @privateRemarks Source: @carto/react-core
|
|
1779
1319
|
*/
|
|
1780
|
-
function
|
|
1781
|
-
const [
|
|
1782
|
-
|
|
1783
|
-
const phi2 = 2 * (Math.atan(Math.exp(y / TILE_SIZE * (2 * PI) - PI)) - PI_4);
|
|
1784
|
-
return [lambda2 * RADIANS_TO_DEGREES, phi2 * RADIANS_TO_DEGREES];
|
|
1320
|
+
function _isGlobalViewport(viewport) {
|
|
1321
|
+
const [minx, miny, maxx, maxy] = viewport;
|
|
1322
|
+
return maxx - minx > 179.5 * 2 && maxy - miny > 85.05 * 2;
|
|
1785
1323
|
}
|
|
1786
|
-
|
|
1787
|
-
const TRANSFORM_FN$1 = {
|
|
1788
|
-
Point: transformPoint$1,
|
|
1789
|
-
MultiPoint: transformMultiPoint$1,
|
|
1790
|
-
LineString: transformLineString$1,
|
|
1791
|
-
MultiLineString: transformMultiLineString$1,
|
|
1792
|
-
Polygon: transformPolygon$1,
|
|
1793
|
-
MultiPolygon: transformMultiPolygon$1
|
|
1794
|
-
};
|
|
1795
1324
|
/**
|
|
1796
|
-
*
|
|
1797
|
-
*
|
|
1325
|
+
* Normalized a geometry, coming from a mask or a viewport. The parts
|
|
1326
|
+
* spanning outside longitude range [-180, +180] are clipped and "folded"
|
|
1327
|
+
* back to the valid range and unioned to the polygons inide that range.
|
|
1798
1328
|
*
|
|
1799
|
-
*
|
|
1800
|
-
*
|
|
1329
|
+
* It results in a Polygon or MultiPolygon strictly inside the validity range.
|
|
1330
|
+
*
|
|
1331
|
+
* @privateRemarks Source: @carto/react-core
|
|
1801
1332
|
*/
|
|
1802
|
-
function
|
|
1803
|
-
const [
|
|
1804
|
-
const
|
|
1805
|
-
const
|
|
1806
|
-
const
|
|
1807
|
-
|
|
1808
|
-
|
|
1333
|
+
function _normalizeGeometry(geometry) {
|
|
1334
|
+
const WORLD = [-180, -90, +180, +90];
|
|
1335
|
+
const worldClip = _clean(bboxClip(geometry, WORLD).geometry);
|
|
1336
|
+
const geometryTxWest = _tx(geometry, 360);
|
|
1337
|
+
const geometryTxEast = _tx(geometry, -360);
|
|
1338
|
+
let result = worldClip;
|
|
1339
|
+
if (result && geometryTxWest) {
|
|
1340
|
+
const worldWestClip = _clean(bboxClip(geometryTxWest, WORLD).geometry);
|
|
1341
|
+
if (worldWestClip) {
|
|
1342
|
+
const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldWestClip)]);
|
|
1343
|
+
const merged = union(collection);
|
|
1344
|
+
result = merged ? _clean(merged.geometry) : result;
|
|
1345
|
+
}
|
|
1809
1346
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1347
|
+
if (result && geometryTxEast) {
|
|
1348
|
+
const worldEastClip = _clean(bboxClip(geometryTxEast, WORLD).geometry);
|
|
1349
|
+
if (worldEastClip) {
|
|
1350
|
+
const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldEastClip)]);
|
|
1351
|
+
const merged = union(collection);
|
|
1352
|
+
result = merged ? _clean(merged.geometry) : result;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return result;
|
|
1816
1356
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
const y = inverseLerp(nw[1], se[1], pointY);
|
|
1822
|
-
return [x, y];
|
|
1357
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1358
|
+
function _cleanPolygonCoords(cc) {
|
|
1359
|
+
const coords = cc.filter(c => c.length > 0);
|
|
1360
|
+
return coords.length > 0 ? coords : null;
|
|
1823
1361
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1362
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1363
|
+
function _cleanMultiPolygonCoords(ccc) {
|
|
1364
|
+
const coords = ccc.map(_cleanPolygonCoords).filter(cc => cc);
|
|
1365
|
+
return coords.length > 0 ? coords : null;
|
|
1826
1366
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1367
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1368
|
+
function _clean(geometry) {
|
|
1369
|
+
if (!geometry) {
|
|
1370
|
+
return null;
|
|
1371
|
+
}
|
|
1372
|
+
if (_isPolygon(geometry)) {
|
|
1373
|
+
const coords = _cleanPolygonCoords(geometry.coordinates);
|
|
1374
|
+
return coords ? helpers.polygon(coords).geometry : null;
|
|
1375
|
+
}
|
|
1376
|
+
if (_isMultiPolygon(geometry)) {
|
|
1377
|
+
const coords = _cleanMultiPolygonCoords(geometry.coordinates);
|
|
1378
|
+
return coords ? helpers.multiPolygon(coords).geometry : null;
|
|
1379
|
+
}
|
|
1380
|
+
return null;
|
|
1829
1381
|
}
|
|
1830
|
-
|
|
1831
|
-
|
|
1382
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1383
|
+
function _txContourCoords(cc, distance) {
|
|
1384
|
+
return cc.map(c => [c[0] + distance, c[1]]);
|
|
1832
1385
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1386
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1387
|
+
function _txPolygonCoords(ccc, distance) {
|
|
1388
|
+
return ccc.map(cc => _txContourCoords(cc, distance));
|
|
1835
1389
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1390
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1391
|
+
function _txMultiPolygonCoords(cccc, distance) {
|
|
1392
|
+
return cccc.map(ccc => _txPolygonCoords(ccc, distance));
|
|
1393
|
+
}
|
|
1394
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
1395
|
+
function _tx(geometry, distance) {
|
|
1396
|
+
if (geometry && invariant.getType(geometry) === 'Polygon') {
|
|
1397
|
+
const coords = _txPolygonCoords(geometry.coordinates, distance);
|
|
1398
|
+
return helpers.polygon(coords).geometry;
|
|
1399
|
+
} else if (geometry && invariant.getType(geometry) === 'MultiPolygon') {
|
|
1400
|
+
const coords = _txMultiPolygonCoords(geometry.coordinates, distance);
|
|
1401
|
+
return helpers.multiPolygon(coords).geometry;
|
|
1402
|
+
} else {
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
function _isPolygon(geometry) {
|
|
1407
|
+
return invariant.getType(geometry) === 'Polygon';
|
|
1408
|
+
}
|
|
1409
|
+
function _isMultiPolygon(geometry) {
|
|
1410
|
+
return invariant.getType(geometry) === 'MultiPolygon';
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// deck.gl
|
|
1414
|
+
// SPDX-License-Identifier: MIT
|
|
1415
|
+
// Copyright (c) vis.gl contributors
|
|
1416
|
+
function joinPath() {
|
|
1417
|
+
return [].slice.call(arguments).map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
|
|
1418
|
+
}
|
|
1419
|
+
function buildV3Path(apiBaseUrl, version, endpoint) {
|
|
1420
|
+
return joinPath(apiBaseUrl, version, endpoint, ...[].slice.call(arguments, 3));
|
|
1421
|
+
}
|
|
1422
|
+
/** @internal Required by fetchMap(). */
|
|
1423
|
+
function buildPublicMapUrl(_ref) {
|
|
1424
|
+
let {
|
|
1425
|
+
apiBaseUrl,
|
|
1426
|
+
cartoMapId
|
|
1427
|
+
} = _ref;
|
|
1428
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', 'public', cartoMapId);
|
|
1838
1429
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1430
|
+
/** @internal Required by fetchMap(). */
|
|
1431
|
+
function buildStatsUrl(_ref2) {
|
|
1432
|
+
let {
|
|
1433
|
+
attribute,
|
|
1434
|
+
apiBaseUrl,
|
|
1435
|
+
connectionName,
|
|
1436
|
+
source,
|
|
1437
|
+
type
|
|
1438
|
+
} = _ref2;
|
|
1439
|
+
if (type === 'query') {
|
|
1440
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, attribute);
|
|
1441
|
+
}
|
|
1442
|
+
// type === 'table'
|
|
1443
|
+
return buildV3Path(apiBaseUrl, 'v3', 'stats', connectionName, source, attribute);
|
|
1841
1444
|
}
|
|
1842
|
-
function
|
|
1843
|
-
|
|
1445
|
+
function buildSourceUrl(_ref3) {
|
|
1446
|
+
let {
|
|
1447
|
+
apiBaseUrl,
|
|
1448
|
+
connectionName,
|
|
1449
|
+
endpoint
|
|
1450
|
+
} = _ref3;
|
|
1451
|
+
return buildV3Path(apiBaseUrl, 'v3', 'maps', connectionName, endpoint);
|
|
1844
1452
|
}
|
|
1845
|
-
function
|
|
1846
|
-
|
|
1453
|
+
function buildQueryUrl(_ref4) {
|
|
1454
|
+
let {
|
|
1455
|
+
apiBaseUrl,
|
|
1456
|
+
connectionName
|
|
1457
|
+
} = _ref4;
|
|
1458
|
+
return buildV3Path(apiBaseUrl, 'v3', 'sql', connectionName, 'query');
|
|
1847
1459
|
}
|
|
1848
1460
|
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
LineString: transformLineString,
|
|
1853
|
-
MultiLineString: transformMultiLineString,
|
|
1854
|
-
Polygon: transformPolygon,
|
|
1855
|
-
MultiPolygon: transformMultiPolygon
|
|
1856
|
-
};
|
|
1461
|
+
// deck.gl
|
|
1462
|
+
// SPDX-License-Identifier: MIT
|
|
1463
|
+
// Copyright (c) vis.gl contributors
|
|
1857
1464
|
/**
|
|
1858
|
-
* Transform tile coords to WGS84 coordinates.
|
|
1859
1465
|
*
|
|
1860
|
-
*
|
|
1861
|
-
*
|
|
1466
|
+
* Custom error for reported errors in CARTO Maps API.
|
|
1467
|
+
* Provides useful debugging information in console and context for applications.
|
|
1468
|
+
*
|
|
1862
1469
|
*/
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1470
|
+
class CartoAPIError extends Error {
|
|
1471
|
+
constructor(error, errorContext, response, responseJson) {
|
|
1472
|
+
let responseString = 'Failed to connect';
|
|
1473
|
+
if (response) {
|
|
1474
|
+
responseString = 'Server returned: ';
|
|
1475
|
+
if (response.status === 400) {
|
|
1476
|
+
responseString += 'Bad request';
|
|
1477
|
+
} else if (response.status === 401 || response.status === 403) {
|
|
1478
|
+
responseString += 'Unauthorized access';
|
|
1479
|
+
} else if (response.status === 404) {
|
|
1480
|
+
responseString += 'Not found';
|
|
1481
|
+
} else {
|
|
1482
|
+
responseString += 'Error';
|
|
1483
|
+
}
|
|
1484
|
+
responseString += ` (${response.status}):`;
|
|
1485
|
+
}
|
|
1486
|
+
responseString += ` ${error.message || error}`;
|
|
1487
|
+
let message = `${errorContext.requestType} API request failed`;
|
|
1488
|
+
message += `\n${responseString}`;
|
|
1489
|
+
for (const key of Object.keys(errorContext)) {
|
|
1490
|
+
if (key === 'requestType') continue;
|
|
1491
|
+
message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
|
|
1492
|
+
}
|
|
1493
|
+
message += '\n';
|
|
1494
|
+
super(message);
|
|
1495
|
+
/** Source error from server */
|
|
1496
|
+
this.error = void 0;
|
|
1497
|
+
/** Context (API call & parameters) in which error occured */
|
|
1498
|
+
this.errorContext = void 0;
|
|
1499
|
+
/** Response from server */
|
|
1500
|
+
this.response = void 0;
|
|
1501
|
+
/** JSON Response from server */
|
|
1502
|
+
this.responseJson = void 0;
|
|
1503
|
+
this.name = 'CartoAPIError';
|
|
1504
|
+
this.response = response;
|
|
1505
|
+
this.responseJson = responseJson;
|
|
1506
|
+
this.error = error;
|
|
1507
|
+
this.errorContext = errorContext;
|
|
1870
1508
|
}
|
|
1871
|
-
const transformFn = TRANSFORM_FN[geometry.type];
|
|
1872
|
-
const coordinates = transformFn(geometry.coordinates, projectedBbox);
|
|
1873
|
-
return {
|
|
1874
|
-
...geometry,
|
|
1875
|
-
coordinates
|
|
1876
|
-
};
|
|
1877
|
-
}
|
|
1878
|
-
function transformPoint(_ref, _ref2) {
|
|
1879
|
-
let [pointX, pointY] = _ref;
|
|
1880
|
-
let [nw, se] = _ref2;
|
|
1881
|
-
const x = lerp(nw[0], se[0], pointX);
|
|
1882
|
-
const y = lerp(nw[1], se[1], pointY);
|
|
1883
|
-
return worldToLngLat([x, y]);
|
|
1884
|
-
}
|
|
1885
|
-
function getPoints(geometry, bbox) {
|
|
1886
|
-
return geometry.map(g => transformPoint(g, bbox));
|
|
1887
1509
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1510
|
+
/**
|
|
1511
|
+
* Converts camelCase to Camel Case
|
|
1512
|
+
*/
|
|
1513
|
+
function formatErrorKey(key) {
|
|
1514
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
|
|
1890
1515
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1516
|
+
|
|
1517
|
+
// deck.gl
|
|
1518
|
+
// SPDX-License-Identifier: MIT
|
|
1519
|
+
// Copyright (c) vis.gl contributors
|
|
1520
|
+
const requestWithParameters = function (_ref) {
|
|
1521
|
+
let {
|
|
1522
|
+
baseUrl,
|
|
1523
|
+
parameters = {},
|
|
1524
|
+
headers: customHeaders = {},
|
|
1525
|
+
errorContext,
|
|
1526
|
+
maxLengthURL = DEFAULT_MAX_LENGTH_URL,
|
|
1527
|
+
localCache
|
|
1528
|
+
} = _ref;
|
|
1529
|
+
try {
|
|
1530
|
+
// Parameters added to all requests issued with `requestWithParameters()`.
|
|
1531
|
+
// These parameters override parameters already in the base URL, but not
|
|
1532
|
+
// user-provided parameters.
|
|
1533
|
+
parameters = {
|
|
1534
|
+
v: V3_MINOR_VERSION,
|
|
1535
|
+
client: getClient(),
|
|
1536
|
+
...(typeof deck !== 'undefined' && deck.VERSION && {
|
|
1537
|
+
deckglVersion: deck.VERSION
|
|
1538
|
+
}),
|
|
1539
|
+
...parameters
|
|
1540
|
+
};
|
|
1541
|
+
baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
|
|
1542
|
+
const key = createCacheKey(baseUrl, parameters, customHeaders);
|
|
1543
|
+
const {
|
|
1544
|
+
cache: REQUEST_CACHE,
|
|
1545
|
+
canReadCache,
|
|
1546
|
+
canStoreInCache
|
|
1547
|
+
} = getCacheSettings(localCache);
|
|
1548
|
+
if (canReadCache && REQUEST_CACHE.has(key)) {
|
|
1549
|
+
return Promise.resolve(REQUEST_CACHE.get(key));
|
|
1550
|
+
}
|
|
1551
|
+
const url = createURLWithParameters(baseUrl, parameters);
|
|
1552
|
+
const headers = {
|
|
1553
|
+
...DEFAULT_HEADERS,
|
|
1554
|
+
...customHeaders
|
|
1555
|
+
};
|
|
1556
|
+
/* global fetch */
|
|
1557
|
+
const fetchPromise = url.length > maxLengthURL ? fetch(baseUrl, {
|
|
1558
|
+
method: 'POST',
|
|
1559
|
+
body: JSON.stringify(parameters),
|
|
1560
|
+
headers
|
|
1561
|
+
}) : fetch(url, {
|
|
1562
|
+
headers
|
|
1563
|
+
});
|
|
1564
|
+
let response;
|
|
1565
|
+
let responseJson;
|
|
1566
|
+
const jsonPromise = fetchPromise.then(_response => {
|
|
1567
|
+
response = _response;
|
|
1568
|
+
return response.json();
|
|
1569
|
+
}).then(json => {
|
|
1570
|
+
responseJson = json;
|
|
1571
|
+
if (!response || !response.ok) {
|
|
1572
|
+
throw new Error(json.error);
|
|
1573
|
+
}
|
|
1574
|
+
return json;
|
|
1575
|
+
}).catch(error => {
|
|
1576
|
+
if (canStoreInCache) {
|
|
1577
|
+
REQUEST_CACHE.delete(key);
|
|
1578
|
+
}
|
|
1579
|
+
throw new CartoAPIError(error, errorContext, response, responseJson);
|
|
1580
|
+
});
|
|
1581
|
+
if (canStoreInCache) {
|
|
1582
|
+
REQUEST_CACHE.set(key, jsonPromise);
|
|
1583
|
+
}
|
|
1584
|
+
return Promise.resolve(jsonPromise);
|
|
1585
|
+
} catch (e) {
|
|
1586
|
+
return Promise.reject(e);
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
const DEFAULT_HEADERS = {
|
|
1590
|
+
Accept: 'application/json',
|
|
1591
|
+
'Content-Type': 'application/json'
|
|
1592
|
+
};
|
|
1593
|
+
const DEFAULT_REQUEST_CACHE = new Map();
|
|
1594
|
+
function getCacheSettings(localCache) {
|
|
1595
|
+
const canReadCache = localCache?.cacheControl?.includes('no-cache') ? false : true;
|
|
1596
|
+
const canStoreInCache = localCache?.cacheControl?.includes('no-store') ? false : true;
|
|
1597
|
+
const cache = localCache?.cache || DEFAULT_REQUEST_CACHE;
|
|
1598
|
+
return {
|
|
1599
|
+
cache,
|
|
1600
|
+
canReadCache,
|
|
1601
|
+
canStoreInCache
|
|
1602
|
+
};
|
|
1893
1603
|
}
|
|
1894
|
-
function
|
|
1895
|
-
|
|
1604
|
+
function createCacheKey(baseUrl, parameters, headers) {
|
|
1605
|
+
const parameterEntries = Object.entries(parameters).sort((_ref2, _ref3) => {
|
|
1606
|
+
let [a] = _ref2;
|
|
1607
|
+
let [b] = _ref3;
|
|
1608
|
+
return a > b ? 1 : -1;
|
|
1609
|
+
});
|
|
1610
|
+
const headerEntries = Object.entries(headers).sort((_ref4, _ref5) => {
|
|
1611
|
+
let [a] = _ref4;
|
|
1612
|
+
let [b] = _ref5;
|
|
1613
|
+
return a > b ? 1 : -1;
|
|
1614
|
+
});
|
|
1615
|
+
return JSON.stringify({
|
|
1616
|
+
baseUrl,
|
|
1617
|
+
parameters: parameterEntries,
|
|
1618
|
+
headers: headerEntries
|
|
1619
|
+
});
|
|
1896
1620
|
}
|
|
1897
|
-
|
|
1898
|
-
|
|
1621
|
+
/**
|
|
1622
|
+
* Appends query string parameters to a URL. Existing URL parameters are kept,
|
|
1623
|
+
* unless there is a conflict, in which case the new parameters override
|
|
1624
|
+
* those already in the URL.
|
|
1625
|
+
*/
|
|
1626
|
+
function createURLWithParameters(baseUrlString, parameters) {
|
|
1627
|
+
const baseUrl = new URL(baseUrlString);
|
|
1628
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
1629
|
+
if (isPureObject(value) || Array.isArray(value)) {
|
|
1630
|
+
baseUrl.searchParams.set(key, JSON.stringify(value));
|
|
1631
|
+
} else {
|
|
1632
|
+
baseUrl.searchParams.set(key, value.toString());
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
return baseUrl.toString();
|
|
1899
1636
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1637
|
+
/**
|
|
1638
|
+
* Deletes query string parameters from a URL.
|
|
1639
|
+
*/
|
|
1640
|
+
function excludeURLParameters(baseUrlString, parameters) {
|
|
1641
|
+
const baseUrl = new URL(baseUrlString);
|
|
1642
|
+
for (const param of parameters) {
|
|
1643
|
+
if (baseUrl.searchParams.has(param)) {
|
|
1644
|
+
baseUrl.searchParams.delete(param);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
return baseUrl.toString();
|
|
1902
1648
|
}
|
|
1903
1649
|
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
const bbox = [tile.bbox.west, tile.bbox.south, tile.bbox.east, tile.bbox.north];
|
|
1921
|
-
const bboxToGeom = bboxPolygon(bbox);
|
|
1922
|
-
const tileIsFullyVisible = booleanWithin(bboxToGeom, spatialFilter);
|
|
1923
|
-
// Clip the geometry to intersect with the tile
|
|
1924
|
-
const spatialFilterFeature = {
|
|
1925
|
-
type: 'Feature',
|
|
1926
|
-
geometry: spatialFilter,
|
|
1927
|
-
properties: {}
|
|
1650
|
+
// deck.gl
|
|
1651
|
+
// SPDX-License-Identifier: MIT
|
|
1652
|
+
// Copyright (c) vis.gl contributors
|
|
1653
|
+
const baseSource = function (endpoint, options, urlParameters) {
|
|
1654
|
+
try {
|
|
1655
|
+
const {
|
|
1656
|
+
accessToken,
|
|
1657
|
+
connectionName,
|
|
1658
|
+
cache,
|
|
1659
|
+
...optionalOptions
|
|
1660
|
+
} = options;
|
|
1661
|
+
const mergedOptions = {
|
|
1662
|
+
...SOURCE_DEFAULTS,
|
|
1663
|
+
accessToken,
|
|
1664
|
+
connectionName,
|
|
1665
|
+
endpoint
|
|
1928
1666
|
};
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1931
|
-
|
|
1667
|
+
for (const key in optionalOptions) {
|
|
1668
|
+
if (optionalOptions[key]) {
|
|
1669
|
+
mergedOptions[key] = optionalOptions[key];
|
|
1670
|
+
}
|
|
1932
1671
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1672
|
+
const baseUrl = buildSourceUrl(mergedOptions);
|
|
1673
|
+
const {
|
|
1674
|
+
clientId,
|
|
1675
|
+
maxLengthURL,
|
|
1676
|
+
format,
|
|
1677
|
+
localCache
|
|
1678
|
+
} = mergedOptions;
|
|
1679
|
+
const headers = {
|
|
1680
|
+
Authorization: `Bearer ${options.accessToken}`,
|
|
1681
|
+
...options.headers
|
|
1682
|
+
};
|
|
1683
|
+
const parameters = {
|
|
1684
|
+
client: clientId,
|
|
1685
|
+
...urlParameters
|
|
1686
|
+
};
|
|
1687
|
+
const errorContext = {
|
|
1688
|
+
requestType: 'Map instantiation',
|
|
1689
|
+
connection: options.connectionName,
|
|
1690
|
+
type: endpoint,
|
|
1691
|
+
source: JSON.stringify(parameters, undefined, 2)
|
|
1692
|
+
};
|
|
1693
|
+
return Promise.resolve(requestWithParameters({
|
|
1694
|
+
baseUrl,
|
|
1695
|
+
parameters,
|
|
1696
|
+
headers,
|
|
1697
|
+
errorContext,
|
|
1698
|
+
maxLengthURL,
|
|
1699
|
+
localCache
|
|
1700
|
+
})).then(function (mapInstantiation) {
|
|
1701
|
+
let _exit;
|
|
1702
|
+
function _temp2(_result) {
|
|
1703
|
+
return _exit ? _result : Promise.resolve(requestWithParameters({
|
|
1704
|
+
baseUrl: dataUrl,
|
|
1705
|
+
headers,
|
|
1706
|
+
errorContext,
|
|
1707
|
+
maxLengthURL,
|
|
1708
|
+
localCache
|
|
1709
|
+
}));
|
|
1710
|
+
}
|
|
1711
|
+
const dataUrl = mapInstantiation[format].url[0];
|
|
1712
|
+
if (cache) {
|
|
1713
|
+
cache.value = parseInt(new URL(dataUrl).searchParams.get('cache') || '', 10);
|
|
1714
|
+
}
|
|
1715
|
+
errorContext.requestType = 'Map data';
|
|
1716
|
+
const _temp = function () {
|
|
1717
|
+
if (format === 'tilejson') {
|
|
1718
|
+
return Promise.resolve(requestWithParameters({
|
|
1719
|
+
baseUrl: dataUrl,
|
|
1720
|
+
headers,
|
|
1721
|
+
errorContext,
|
|
1722
|
+
maxLengthURL,
|
|
1723
|
+
localCache
|
|
1724
|
+
})).then(function (json) {
|
|
1725
|
+
if (accessToken) {
|
|
1726
|
+
json.accessToken = accessToken;
|
|
1727
|
+
}
|
|
1728
|
+
_exit = 1;
|
|
1729
|
+
return json;
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
}();
|
|
1733
|
+
return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
|
|
1969
1734
|
});
|
|
1735
|
+
} catch (e) {
|
|
1736
|
+
return Promise.reject(e);
|
|
1970
1737
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1738
|
+
};
|
|
1739
|
+
const SOURCE_DEFAULTS = {
|
|
1740
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1741
|
+
clientId: getClient(),
|
|
1742
|
+
format: 'tilejson',
|
|
1743
|
+
headers: {},
|
|
1744
|
+
maxLengthURL: DEFAULT_MAX_LENGTH_URL
|
|
1745
|
+
};
|
|
1746
|
+
|
|
1747
|
+
// deck.gl
|
|
1748
|
+
// SPDX-License-Identifier: MIT
|
|
1749
|
+
// Copyright (c) vis.gl contributors
|
|
1750
|
+
const boundaryQuerySource = function (options) {
|
|
1751
|
+
try {
|
|
1752
|
+
const {
|
|
1753
|
+
columns,
|
|
1754
|
+
filters,
|
|
1755
|
+
tilesetTableName,
|
|
1756
|
+
propertiesSqlQuery,
|
|
1757
|
+
queryParameters
|
|
1758
|
+
} = options;
|
|
1759
|
+
const urlParameters = {
|
|
1760
|
+
tilesetTableName,
|
|
1761
|
+
propertiesSqlQuery
|
|
1762
|
+
};
|
|
1763
|
+
if (columns) {
|
|
1764
|
+
urlParameters.columns = columns.join(',');
|
|
1765
|
+
}
|
|
1766
|
+
if (filters) {
|
|
1767
|
+
urlParameters.filters = filters;
|
|
1768
|
+
}
|
|
1769
|
+
if (queryParameters) {
|
|
1770
|
+
urlParameters.queryParameters = queryParameters;
|
|
1771
|
+
}
|
|
1772
|
+
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
1773
|
+
} catch (e) {
|
|
1774
|
+
return Promise.reject(e);
|
|
1990
1775
|
}
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
// deck.gl
|
|
1779
|
+
// SPDX-License-Identifier: MIT
|
|
1780
|
+
// Copyright (c) vis.gl contributors
|
|
1781
|
+
const boundaryTableSource = function (options) {
|
|
1782
|
+
try {
|
|
1994
1783
|
const {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1784
|
+
filters,
|
|
1785
|
+
tilesetTableName,
|
|
1786
|
+
columns,
|
|
1787
|
+
propertiesTableName
|
|
1788
|
+
} = options;
|
|
1789
|
+
const urlParameters = {
|
|
1790
|
+
tilesetTableName,
|
|
1791
|
+
propertiesTableName
|
|
1792
|
+
};
|
|
1793
|
+
if (columns) {
|
|
1794
|
+
urlParameters.columns = columns.join(',');
|
|
1795
|
+
}
|
|
1796
|
+
if (filters) {
|
|
1797
|
+
urlParameters.filters = filters;
|
|
1798
|
+
}
|
|
1799
|
+
return Promise.resolve(baseSource('boundary', options, urlParameters));
|
|
1800
|
+
} catch (e) {
|
|
1801
|
+
return Promise.reject(e);
|
|
1999
1802
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
1803
|
+
};
|
|
1804
|
+
|
|
1805
|
+
const DEFAULT_TILE_SIZE = 512;
|
|
1806
|
+
const QUADBIN_ZOOM_MAX_OFFSET = 4;
|
|
1807
|
+
function getSpatialFiltersResolution(source, viewState) {
|
|
1808
|
+
const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
|
|
1809
|
+
const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
|
|
1810
|
+
const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
|
|
1811
|
+
const currentZoomInt = Math.ceil(viewState.zoom);
|
|
1812
|
+
if (source.spatialDataType === 'h3') {
|
|
1813
|
+
const tileSize = DEFAULT_TILE_SIZE;
|
|
1814
|
+
const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref => {
|
|
1815
|
+
let [zoom] = _ref;
|
|
1816
|
+
return zoom === currentZoomInt;
|
|
1817
|
+
})?.[1] ?? Math.max(0, currentZoomInt - 3);
|
|
1818
|
+
const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
|
|
1819
|
+
const hexagonResolution = _getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
|
|
1820
|
+
return Math.min(hexagonResolution, maxSpatialFiltersResolution);
|
|
2003
1821
|
}
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
1822
|
+
if (source.spatialDataType === 'quadbin') {
|
|
1823
|
+
const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
|
|
1824
|
+
const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
|
|
1825
|
+
const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
|
|
1826
|
+
return Math.min(quadsResolution, maxSpatialFiltersResolution);
|
|
2008
1827
|
}
|
|
2009
|
-
|
|
1828
|
+
return undefined;
|
|
2010
1829
|
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
data,
|
|
2030
|
-
startIndex,
|
|
2031
|
-
endIndex,
|
|
2032
|
-
type,
|
|
2033
|
-
bbox,
|
|
2034
|
-
tileFormat,
|
|
2035
|
-
uniqueIdProperty,
|
|
2036
|
-
storeGeometry,
|
|
2037
|
-
geometryIntersection
|
|
2038
|
-
});
|
|
2039
|
-
}
|
|
1830
|
+
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]];
|
|
1831
|
+
// stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
|
|
1832
|
+
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
|
|
1833
|
+
const BIAS = 2;
|
|
1834
|
+
/**
|
|
1835
|
+
* Resolution conversion function. Takes a WebMercatorViewport and returns
|
|
1836
|
+
* a H3 resolution such that the screen space size of the hexagons is
|
|
1837
|
+
* "similar" to the given tileSize on screen. Intended for use with deck.gl.
|
|
1838
|
+
* @internal
|
|
1839
|
+
*/
|
|
1840
|
+
function _getHexagonResolution(viewport, tileSize) {
|
|
1841
|
+
// Difference in given tile size compared to deck's internal 512px tile size,
|
|
1842
|
+
// expressed as an offset to the viewport zoom.
|
|
1843
|
+
const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
|
|
1844
|
+
const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
|
|
1845
|
+
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
|
|
1846
|
+
// Clip and bias
|
|
1847
|
+
return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
|
|
2040
1848
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
1849
|
+
|
|
1850
|
+
/**
|
|
1851
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
1852
|
+
*
|
|
1853
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
1854
|
+
*/
|
|
1855
|
+
class WidgetSource {
|
|
1856
|
+
constructor(props) {
|
|
1857
|
+
this.props = void 0;
|
|
1858
|
+
this.props = {
|
|
1859
|
+
...WidgetSource.defaultProps,
|
|
1860
|
+
...props
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
_getModelSource(filters, filterOwner) {
|
|
1864
|
+
const props = this.props;
|
|
1865
|
+
return {
|
|
1866
|
+
apiVersion: props.apiVersion,
|
|
1867
|
+
apiBaseUrl: props.apiBaseUrl,
|
|
1868
|
+
clientId: props.clientId,
|
|
1869
|
+
accessToken: props.accessToken,
|
|
1870
|
+
connectionName: props.connectionName,
|
|
1871
|
+
filters: getApplicableFilters(filterOwner, filters || props.filters),
|
|
1872
|
+
filtersLogicalOperator: props.filtersLogicalOperator,
|
|
1873
|
+
spatialDataType: props.spatialDataType,
|
|
1874
|
+
spatialDataColumn: props.spatialDataColumn,
|
|
1875
|
+
dataResolution: props.dataResolution
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
_getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
|
|
1879
|
+
// spatialFiltersResolution applies only to spatial index sources.
|
|
1880
|
+
if (!spatialFilter || source.spatialDataType === 'geo') {
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
if (!referenceViewState) {
|
|
1884
|
+
throw new Error('Missing required option, "spatialIndexReferenceViewState".');
|
|
1885
|
+
}
|
|
1886
|
+
return getSpatialFiltersResolution(source, referenceViewState);
|
|
2056
1887
|
}
|
|
2057
|
-
return indices.value;
|
|
2058
|
-
}
|
|
2059
|
-
function getFeatureId(data, startIndex) {
|
|
2060
|
-
return data.featureIds.value[startIndex];
|
|
2061
1888
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
1889
|
+
WidgetSource.defaultProps = {
|
|
1890
|
+
apiVersion: exports.ApiVersion.V3,
|
|
1891
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1892
|
+
clientId: getClient(),
|
|
1893
|
+
filters: {},
|
|
1894
|
+
filtersLogicalOperator: 'and'
|
|
1895
|
+
};
|
|
1896
|
+
|
|
1897
|
+
/**
|
|
1898
|
+
* Return more descriptive error from API
|
|
1899
|
+
* @privateRemarks Source: @carto/react-api
|
|
1900
|
+
*/
|
|
1901
|
+
|
|
1902
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
1903
|
+
|
|
1904
|
+
function _catch(body, recover) {
|
|
1905
|
+
try {
|
|
1906
|
+
var result = body();
|
|
1907
|
+
} catch (e) {
|
|
1908
|
+
return recover(e);
|
|
1909
|
+
}
|
|
1910
|
+
if (result && result.then) {
|
|
1911
|
+
return result.then(void 0, recover);
|
|
2076
1912
|
}
|
|
2077
1913
|
return result;
|
|
2078
1914
|
}
|
|
2079
|
-
function
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
1915
|
+
const makeCall = function (_ref2) {
|
|
1916
|
+
let {
|
|
1917
|
+
url,
|
|
1918
|
+
accessToken,
|
|
1919
|
+
opts
|
|
1920
|
+
} = _ref2;
|
|
1921
|
+
try {
|
|
1922
|
+
let _exit;
|
|
1923
|
+
function _temp2(_result) {
|
|
1924
|
+
if (_exit) ;
|
|
1925
|
+
if (!response.ok) {
|
|
1926
|
+
dealWithApiError({
|
|
1927
|
+
response,
|
|
1928
|
+
data
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
return data;
|
|
1932
|
+
}
|
|
1933
|
+
let response;
|
|
1934
|
+
let data;
|
|
1935
|
+
const isPost = opts?.method === 'POST';
|
|
1936
|
+
const _temp = _catch(function () {
|
|
1937
|
+
return Promise.resolve(fetch(url.toString(), {
|
|
1938
|
+
headers: {
|
|
1939
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1940
|
+
...(isPost && {
|
|
1941
|
+
'Content-Type': 'application/json'
|
|
1942
|
+
}),
|
|
1943
|
+
...opts.headers
|
|
1944
|
+
},
|
|
1945
|
+
...(isPost && {
|
|
1946
|
+
method: opts?.method,
|
|
1947
|
+
body: opts?.body
|
|
1948
|
+
}),
|
|
1949
|
+
signal: opts?.abortController?.signal,
|
|
1950
|
+
...opts?.otherOptions
|
|
1951
|
+
})).then(function (_fetch) {
|
|
1952
|
+
response = _fetch;
|
|
1953
|
+
return Promise.resolve(response.json()).then(function (_response$json) {
|
|
1954
|
+
data = _response$json;
|
|
1955
|
+
});
|
|
1956
|
+
});
|
|
1957
|
+
}, function (error) {
|
|
1958
|
+
if (error.name === 'AbortError') throw error;
|
|
1959
|
+
throw new Error(`Failed request: ${error}`);
|
|
1960
|
+
});
|
|
1961
|
+
return Promise.resolve(_temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp));
|
|
1962
|
+
} catch (e) {
|
|
1963
|
+
return Promise.reject(e);
|
|
2089
1964
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
1965
|
+
};
|
|
1966
|
+
function dealWithApiError(_ref) {
|
|
1967
|
+
let {
|
|
1968
|
+
response,
|
|
1969
|
+
data
|
|
1970
|
+
} = _ref;
|
|
1971
|
+
if (data.error === 'Column not found') {
|
|
1972
|
+
throw new InvalidColumnError(`${data.error} ${data.column_name}`);
|
|
2092
1973
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
}
|
|
2096
|
-
function getValueFromTileProps(tileProps, propertyName) {
|
|
2097
|
-
const {
|
|
2098
|
-
properties,
|
|
2099
|
-
numericProps
|
|
2100
|
-
} = tileProps;
|
|
2101
|
-
return numericProps[propertyName] || properties[propertyName];
|
|
2102
|
-
}
|
|
2103
|
-
function getFeatureByType(coordinates, type) {
|
|
2104
|
-
switch (type) {
|
|
2105
|
-
case 'Polygon':
|
|
2106
|
-
return {
|
|
2107
|
-
type: 'Polygon',
|
|
2108
|
-
coordinates: [coordinates]
|
|
2109
|
-
};
|
|
2110
|
-
case 'LineString':
|
|
2111
|
-
return {
|
|
2112
|
-
type: 'LineString',
|
|
2113
|
-
coordinates
|
|
2114
|
-
};
|
|
2115
|
-
case 'Point':
|
|
2116
|
-
return {
|
|
2117
|
-
type: 'Point',
|
|
2118
|
-
coordinates: coordinates[0]
|
|
2119
|
-
};
|
|
2120
|
-
default:
|
|
2121
|
-
throw new Error('Invalid geometry type');
|
|
1974
|
+
if (typeof data.error === 'string' && data.error?.includes('Missing columns')) {
|
|
1975
|
+
throw new InvalidColumnError(data.error);
|
|
2122
1976
|
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
1977
|
+
switch (response.status) {
|
|
1978
|
+
case 401:
|
|
1979
|
+
throw new Error('Unauthorized access. Invalid credentials');
|
|
1980
|
+
case 403:
|
|
1981
|
+
throw new Error('Forbidden access to the requested data');
|
|
1982
|
+
default:
|
|
1983
|
+
throw new Error(data && data.error && typeof data.error === 'string' ? data.error : JSON.stringify(data?.hint || data.error?.[0]));
|
|
2128
1984
|
}
|
|
2129
|
-
return ringCoordinates;
|
|
2130
1985
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
1986
|
+
|
|
1987
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
1988
|
+
const AVAILABLE_MODELS = ['category', 'histogram', 'formula', 'pick', 'timeseries', 'range', 'scatterplot', 'table'];
|
|
1989
|
+
const {
|
|
1990
|
+
V3
|
|
1991
|
+
} = exports.ApiVersion;
|
|
1992
|
+
const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
1993
|
+
/**
|
|
1994
|
+
* Execute a SQL model request.
|
|
1995
|
+
* @privateRemarks Source: @carto/react-api
|
|
1996
|
+
*/
|
|
1997
|
+
function executeModel(props) {
|
|
1998
|
+
assert(props.source, 'executeModel: missing source');
|
|
1999
|
+
assert(props.model, 'executeModel: missing model');
|
|
2000
|
+
assert(props.params, 'executeModel: missing params');
|
|
2001
|
+
assert(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
|
|
2002
|
+
const {
|
|
2003
|
+
model,
|
|
2004
|
+
source,
|
|
2005
|
+
params,
|
|
2006
|
+
opts
|
|
2007
|
+
} = props;
|
|
2008
|
+
const {
|
|
2009
|
+
type,
|
|
2010
|
+
apiVersion,
|
|
2011
|
+
apiBaseUrl,
|
|
2012
|
+
accessToken,
|
|
2013
|
+
connectionName,
|
|
2014
|
+
clientId
|
|
2015
|
+
} = source;
|
|
2016
|
+
assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
|
|
2017
|
+
assert(accessToken, 'executeModel: missing accessToken');
|
|
2018
|
+
assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
|
|
2019
|
+
assert(type !== 'tileset', 'executeModel: Tilesets not supported');
|
|
2020
|
+
let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
|
|
2021
|
+
const {
|
|
2136
2022
|
data,
|
|
2023
|
+
filters,
|
|
2024
|
+
filtersLogicalOperator = 'and',
|
|
2025
|
+
spatialDataType = 'geo',
|
|
2026
|
+
spatialFiltersMode = 'intersects',
|
|
2027
|
+
spatialFiltersResolution = 0
|
|
2028
|
+
} = source;
|
|
2029
|
+
const queryParams = {
|
|
2137
2030
|
type,
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2031
|
+
client: clientId,
|
|
2032
|
+
source: data,
|
|
2033
|
+
params,
|
|
2034
|
+
queryParameters: source.queryParameters || '',
|
|
2035
|
+
filters,
|
|
2036
|
+
filtersLogicalOperator
|
|
2037
|
+
};
|
|
2038
|
+
const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
|
|
2039
|
+
// Picking Model API requires 'spatialDataColumn'.
|
|
2040
|
+
if (model === 'pick') {
|
|
2041
|
+
queryParams.spatialDataColumn = spatialDataColumn;
|
|
2145
2042
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
options
|
|
2155
|
-
});
|
|
2156
|
-
} else {
|
|
2157
|
-
addIntersectedFeaturesInTile({
|
|
2158
|
-
map,
|
|
2159
|
-
data,
|
|
2160
|
-
geometryIntersection,
|
|
2161
|
-
type,
|
|
2162
|
-
bbox,
|
|
2163
|
-
tileFormat,
|
|
2164
|
-
uniqueIdProperty,
|
|
2165
|
-
options
|
|
2166
|
-
});
|
|
2043
|
+
// API supports multiple filters, we apply it only to spatialDataColumn
|
|
2044
|
+
const spatialFilters = source.spatialFilter ? {
|
|
2045
|
+
[spatialDataColumn]: source.spatialFilter
|
|
2046
|
+
} : undefined;
|
|
2047
|
+
if (spatialFilters) {
|
|
2048
|
+
queryParams.spatialFilters = spatialFilters;
|
|
2049
|
+
queryParams.spatialDataColumn = spatialDataColumn;
|
|
2050
|
+
queryParams.spatialDataType = spatialDataType;
|
|
2167
2051
|
}
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
type,
|
|
2174
|
-
bbox,
|
|
2175
|
-
tileFormat,
|
|
2176
|
-
uniqueIdProperty,
|
|
2177
|
-
options
|
|
2178
|
-
} = _ref5;
|
|
2179
|
-
const indices = getIndices(data);
|
|
2180
|
-
const storeGeometry = options?.storeGeometry || false;
|
|
2181
|
-
for (let i = 0; i < indices.length - 1; i++) {
|
|
2182
|
-
const startIndex = indices[i];
|
|
2183
|
-
const endIndex = indices[i + 1];
|
|
2184
|
-
processTileFeatureProperties({
|
|
2185
|
-
map,
|
|
2186
|
-
data,
|
|
2187
|
-
startIndex,
|
|
2188
|
-
endIndex,
|
|
2189
|
-
type,
|
|
2190
|
-
bbox,
|
|
2191
|
-
tileFormat,
|
|
2192
|
-
uniqueIdProperty,
|
|
2193
|
-
storeGeometry
|
|
2194
|
-
});
|
|
2052
|
+
if (spatialDataType !== 'geo') {
|
|
2053
|
+
if (spatialFiltersResolution > 0) {
|
|
2054
|
+
queryParams.spatialFiltersResolution = spatialFiltersResolution;
|
|
2055
|
+
}
|
|
2056
|
+
queryParams.spatialFiltersMode = spatialFiltersMode;
|
|
2195
2057
|
}
|
|
2058
|
+
const urlWithSearchParams = url + '?' + objectToURLSearchParams(queryParams).toString();
|
|
2059
|
+
const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
|
|
2060
|
+
if (isGet) {
|
|
2061
|
+
url = urlWithSearchParams;
|
|
2062
|
+
}
|
|
2063
|
+
return makeCall({
|
|
2064
|
+
url,
|
|
2065
|
+
accessToken: source.accessToken,
|
|
2066
|
+
opts: {
|
|
2067
|
+
...opts,
|
|
2068
|
+
method: isGet ? 'GET' : 'POST',
|
|
2069
|
+
...(!isGet && {
|
|
2070
|
+
body: JSON.stringify(queryParams)
|
|
2071
|
+
})
|
|
2072
|
+
}
|
|
2073
|
+
});
|
|
2196
2074
|
}
|
|
2197
|
-
function
|
|
2198
|
-
const
|
|
2199
|
-
const
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2075
|
+
function objectToURLSearchParams(object) {
|
|
2076
|
+
const params = new URLSearchParams();
|
|
2077
|
+
for (const key in object) {
|
|
2078
|
+
if (isPureObject(object[key])) {
|
|
2079
|
+
params.append(key, JSON.stringify(object[key]));
|
|
2080
|
+
} else if (Array.isArray(object[key])) {
|
|
2081
|
+
params.append(key, JSON.stringify(object[key]));
|
|
2082
|
+
} else if (object[key] === null) {
|
|
2083
|
+
params.append(key, 'null');
|
|
2084
|
+
} else if (object[key] !== undefined) {
|
|
2085
|
+
params.append(key, String(object[key]));
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
return params;
|
|
2209
2089
|
}
|
|
2210
2090
|
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
const resolution = getResolution(tiles, spatialIndex);
|
|
2221
|
-
const spatialIndexIDName = spatialDataColumn ? spatialDataColumn : spatialIndex;
|
|
2222
|
-
if (!resolution) {
|
|
2223
|
-
return [];
|
|
2091
|
+
/**
|
|
2092
|
+
* Source for Widget API requests.
|
|
2093
|
+
*
|
|
2094
|
+
* Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
|
|
2095
|
+
*/
|
|
2096
|
+
class WidgetRemoteSource extends WidgetSource {
|
|
2097
|
+
constructor() {
|
|
2098
|
+
super(...arguments);
|
|
2099
|
+
this._headers = {};
|
|
2224
2100
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2101
|
+
/** Assigns HTTP headers to be included on API requests from this source. */
|
|
2102
|
+
setRequestHeaders(headers) {
|
|
2103
|
+
this._headers = headers;
|
|
2228
2104
|
}
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2105
|
+
getCategories(options) {
|
|
2106
|
+
try {
|
|
2107
|
+
const _this = this;
|
|
2108
|
+
const {
|
|
2109
|
+
filters = _this.props.filters,
|
|
2110
|
+
filterOwner,
|
|
2111
|
+
spatialFilter,
|
|
2112
|
+
spatialFiltersMode,
|
|
2113
|
+
spatialIndexReferenceViewState,
|
|
2114
|
+
abortController,
|
|
2115
|
+
...params
|
|
2116
|
+
} = options;
|
|
2117
|
+
const {
|
|
2118
|
+
column,
|
|
2119
|
+
operation,
|
|
2120
|
+
operationColumn
|
|
2121
|
+
} = params;
|
|
2122
|
+
const source = _this.getModelSource(filters, filterOwner);
|
|
2123
|
+
const spatialFiltersResolution = _this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2124
|
+
return Promise.resolve(executeModel({
|
|
2125
|
+
model: 'category',
|
|
2126
|
+
source: {
|
|
2127
|
+
...source,
|
|
2128
|
+
spatialFiltersResolution,
|
|
2129
|
+
spatialFiltersMode,
|
|
2130
|
+
spatialFilter
|
|
2131
|
+
},
|
|
2132
|
+
params: {
|
|
2133
|
+
column,
|
|
2134
|
+
operation,
|
|
2135
|
+
operationColumn: operationColumn || column
|
|
2136
|
+
},
|
|
2137
|
+
opts: {
|
|
2138
|
+
abortController,
|
|
2139
|
+
headers: _this._headers
|
|
2140
|
+
}
|
|
2141
|
+
}).then(res => normalizeObjectKeys(res.rows)));
|
|
2142
|
+
} catch (e) {
|
|
2143
|
+
return Promise.reject(e);
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
getFeatures(options) {
|
|
2147
|
+
try {
|
|
2148
|
+
const _this2 = this;
|
|
2149
|
+
const {
|
|
2150
|
+
filters = _this2.props.filters,
|
|
2151
|
+
filterOwner,
|
|
2152
|
+
spatialFilter,
|
|
2153
|
+
spatialFiltersMode,
|
|
2154
|
+
spatialIndexReferenceViewState,
|
|
2155
|
+
abortController,
|
|
2156
|
+
...params
|
|
2157
|
+
} = options;
|
|
2158
|
+
const {
|
|
2159
|
+
columns,
|
|
2160
|
+
dataType,
|
|
2161
|
+
featureIds,
|
|
2162
|
+
z,
|
|
2163
|
+
limit,
|
|
2164
|
+
tileResolution
|
|
2165
|
+
} = params;
|
|
2166
|
+
const source = _this2.getModelSource(filters, filterOwner);
|
|
2167
|
+
const spatialFiltersResolution = _this2._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2168
|
+
return Promise.resolve(executeModel({
|
|
2169
|
+
model: 'pick',
|
|
2170
|
+
source: {
|
|
2171
|
+
...source,
|
|
2172
|
+
spatialFiltersResolution,
|
|
2173
|
+
spatialFiltersMode,
|
|
2174
|
+
spatialFilter
|
|
2175
|
+
},
|
|
2176
|
+
params: {
|
|
2177
|
+
columns,
|
|
2178
|
+
dataType,
|
|
2179
|
+
featureIds,
|
|
2180
|
+
z,
|
|
2181
|
+
limit: limit || 1000,
|
|
2182
|
+
tileResolution: tileResolution || DEFAULT_TILE_RESOLUTION
|
|
2183
|
+
},
|
|
2184
|
+
opts: {
|
|
2185
|
+
abortController,
|
|
2186
|
+
headers: _this2._headers
|
|
2187
|
+
}
|
|
2188
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
2189
|
+
}).then(_ref => {
|
|
2190
|
+
let {
|
|
2191
|
+
rows
|
|
2192
|
+
} = _ref;
|
|
2193
|
+
return {
|
|
2194
|
+
rows
|
|
2195
|
+
};
|
|
2196
|
+
}));
|
|
2197
|
+
} catch (e) {
|
|
2198
|
+
return Promise.reject(e);
|
|
2234
2199
|
}
|
|
2235
|
-
tile.data.forEach(d => {
|
|
2236
|
-
if (cellsSet.has(d.id)) {
|
|
2237
|
-
map.set(d.id, {
|
|
2238
|
-
...d.properties,
|
|
2239
|
-
[spatialIndexIDName]: d.id
|
|
2240
|
-
});
|
|
2241
|
-
}
|
|
2242
|
-
});
|
|
2243
|
-
}
|
|
2244
|
-
return Array.from(map.values());
|
|
2245
|
-
}
|
|
2246
|
-
function getResolution(tiles, spatialIndex) {
|
|
2247
|
-
const data = tiles.find(tile => tile.data?.length)?.data;
|
|
2248
|
-
if (!data) {
|
|
2249
|
-
return;
|
|
2250
2200
|
}
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2201
|
+
getFormula(options) {
|
|
2202
|
+
try {
|
|
2203
|
+
const _this3 = this;
|
|
2204
|
+
const {
|
|
2205
|
+
filters = _this3.props.filters,
|
|
2206
|
+
filterOwner,
|
|
2207
|
+
spatialFilter,
|
|
2208
|
+
spatialFiltersMode,
|
|
2209
|
+
spatialIndexReferenceViewState,
|
|
2210
|
+
abortController,
|
|
2211
|
+
operationExp,
|
|
2212
|
+
...params
|
|
2213
|
+
} = options;
|
|
2214
|
+
const {
|
|
2215
|
+
column,
|
|
2216
|
+
operation
|
|
2217
|
+
} = params;
|
|
2218
|
+
const source = _this3.getModelSource(filters, filterOwner);
|
|
2219
|
+
const spatialFiltersResolution = _this3._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2220
|
+
return Promise.resolve(executeModel({
|
|
2221
|
+
model: 'formula',
|
|
2222
|
+
source: {
|
|
2223
|
+
...source,
|
|
2224
|
+
spatialFiltersResolution,
|
|
2225
|
+
spatialFiltersMode,
|
|
2226
|
+
spatialFilter
|
|
2227
|
+
},
|
|
2228
|
+
params: {
|
|
2229
|
+
column: column ?? '*',
|
|
2230
|
+
operation: operation ?? 'count',
|
|
2231
|
+
operationExp
|
|
2232
|
+
},
|
|
2233
|
+
opts: {
|
|
2234
|
+
abortController,
|
|
2235
|
+
headers: _this3._headers
|
|
2236
|
+
}
|
|
2237
|
+
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
2238
|
+
} catch (e) {
|
|
2239
|
+
return Promise.reject(e);
|
|
2240
|
+
}
|
|
2256
2241
|
}
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
const
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2242
|
+
getHistogram(options) {
|
|
2243
|
+
try {
|
|
2244
|
+
const _this4 = this;
|
|
2245
|
+
const {
|
|
2246
|
+
filters = _this4.props.filters,
|
|
2247
|
+
filterOwner,
|
|
2248
|
+
spatialFilter,
|
|
2249
|
+
spatialFiltersMode,
|
|
2250
|
+
spatialIndexReferenceViewState,
|
|
2251
|
+
abortController,
|
|
2252
|
+
...params
|
|
2253
|
+
} = options;
|
|
2254
|
+
const {
|
|
2255
|
+
column,
|
|
2256
|
+
operation,
|
|
2257
|
+
ticks
|
|
2258
|
+
} = params;
|
|
2259
|
+
const source = _this4.getModelSource(filters, filterOwner);
|
|
2260
|
+
const spatialFiltersResolution = _this4._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2261
|
+
return Promise.resolve(executeModel({
|
|
2262
|
+
model: 'histogram',
|
|
2263
|
+
source: {
|
|
2264
|
+
...source,
|
|
2265
|
+
spatialFiltersResolution,
|
|
2266
|
+
spatialFiltersMode,
|
|
2267
|
+
spatialFilter
|
|
2268
|
+
},
|
|
2269
|
+
params: {
|
|
2270
|
+
column,
|
|
2271
|
+
operation,
|
|
2272
|
+
ticks
|
|
2273
|
+
},
|
|
2274
|
+
opts: {
|
|
2275
|
+
abortController,
|
|
2276
|
+
headers: _this4._headers
|
|
2277
|
+
}
|
|
2278
|
+
}).then(res => normalizeObjectKeys(res.rows))).then(function (data) {
|
|
2279
|
+
if (data.length) {
|
|
2280
|
+
// Given N ticks the API returns up to N+1 bins, omitting any empty bins. Bins
|
|
2281
|
+
// include 1 bin below the lowest tick, N-1 between ticks, and 1 bin above the highest tick.
|
|
2282
|
+
const result = Array(ticks.length + 1).fill(0);
|
|
2283
|
+
data.forEach(_ref2 => {
|
|
2284
|
+
let {
|
|
2285
|
+
tick,
|
|
2286
|
+
value
|
|
2287
|
+
} = _ref2;
|
|
2288
|
+
return result[tick] = value;
|
|
2289
|
+
});
|
|
2290
|
+
return result;
|
|
2291
|
+
}
|
|
2292
|
+
return [];
|
|
2293
|
+
});
|
|
2294
|
+
} catch (e) {
|
|
2295
|
+
return Promise.reject(e);
|
|
2296
|
+
}
|
|
2264
2297
|
}
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2298
|
+
getRange(options) {
|
|
2299
|
+
try {
|
|
2300
|
+
const _this5 = this;
|
|
2301
|
+
const {
|
|
2302
|
+
filters = _this5.props.filters,
|
|
2303
|
+
filterOwner,
|
|
2304
|
+
spatialFilter,
|
|
2305
|
+
spatialFiltersMode,
|
|
2306
|
+
spatialIndexReferenceViewState,
|
|
2307
|
+
abortController,
|
|
2308
|
+
...params
|
|
2309
|
+
} = options;
|
|
2310
|
+
const {
|
|
2311
|
+
column
|
|
2312
|
+
} = params;
|
|
2313
|
+
const source = _this5.getModelSource(filters, filterOwner);
|
|
2314
|
+
const spatialFiltersResolution = _this5._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2315
|
+
return Promise.resolve(executeModel({
|
|
2316
|
+
model: 'range',
|
|
2317
|
+
source: {
|
|
2318
|
+
...source,
|
|
2319
|
+
spatialFiltersResolution,
|
|
2320
|
+
spatialFiltersMode,
|
|
2321
|
+
spatialFilter
|
|
2322
|
+
},
|
|
2323
|
+
params: {
|
|
2324
|
+
column
|
|
2325
|
+
},
|
|
2326
|
+
opts: {
|
|
2327
|
+
abortController,
|
|
2328
|
+
headers: _this5._headers
|
|
2329
|
+
}
|
|
2330
|
+
}).then(res => normalizeObjectKeys(res.rows[0])));
|
|
2331
|
+
} catch (e) {
|
|
2332
|
+
return Promise.reject(e);
|
|
2333
|
+
}
|
|
2270
2334
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2335
|
+
getScatter(options) {
|
|
2336
|
+
try {
|
|
2337
|
+
const _this6 = this;
|
|
2338
|
+
const {
|
|
2339
|
+
filters = _this6.props.filters,
|
|
2340
|
+
filterOwner,
|
|
2341
|
+
spatialFilter,
|
|
2342
|
+
spatialFiltersMode,
|
|
2343
|
+
spatialIndexReferenceViewState,
|
|
2344
|
+
abortController,
|
|
2345
|
+
...params
|
|
2346
|
+
} = options;
|
|
2347
|
+
const {
|
|
2348
|
+
xAxisColumn,
|
|
2349
|
+
xAxisJoinOperation,
|
|
2350
|
+
yAxisColumn,
|
|
2351
|
+
yAxisJoinOperation
|
|
2352
|
+
} = params;
|
|
2353
|
+
const source = _this6.getModelSource(filters, filterOwner);
|
|
2354
|
+
const spatialFiltersResolution = _this6._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2355
|
+
// Make sure this is sync with the same constant in cloud-native/maps-api
|
|
2356
|
+
const HARD_LIMIT = 500;
|
|
2357
|
+
return Promise.resolve(executeModel({
|
|
2358
|
+
model: 'scatterplot',
|
|
2359
|
+
source: {
|
|
2360
|
+
...source,
|
|
2361
|
+
spatialFiltersResolution,
|
|
2362
|
+
spatialFiltersMode,
|
|
2363
|
+
spatialFilter
|
|
2364
|
+
},
|
|
2365
|
+
params: {
|
|
2366
|
+
xAxisColumn,
|
|
2367
|
+
xAxisJoinOperation,
|
|
2368
|
+
yAxisColumn,
|
|
2369
|
+
yAxisJoinOperation,
|
|
2370
|
+
limit: HARD_LIMIT
|
|
2371
|
+
},
|
|
2372
|
+
opts: {
|
|
2373
|
+
abortController,
|
|
2374
|
+
headers: _this6._headers
|
|
2375
|
+
}
|
|
2376
|
+
}).then(res => normalizeObjectKeys(res.rows)).then(res => res.map(_ref3 => {
|
|
2377
|
+
let {
|
|
2378
|
+
x,
|
|
2379
|
+
y
|
|
2380
|
+
} = _ref3;
|
|
2381
|
+
return [x, y];
|
|
2382
|
+
})));
|
|
2383
|
+
} catch (e) {
|
|
2384
|
+
return Promise.reject(e);
|
|
2385
|
+
}
|
|
2280
2386
|
}
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2387
|
+
getTable(options) {
|
|
2388
|
+
try {
|
|
2389
|
+
const _this7 = this;
|
|
2390
|
+
const {
|
|
2391
|
+
filters = _this7.props.filters,
|
|
2392
|
+
filterOwner,
|
|
2393
|
+
spatialFilter,
|
|
2394
|
+
spatialFiltersMode,
|
|
2395
|
+
spatialIndexReferenceViewState,
|
|
2396
|
+
abortController,
|
|
2397
|
+
...params
|
|
2398
|
+
} = options;
|
|
2399
|
+
const {
|
|
2400
|
+
columns,
|
|
2401
|
+
sortBy,
|
|
2402
|
+
sortDirection,
|
|
2403
|
+
offset = 0,
|
|
2404
|
+
limit = 10
|
|
2405
|
+
} = params;
|
|
2406
|
+
const source = _this7.getModelSource(filters, filterOwner);
|
|
2407
|
+
const spatialFiltersResolution = _this7._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2408
|
+
return Promise.resolve(executeModel({
|
|
2409
|
+
model: 'table',
|
|
2410
|
+
source: {
|
|
2411
|
+
...source,
|
|
2412
|
+
spatialFiltersResolution,
|
|
2413
|
+
spatialFiltersMode,
|
|
2414
|
+
spatialFilter
|
|
2415
|
+
},
|
|
2416
|
+
params: {
|
|
2417
|
+
column: columns,
|
|
2418
|
+
sortBy,
|
|
2419
|
+
sortDirection,
|
|
2420
|
+
limit,
|
|
2421
|
+
offset
|
|
2422
|
+
},
|
|
2423
|
+
opts: {
|
|
2424
|
+
abortController,
|
|
2425
|
+
headers: _this7._headers
|
|
2426
|
+
}
|
|
2427
|
+
}).then(res => ({
|
|
2428
|
+
// Avoid `normalizeObjectKeys()`, which changes column names.
|
|
2429
|
+
rows: res.rows ?? res.ROWS,
|
|
2430
|
+
totalCount: res.metadata?.total ?? res.METADATA?.TOTAL
|
|
2431
|
+
})));
|
|
2432
|
+
} catch (e) {
|
|
2433
|
+
return Promise.reject(e);
|
|
2434
|
+
}
|
|
2292
2435
|
}
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2436
|
+
getTimeSeries(options) {
|
|
2437
|
+
try {
|
|
2438
|
+
const _this8 = this;
|
|
2439
|
+
const {
|
|
2440
|
+
filters = _this8.props.filters,
|
|
2441
|
+
filterOwner,
|
|
2442
|
+
abortController,
|
|
2443
|
+
spatialFilter,
|
|
2444
|
+
spatialFiltersMode,
|
|
2445
|
+
spatialIndexReferenceViewState,
|
|
2446
|
+
...params
|
|
2447
|
+
} = options;
|
|
2448
|
+
const {
|
|
2449
|
+
column,
|
|
2450
|
+
operationColumn,
|
|
2451
|
+
joinOperation,
|
|
2452
|
+
operation,
|
|
2453
|
+
stepSize,
|
|
2454
|
+
stepMultiplier,
|
|
2455
|
+
splitByCategory,
|
|
2456
|
+
splitByCategoryLimit,
|
|
2457
|
+
splitByCategoryValues
|
|
2458
|
+
} = params;
|
|
2459
|
+
const source = _this8.getModelSource(filters, filterOwner);
|
|
2460
|
+
const spatialFiltersResolution = _this8._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
|
|
2461
|
+
return Promise.resolve(executeModel({
|
|
2462
|
+
model: 'timeseries',
|
|
2463
|
+
source: {
|
|
2464
|
+
...source,
|
|
2465
|
+
spatialFiltersResolution,
|
|
2466
|
+
spatialFiltersMode,
|
|
2467
|
+
spatialFilter
|
|
2468
|
+
},
|
|
2469
|
+
params: {
|
|
2470
|
+
column,
|
|
2471
|
+
stepSize,
|
|
2472
|
+
stepMultiplier,
|
|
2473
|
+
operationColumn: operationColumn || column,
|
|
2474
|
+
joinOperation,
|
|
2475
|
+
operation,
|
|
2476
|
+
splitByCategory,
|
|
2477
|
+
splitByCategoryLimit,
|
|
2478
|
+
splitByCategoryValues
|
|
2479
|
+
},
|
|
2480
|
+
opts: {
|
|
2481
|
+
abortController,
|
|
2482
|
+
headers: _this8._headers
|
|
2321
2483
|
}
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
}
|
|
2484
|
+
}).then(res => ({
|
|
2485
|
+
rows: normalizeObjectKeys(res.rows),
|
|
2486
|
+
categories: res.metadata?.categories
|
|
2487
|
+
})));
|
|
2488
|
+
} catch (e) {
|
|
2489
|
+
return Promise.reject(e);
|
|
2326
2490
|
}
|
|
2327
2491
|
}
|
|
2328
|
-
return Array.from(data.values());
|
|
2329
|
-
}
|
|
2330
|
-
/**
|
|
2331
|
-
* Detects whether a given {@link Tile} is a {@link RasterTile}.
|
|
2332
|
-
* @privateRemarks Method of detection is arbitrary, and may be changed.
|
|
2333
|
-
*/
|
|
2334
|
-
function isRasterTile(tile) {
|
|
2335
|
-
return tile.data ? tile.data.hasOwnProperty('cells') : false;
|
|
2336
|
-
}
|
|
2337
|
-
function isRasterTileVisible(tile) {
|
|
2338
|
-
return !!(tile.isVisible && tile.data?.cells?.numericProps);
|
|
2339
2492
|
}
|
|
2493
|
+
|
|
2340
2494
|
/**
|
|
2341
|
-
*
|
|
2342
|
-
*
|
|
2343
|
-
*
|
|
2495
|
+
* Source for Widget API requests on a data source defined by a SQL query.
|
|
2496
|
+
*
|
|
2497
|
+
* Generally not intended to be constructed directly. Instead, call
|
|
2498
|
+
* {@link vectorQuerySource}, {@link h3QuerySource}, or {@link quadbinQuerySource},
|
|
2499
|
+
* which can be shared with map layers. Sources contain a `widgetSource` property,
|
|
2500
|
+
* for use by widget implementations.
|
|
2501
|
+
*
|
|
2502
|
+
* Example:
|
|
2503
|
+
*
|
|
2504
|
+
* ```javascript
|
|
2505
|
+
* import { vectorQuerySource } from '@carto/api-client';
|
|
2506
|
+
*
|
|
2507
|
+
* const data = vectorQuerySource({
|
|
2508
|
+
* accessToken: '••••',
|
|
2509
|
+
* connectionName: 'carto_dw',
|
|
2510
|
+
* sqlQuery: 'SELECT * FROM carto-demo-data.demo_tables.retail_stores'
|
|
2511
|
+
* });
|
|
2512
|
+
*
|
|
2513
|
+
* const { widgetSource } = await data;
|
|
2514
|
+
* ```
|
|
2344
2515
|
*/
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
/** @internalRemarks Source: @carto/react-core */
|
|
2357
|
-
function tileFeatures(_ref) {
|
|
2358
|
-
let {
|
|
2359
|
-
tiles,
|
|
2360
|
-
spatialFilter,
|
|
2361
|
-
uniqueIdProperty,
|
|
2362
|
-
tileFormat,
|
|
2363
|
-
spatialDataColumn = DEFAULT_GEO_COLUMN,
|
|
2364
|
-
spatialDataType,
|
|
2365
|
-
rasterMetadata,
|
|
2366
|
-
options = {}
|
|
2367
|
-
} = _ref;
|
|
2368
|
-
// TODO(cleanup): Is an empty response the expected result when spatialFilter
|
|
2369
|
-
// is omitted? Why not make the parameter required, or return the full input?
|
|
2370
|
-
if (!spatialFilter) {
|
|
2371
|
-
return [];
|
|
2372
|
-
}
|
|
2373
|
-
if (spatialDataType === 'geo') {
|
|
2374
|
-
return tileFeaturesGeometries({
|
|
2375
|
-
tiles,
|
|
2376
|
-
tileFormat,
|
|
2377
|
-
spatialFilter,
|
|
2378
|
-
uniqueIdProperty,
|
|
2379
|
-
options
|
|
2380
|
-
});
|
|
2381
|
-
}
|
|
2382
|
-
if (tiles.some(isRasterTile)) {
|
|
2383
|
-
assert$1(rasterMetadata, 'Missing raster metadata');
|
|
2384
|
-
return tileFeaturesRaster({
|
|
2385
|
-
tiles: tiles,
|
|
2386
|
-
spatialFilter,
|
|
2387
|
-
spatialDataColumn,
|
|
2388
|
-
spatialDataType,
|
|
2389
|
-
rasterMetadata
|
|
2390
|
-
});
|
|
2516
|
+
class WidgetQuerySource extends WidgetRemoteSource {
|
|
2517
|
+
getModelSource(filters, filterOwner) {
|
|
2518
|
+
return {
|
|
2519
|
+
...super._getModelSource(filters, filterOwner),
|
|
2520
|
+
type: 'query',
|
|
2521
|
+
data: this.props.sqlQuery,
|
|
2522
|
+
queryParameters: this.props.queryParameters
|
|
2523
|
+
};
|
|
2391
2524
|
}
|
|
2392
|
-
return tileFeaturesSpatialIndex({
|
|
2393
|
-
tiles: tiles,
|
|
2394
|
-
spatialFilter,
|
|
2395
|
-
spatialDataColumn,
|
|
2396
|
-
spatialDataType
|
|
2397
|
-
});
|
|
2398
2525
|
}
|
|
2399
2526
|
|
|
2400
|
-
/** @
|
|
2527
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2401
2528
|
const aggregationFunctions = {
|
|
2402
2529
|
count: values => values.length,
|
|
2403
2530
|
min: function () {
|
|
@@ -2413,7 +2540,7 @@ const aggregationFunctions = {
|
|
|
2413
2540
|
return applyAggregationFunction(avg, ...[].slice.call(arguments));
|
|
2414
2541
|
}
|
|
2415
2542
|
};
|
|
2416
|
-
/** @
|
|
2543
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2417
2544
|
function aggregate(feature, keys, operation) {
|
|
2418
2545
|
if (!keys?.length) {
|
|
2419
2546
|
throw new Error('Cannot aggregate a feature without having keys');
|
|
@@ -2562,7 +2689,7 @@ var thenBy_module = function () {
|
|
|
2562
2689
|
* @param [sortOptions.sortByDirection] - Direction by the columns will be sorted
|
|
2563
2690
|
* @param [sortOptions.sortByColumnType] - Column type
|
|
2564
2691
|
* @internal
|
|
2565
|
-
* @
|
|
2692
|
+
* @privateRemarks Source: @carto/react-core
|
|
2566
2693
|
*/
|
|
2567
2694
|
function applySorting(features, _temp) {
|
|
2568
2695
|
let {
|
|
@@ -2601,7 +2728,7 @@ function createSortFn(_ref) {
|
|
|
2601
2728
|
sortByColumnType
|
|
2602
2729
|
});
|
|
2603
2730
|
let sortFn = thenBy_module.firstBy(...firstSortOption);
|
|
2604
|
-
for (
|
|
2731
|
+
for (const sortOptions of othersSortOptions) {
|
|
2605
2732
|
sortFn = sortFn.thenBy(...sortOptions);
|
|
2606
2733
|
}
|
|
2607
2734
|
return sortFn;
|
|
@@ -2650,7 +2777,7 @@ function normalizeSortByOptions(_ref2) {
|
|
|
2650
2777
|
});
|
|
2651
2778
|
}
|
|
2652
2779
|
|
|
2653
|
-
/** @
|
|
2780
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2654
2781
|
function groupValuesByColumn(_ref) {
|
|
2655
2782
|
let {
|
|
2656
2783
|
data,
|
|
@@ -2712,7 +2839,7 @@ const GROUP_KEY_FN_MAPPING = {
|
|
|
2712
2839
|
minute: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes()),
|
|
2713
2840
|
second: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds())
|
|
2714
2841
|
};
|
|
2715
|
-
/** @
|
|
2842
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
2716
2843
|
function groupValuesByDateColumn(_ref) {
|
|
2717
2844
|
let {
|
|
2718
2845
|
data,
|
|
@@ -2760,7 +2887,7 @@ function groupValuesByDateColumn(_ref) {
|
|
|
2760
2887
|
|
|
2761
2888
|
/**
|
|
2762
2889
|
* Histogram computation.
|
|
2763
|
-
* @
|
|
2890
|
+
* @privateRemarks Source: @carto/react-core
|
|
2764
2891
|
*/
|
|
2765
2892
|
function histogram(_ref) {
|
|
2766
2893
|
let {
|
|
@@ -2798,7 +2925,7 @@ function histogram(_ref) {
|
|
|
2798
2925
|
|
|
2799
2926
|
/**
|
|
2800
2927
|
* Filters invalid features and formats data.
|
|
2801
|
-
* @
|
|
2928
|
+
* @privateRemarks Source: @carto/react-core
|
|
2802
2929
|
*/
|
|
2803
2930
|
function scatterPlot(_ref) {
|
|
2804
2931
|
let {
|
|
@@ -2847,10 +2974,12 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
2847
2974
|
super(...arguments);
|
|
2848
2975
|
this._tiles = [];
|
|
2849
2976
|
this._features = [];
|
|
2977
|
+
this._tileFeatureExtractOptions = {};
|
|
2978
|
+
this._tileFeatureExtractPreviousInputs = {};
|
|
2850
2979
|
}
|
|
2851
|
-
getModelSource(
|
|
2980
|
+
getModelSource(filters, filterOwner) {
|
|
2852
2981
|
return {
|
|
2853
|
-
...super._getModelSource(
|
|
2982
|
+
...super._getModelSource(filters, filterOwner),
|
|
2854
2983
|
type: 'tileset',
|
|
2855
2984
|
data: this.props.tableName
|
|
2856
2985
|
};
|
|
@@ -2862,39 +2991,46 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
2862
2991
|
*/
|
|
2863
2992
|
loadTiles(tiles) {
|
|
2864
2993
|
this._tiles = tiles;
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2994
|
+
this._features.length = 0;
|
|
2995
|
+
}
|
|
2996
|
+
/** Configures options used to extract features from tiles. */
|
|
2997
|
+
setTileFeatureExtractOptions(options) {
|
|
2998
|
+
this._tileFeatureExtractOptions = options;
|
|
2999
|
+
this._features.length = 0;
|
|
3000
|
+
}
|
|
3001
|
+
_extractTileFeatures(spatialFilter) {
|
|
3002
|
+
// When spatial filter has not changed, don't redo extraction. If tiles or
|
|
3003
|
+
// tile extract options change, features will have been cleared already.
|
|
3004
|
+
const prevInputs = this._tileFeatureExtractPreviousInputs;
|
|
3005
|
+
if (this._features.length && prevInputs.spatialFilter && booleanEqual.booleanEqual(prevInputs.spatialFilter, spatialFilter)) {
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
2876
3008
|
this._features = tileFeatures({
|
|
3009
|
+
...this.props,
|
|
3010
|
+
...this._tileFeatureExtractOptions,
|
|
2877
3011
|
tiles: this._tiles,
|
|
2878
|
-
|
|
2879
|
-
spatialFilter,
|
|
2880
|
-
uniqueIdProperty,
|
|
2881
|
-
...this.props
|
|
3012
|
+
spatialFilter
|
|
2882
3013
|
});
|
|
3014
|
+
prevInputs.spatialFilter = spatialFilter;
|
|
2883
3015
|
}
|
|
2884
|
-
/**
|
|
2885
|
-
|
|
3016
|
+
/**
|
|
3017
|
+
* Loads features as GeoJSON (used for testing).
|
|
3018
|
+
* @experimental
|
|
3019
|
+
* @internal Not for public use. Spatial filters in other method calls will be ignored.
|
|
3020
|
+
*/
|
|
3021
|
+
loadGeoJSON(_ref) {
|
|
2886
3022
|
let {
|
|
2887
3023
|
geojson,
|
|
2888
|
-
spatialFilter
|
|
2889
|
-
|
|
2890
|
-
} = _ref2;
|
|
3024
|
+
spatialFilter
|
|
3025
|
+
} = _ref;
|
|
2891
3026
|
this._features = geojsonFeatures({
|
|
2892
3027
|
geojson,
|
|
2893
3028
|
spatialFilter,
|
|
2894
|
-
|
|
3029
|
+
...this._tileFeatureExtractOptions
|
|
2895
3030
|
});
|
|
3031
|
+
this._tileFeatureExtractPreviousInputs.spatialFilter = spatialFilter;
|
|
2896
3032
|
}
|
|
2897
|
-
getFeatures(
|
|
3033
|
+
getFeatures() {
|
|
2898
3034
|
try {
|
|
2899
3035
|
throw new Error('getFeatures not supported for tilesets');
|
|
2900
3036
|
return Promise.resolve();
|
|
@@ -2902,28 +3038,25 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
2902
3038
|
return Promise.reject(e);
|
|
2903
3039
|
}
|
|
2904
3040
|
}
|
|
2905
|
-
getFormula(
|
|
3041
|
+
getFormula(_ref2) {
|
|
2906
3042
|
let {
|
|
2907
3043
|
column = '*',
|
|
2908
3044
|
operation = 'count',
|
|
2909
3045
|
joinOperation,
|
|
2910
|
-
|
|
2911
|
-
|
|
3046
|
+
filters,
|
|
3047
|
+
filterOwner,
|
|
3048
|
+
spatialFilter
|
|
3049
|
+
} = _ref2;
|
|
2912
3050
|
try {
|
|
2913
3051
|
const _this = this;
|
|
2914
3052
|
if (operation === 'custom') {
|
|
2915
3053
|
throw new Error('Custom aggregation not supported for tilesets');
|
|
2916
3054
|
}
|
|
2917
|
-
if (!_this._features.length) {
|
|
2918
|
-
return Promise.resolve({
|
|
2919
|
-
value: null
|
|
2920
|
-
});
|
|
2921
|
-
}
|
|
2922
3055
|
// Column is required except when operation is 'count'.
|
|
2923
3056
|
if (column && column !== '*' || operation !== 'count') {
|
|
2924
3057
|
assertColumn(_this._features, column);
|
|
2925
3058
|
}
|
|
2926
|
-
const filteredFeatures = _this._getFilteredFeatures(filterOwner);
|
|
3059
|
+
const filteredFeatures = _this._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
2927
3060
|
if (filteredFeatures.length === 0 && operation !== 'count') {
|
|
2928
3061
|
return Promise.resolve({
|
|
2929
3062
|
value: null
|
|
@@ -2937,21 +3070,23 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
2937
3070
|
return Promise.reject(e);
|
|
2938
3071
|
}
|
|
2939
3072
|
}
|
|
2940
|
-
getHistogram(
|
|
3073
|
+
getHistogram(_ref3) {
|
|
2941
3074
|
let {
|
|
2942
3075
|
operation = 'count',
|
|
2943
3076
|
ticks,
|
|
2944
3077
|
column,
|
|
2945
3078
|
joinOperation,
|
|
2946
|
-
|
|
2947
|
-
|
|
3079
|
+
filters,
|
|
3080
|
+
filterOwner,
|
|
3081
|
+
spatialFilter
|
|
3082
|
+
} = _ref3;
|
|
2948
3083
|
try {
|
|
2949
3084
|
const _this2 = this;
|
|
3085
|
+
const filteredFeatures = _this2._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3086
|
+
assertColumn(_this2._features, column);
|
|
2950
3087
|
if (!_this2._features.length) {
|
|
2951
3088
|
return Promise.resolve([]);
|
|
2952
3089
|
}
|
|
2953
|
-
const filteredFeatures = _this2._getFilteredFeatures(filterOwner);
|
|
2954
|
-
assertColumn(_this2._features, column);
|
|
2955
3090
|
return Promise.resolve(histogram({
|
|
2956
3091
|
data: filteredFeatures,
|
|
2957
3092
|
valuesColumns: normalizeColumns(column),
|
|
@@ -2963,20 +3098,22 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
2963
3098
|
return Promise.reject(e);
|
|
2964
3099
|
}
|
|
2965
3100
|
}
|
|
2966
|
-
getCategories(
|
|
3101
|
+
getCategories(_ref4) {
|
|
2967
3102
|
let {
|
|
2968
3103
|
column,
|
|
2969
3104
|
operation = 'count',
|
|
2970
3105
|
operationColumn,
|
|
2971
3106
|
joinOperation,
|
|
2972
|
-
|
|
2973
|
-
|
|
3107
|
+
filters,
|
|
3108
|
+
filterOwner,
|
|
3109
|
+
spatialFilter
|
|
3110
|
+
} = _ref4;
|
|
2974
3111
|
try {
|
|
2975
3112
|
const _this3 = this;
|
|
2976
|
-
|
|
3113
|
+
const filteredFeatures = _this3._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3114
|
+
if (!filteredFeatures.length) {
|
|
2977
3115
|
return Promise.resolve([]);
|
|
2978
3116
|
}
|
|
2979
|
-
const filteredFeatures = _this3._getFilteredFeatures(filterOwner);
|
|
2980
3117
|
assertColumn(_this3._features, column, operationColumn);
|
|
2981
3118
|
const groups = groupValuesByColumn({
|
|
2982
3119
|
data: filteredFeatures,
|
|
@@ -2990,20 +3127,22 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
2990
3127
|
return Promise.reject(e);
|
|
2991
3128
|
}
|
|
2992
3129
|
}
|
|
2993
|
-
getScatter(
|
|
3130
|
+
getScatter(_ref5) {
|
|
2994
3131
|
let {
|
|
2995
3132
|
xAxisColumn,
|
|
2996
3133
|
yAxisColumn,
|
|
2997
3134
|
xAxisJoinOperation,
|
|
2998
3135
|
yAxisJoinOperation,
|
|
2999
|
-
|
|
3000
|
-
|
|
3136
|
+
filters,
|
|
3137
|
+
filterOwner,
|
|
3138
|
+
spatialFilter
|
|
3139
|
+
} = _ref5;
|
|
3001
3140
|
try {
|
|
3002
3141
|
const _this4 = this;
|
|
3003
|
-
|
|
3142
|
+
const filteredFeatures = _this4._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3143
|
+
if (!filteredFeatures.length) {
|
|
3004
3144
|
return Promise.resolve([]);
|
|
3005
3145
|
}
|
|
3006
|
-
const filteredFeatures = _this4._getFilteredFeatures(filterOwner);
|
|
3007
3146
|
assertColumn(_this4._features, xAxisColumn, yAxisColumn);
|
|
3008
3147
|
return Promise.resolve(scatterPlot({
|
|
3009
3148
|
data: filteredFeatures,
|
|
@@ -3016,35 +3155,31 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
3016
3155
|
return Promise.reject(e);
|
|
3017
3156
|
}
|
|
3018
3157
|
}
|
|
3019
|
-
getTable(
|
|
3158
|
+
getTable(_ref6) {
|
|
3159
|
+
let {
|
|
3160
|
+
columns,
|
|
3161
|
+
searchFilterColumn,
|
|
3162
|
+
searchFilterText,
|
|
3163
|
+
sortBy,
|
|
3164
|
+
sortDirection,
|
|
3165
|
+
sortByColumnType,
|
|
3166
|
+
offset = 0,
|
|
3167
|
+
limit = 10,
|
|
3168
|
+
filters,
|
|
3169
|
+
filterOwner,
|
|
3170
|
+
spatialFilter
|
|
3171
|
+
} = _ref6;
|
|
3020
3172
|
try {
|
|
3021
3173
|
const _this5 = this;
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
abortController,
|
|
3026
|
-
...params
|
|
3027
|
-
} = options;
|
|
3028
|
-
const {
|
|
3029
|
-
columns,
|
|
3030
|
-
searchFilterColumn,
|
|
3031
|
-
searchFilterText,
|
|
3032
|
-
sortBy,
|
|
3033
|
-
sortDirection,
|
|
3034
|
-
sortByColumnType,
|
|
3035
|
-
offset = 0,
|
|
3036
|
-
limit = 10
|
|
3037
|
-
} = params;
|
|
3038
|
-
if (!_this5._features.length) {
|
|
3174
|
+
// Filter.
|
|
3175
|
+
let filteredFeatures = _this5._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3176
|
+
if (!filteredFeatures.length) {
|
|
3039
3177
|
return Promise.resolve({
|
|
3040
3178
|
rows: [],
|
|
3041
3179
|
totalCount: 0
|
|
3042
3180
|
});
|
|
3043
3181
|
}
|
|
3044
|
-
// Filter.
|
|
3045
|
-
let filteredFeatures = _this5._getFilteredFeatures(filterOwner);
|
|
3046
3182
|
// Search.
|
|
3047
|
-
// TODO: Could we get the same behavior by applying filters in loadTiles()?
|
|
3048
3183
|
if (searchFilterColumn && searchFilterText) {
|
|
3049
3184
|
filteredFeatures = filteredFeatures.filter(row => row[searchFilterColumn] && String(row[searchFilterColumn]).toLowerCase().includes(String(searchFilterText).toLowerCase()));
|
|
3050
3185
|
}
|
|
@@ -3080,16 +3215,18 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
3080
3215
|
operation,
|
|
3081
3216
|
operationColumn,
|
|
3082
3217
|
joinOperation,
|
|
3083
|
-
|
|
3218
|
+
filters,
|
|
3219
|
+
filterOwner,
|
|
3220
|
+
spatialFilter
|
|
3084
3221
|
} = _ref7;
|
|
3085
3222
|
try {
|
|
3086
3223
|
const _this6 = this;
|
|
3087
|
-
|
|
3224
|
+
const filteredFeatures = _this6._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3225
|
+
if (!filteredFeatures.length) {
|
|
3088
3226
|
return Promise.resolve({
|
|
3089
3227
|
rows: []
|
|
3090
3228
|
});
|
|
3091
3229
|
}
|
|
3092
|
-
const filteredFeatures = _this6._getFilteredFeatures(filterOwner);
|
|
3093
3230
|
assertColumn(_this6._features, column, operationColumn);
|
|
3094
3231
|
const rows = groupValuesByDateColumn({
|
|
3095
3232
|
data: filteredFeatures,
|
|
@@ -3109,17 +3246,19 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
3109
3246
|
getRange(_ref8) {
|
|
3110
3247
|
let {
|
|
3111
3248
|
column,
|
|
3112
|
-
|
|
3249
|
+
filters,
|
|
3250
|
+
filterOwner,
|
|
3251
|
+
spatialFilter
|
|
3113
3252
|
} = _ref8;
|
|
3114
3253
|
try {
|
|
3115
3254
|
const _this7 = this;
|
|
3255
|
+
assertColumn(_this7._features, column);
|
|
3256
|
+
const filteredFeatures = _this7._getFilteredFeatures(spatialFilter, filters, filterOwner);
|
|
3116
3257
|
if (!_this7._features.length) {
|
|
3117
3258
|
// TODO: Is this the only nullable response in the Widgets API? If so,
|
|
3118
3259
|
// can we do something more consistent?
|
|
3119
3260
|
return Promise.resolve(null);
|
|
3120
3261
|
}
|
|
3121
|
-
assertColumn(_this7._features, column);
|
|
3122
|
-
const filteredFeatures = _this7._getFilteredFeatures(filterOwner);
|
|
3123
3262
|
return Promise.resolve({
|
|
3124
3263
|
min: aggregationFunctions.min(filteredFeatures, column),
|
|
3125
3264
|
max: aggregationFunctions.max(filteredFeatures, column)
|
|
@@ -3131,8 +3270,10 @@ class WidgetTilesetSource extends WidgetSource {
|
|
|
3131
3270
|
/****************************************************************************
|
|
3132
3271
|
* INTERNAL
|
|
3133
3272
|
*/
|
|
3134
|
-
_getFilteredFeatures(filterOwner) {
|
|
3135
|
-
|
|
3273
|
+
_getFilteredFeatures(spatialFilter, filters, filterOwner) {
|
|
3274
|
+
assert(spatialFilter, 'spatialFilter required for tilesets');
|
|
3275
|
+
this._extractTileFeatures(spatialFilter);
|
|
3276
|
+
return applyFilters(this._features, getApplicableFilters(filterOwner, filters || this.props.filters), this.props.filtersLogicalOperator || 'and');
|
|
3136
3277
|
}
|
|
3137
3278
|
}
|
|
3138
3279
|
function assertColumn(features) {
|
|
@@ -3174,9 +3315,9 @@ class WidgetRasterSource extends WidgetTilesetSource {}
|
|
|
3174
3315
|
* ```
|
|
3175
3316
|
*/
|
|
3176
3317
|
class WidgetTableSource extends WidgetRemoteSource {
|
|
3177
|
-
getModelSource(
|
|
3318
|
+
getModelSource(filters, filterOwner) {
|
|
3178
3319
|
return {
|
|
3179
|
-
...super._getModelSource(
|
|
3320
|
+
...super._getModelSource(filters, filterOwner),
|
|
3180
3321
|
type: 'table',
|
|
3181
3322
|
data: this.props.tableName
|
|
3182
3323
|
};
|
|
@@ -3186,7 +3327,6 @@ class WidgetTableSource extends WidgetRemoteSource {
|
|
|
3186
3327
|
// deck.gl
|
|
3187
3328
|
// SPDX-License-Identifier: MIT
|
|
3188
3329
|
// Copyright (c) vis.gl contributors
|
|
3189
|
-
/* eslint-disable camelcase */
|
|
3190
3330
|
const h3QuerySource = function (options) {
|
|
3191
3331
|
try {
|
|
3192
3332
|
const {
|
|
@@ -3230,7 +3370,6 @@ const h3QuerySource = function (options) {
|
|
|
3230
3370
|
// deck.gl
|
|
3231
3371
|
// SPDX-License-Identifier: MIT
|
|
3232
3372
|
// Copyright (c) vis.gl contributors
|
|
3233
|
-
/* eslint-disable camelcase */
|
|
3234
3373
|
const h3TableSource = function (options) {
|
|
3235
3374
|
try {
|
|
3236
3375
|
const {
|
|
@@ -3331,7 +3470,6 @@ const rasterSource = function (options) {
|
|
|
3331
3470
|
// deck.gl
|
|
3332
3471
|
// SPDX-License-Identifier: MIT
|
|
3333
3472
|
// Copyright (c) vis.gl contributors
|
|
3334
|
-
/* eslint-disable camelcase */
|
|
3335
3473
|
const quadbinQuerySource = function (options) {
|
|
3336
3474
|
try {
|
|
3337
3475
|
const {
|
|
@@ -3375,7 +3513,6 @@ const quadbinQuerySource = function (options) {
|
|
|
3375
3513
|
// deck.gl
|
|
3376
3514
|
// SPDX-License-Identifier: MIT
|
|
3377
3515
|
// Copyright (c) vis.gl contributors
|
|
3378
|
-
/* eslint-disable camelcase */
|
|
3379
3516
|
const quadbinTableSource = function (options) {
|
|
3380
3517
|
try {
|
|
3381
3518
|
const {
|
|
@@ -3441,7 +3578,6 @@ const quadbinTilesetSource = function (options) {
|
|
|
3441
3578
|
// deck.gl
|
|
3442
3579
|
// SPDX-License-Identifier: MIT
|
|
3443
3580
|
// Copyright (c) vis.gl contributors
|
|
3444
|
-
/* eslint-disable camelcase */
|
|
3445
3581
|
const vectorQuerySource = function (options) {
|
|
3446
3582
|
try {
|
|
3447
3583
|
const {
|
|
@@ -3490,7 +3626,6 @@ const vectorQuerySource = function (options) {
|
|
|
3490
3626
|
// deck.gl
|
|
3491
3627
|
// SPDX-License-Identifier: MIT
|
|
3492
3628
|
// Copyright (c) vis.gl contributors
|
|
3493
|
-
/* eslint-disable camelcase */
|
|
3494
3629
|
const vectorTableSource = function (options) {
|
|
3495
3630
|
try {
|
|
3496
3631
|
const {
|
|
@@ -3619,6 +3754,7 @@ exports.WidgetRemoteSource = WidgetRemoteSource;
|
|
|
3619
3754
|
exports.WidgetSource = WidgetSource;
|
|
3620
3755
|
exports.WidgetTableSource = WidgetTableSource;
|
|
3621
3756
|
exports.WidgetTilesetSource = WidgetTilesetSource;
|
|
3757
|
+
exports._buildFeatureFilter = _buildFeatureFilter;
|
|
3622
3758
|
exports._getHexagonResolution = _getHexagonResolution;
|
|
3623
3759
|
exports.addFilter = addFilter;
|
|
3624
3760
|
exports.aggregate = aggregate;
|
|
@@ -3628,7 +3764,6 @@ exports.applySorting = applySorting;
|
|
|
3628
3764
|
exports.boundaryQuerySource = boundaryQuerySource;
|
|
3629
3765
|
exports.boundaryTableSource = boundaryTableSource;
|
|
3630
3766
|
exports.buildBinaryFeatureFilter = buildBinaryFeatureFilter;
|
|
3631
|
-
exports.buildFeatureFilter = buildFeatureFilter;
|
|
3632
3767
|
exports.buildPublicMapUrl = buildPublicMapUrl;
|
|
3633
3768
|
exports.buildStatsUrl = buildStatsUrl;
|
|
3634
3769
|
exports.clearFilters = clearFilters;
|
|
@@ -3637,6 +3772,7 @@ exports.createViewportSpatialFilter = createViewportSpatialFilter;
|
|
|
3637
3772
|
exports.filterFunctions = filterFunctions;
|
|
3638
3773
|
exports.geojsonFeatures = geojsonFeatures;
|
|
3639
3774
|
exports.getClient = getClient;
|
|
3775
|
+
exports.getDataFilterExtensionProps = getDataFilterExtensionProps;
|
|
3640
3776
|
exports.getFilter = getFilter;
|
|
3641
3777
|
exports.groupValuesByColumn = groupValuesByColumn;
|
|
3642
3778
|
exports.groupValuesByDateColumn = groupValuesByDateColumn;
|