@carto/api-client 0.4.6 → 0.4.7-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api-client.cjs +3473 -1556
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.d.cts +1389 -0
- package/build/api-client.d.ts +1389 -0
- package/build/api-client.js +3676 -0
- package/build/api-client.js.map +1 -0
- package/build/worker.d.ts +2 -0
- package/build/worker.js +1949 -0
- package/build/worker.js.map +1 -0
- package/package.json +58 -40
- 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 -11
- package/src/constants.ts +28 -3
- package/src/deck/get-data-filter-extension-props.ts +146 -0
- package/src/deck/index.ts +1 -0
- package/src/filters/Filter.ts +179 -0
- package/src/filters/FilterTypes.ts +109 -0
- package/src/filters/geosjonFeatures.ts +32 -0
- package/src/filters/index.ts +6 -0
- package/src/filters/tileFeatures.ts +51 -0
- package/src/filters/tileFeaturesGeometries.ts +444 -0
- package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
- package/src/filters.ts +4 -4
- package/src/geo.ts +12 -14
- package/src/global.d.ts +3 -8
- package/src/index.ts +7 -0
- package/src/models/common.ts +11 -9
- package/src/models/index.ts +1 -1
- package/src/models/model.ts +3 -4
- package/src/operations/aggregation.ts +154 -0
- package/src/operations/applySorting.ts +109 -0
- package/src/operations/groupBy.ts +59 -0
- package/src/operations/groupByDate.ts +98 -0
- package/src/operations/histogram.ts +66 -0
- package/src/operations/index.ts +6 -0
- package/src/operations/scatterPlot.ts +50 -0
- package/src/sources/base-source.ts +8 -8
- package/src/sources/boundary-query-source.ts +2 -2
- package/src/sources/boundary-table-source.ts +2 -2
- package/src/sources/h3-query-source.ts +7 -5
- package/src/sources/h3-table-source.ts +7 -5
- package/src/sources/h3-tileset-source.ts +20 -8
- 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 +20 -8
- package/src/sources/raster-source.ts +3 -2
- package/src/sources/types.ts +9 -3
- package/src/sources/vector-query-source.ts +7 -5
- package/src/sources/vector-table-source.ts +7 -5
- package/src/sources/vector-tileset-source.ts +21 -8
- package/src/spatial-index.ts +4 -5
- package/src/types-internal.ts +11 -5
- package/src/types.ts +73 -15
- package/src/utils/dateUtils.ts +28 -0
- package/src/utils/getTileFormat.ts +9 -0
- package/src/utils/makeIntervalComplete.ts +17 -0
- package/src/utils/transformTileCoordsToWGS84.ts +77 -0
- package/src/utils/transformToTileCoords.ts +85 -0
- package/src/utils.ts +3 -3
- package/src/widget-sources/index.ts +3 -1
- package/src/widget-sources/types.ts +37 -25
- package/src/widget-sources/widget-query-source.ts +12 -5
- package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +55 -149
- package/src/widget-sources/widget-source.ts +145 -0
- package/src/widget-sources/widget-table-source.ts +12 -5
- package/src/widget-sources/widget-tileset-source-impl.ts +417 -0
- package/src/widget-sources/widget-tileset-source.ts +311 -0
- package/src/workers/constants.ts +13 -0
- package/src/workers/types.ts +19 -0
- package/src/workers/widget-tileset-worker.ts +40 -0
- package/build/api/carto-api-error.d.ts +0 -26
- package/build/api/endpoints.d.ts +0 -24
- package/build/api/index.d.ts +0 -5
- package/build/api/query.d.ts +0 -3
- package/build/api/request-with-parameters.d.ts +0 -10
- package/build/api-client.modern.js +0 -1742
- package/build/api-client.modern.js.map +0 -1
- package/build/client.d.ts +0 -14
- package/build/constants-internal.d.ts +0 -26
- package/build/constants.d.ts +0 -31
- package/build/filters.d.ts +0 -39
- package/build/geo.d.ts +0 -19
- package/build/index.d.ts +0 -11
- package/build/models/common.d.ts +0 -27
- package/build/models/index.d.ts +0 -3
- package/build/models/model.d.ts +0 -37
- package/build/sources/base-source.d.ts +0 -4
- package/build/sources/boundary-query-source.d.ts +0 -10
- package/build/sources/boundary-table-source.d.ts +0 -8
- package/build/sources/h3-query-source.d.ts +0 -5
- package/build/sources/h3-table-source.d.ts +0 -5
- package/build/sources/h3-tileset-source.d.ts +0 -4
- package/build/sources/index.d.ts +0 -26
- package/build/sources/quadbin-query-source.d.ts +0 -5
- package/build/sources/quadbin-table-source.d.ts +0 -5
- package/build/sources/quadbin-tileset-source.d.ts +0 -4
- package/build/sources/raster-source.d.ts +0 -4
- package/build/sources/types.d.ts +0 -366
- package/build/sources/vector-query-source.d.ts +0 -5
- package/build/sources/vector-table-source.d.ts +0 -5
- package/build/sources/vector-tileset-source.d.ts +0 -4
- package/build/spatial-index.d.ts +0 -14
- package/build/types-internal.d.ts +0 -52
- package/build/types.d.ts +0 -80
- package/build/utils.d.ts +0 -32
- package/build/widget-sources/index.d.ts +0 -4
- package/build/widget-sources/types.d.ts +0 -149
- package/build/widget-sources/widget-base-source.d.ts +0 -99
- package/build/widget-sources/widget-query-source.d.ts +0 -33
- package/build/widget-sources/widget-table-source.d.ts +0 -33
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import bboxPolygon from '@turf/bbox-polygon';
|
|
2
|
+
import intersects from '@turf/boolean-intersects';
|
|
3
|
+
import booleanWithin from '@turf/boolean-within';
|
|
4
|
+
import intersect from '@turf/intersect';
|
|
5
|
+
import {transformToTileCoords} from '../utils/transformToTileCoords.js';
|
|
6
|
+
import {transformTileCoordsToWGS84} from '../utils/transformTileCoordsToWGS84.js';
|
|
7
|
+
import {TileFormat} from '../constants.js';
|
|
8
|
+
import {
|
|
9
|
+
BBox,
|
|
10
|
+
Feature,
|
|
11
|
+
Geometry,
|
|
12
|
+
LineString,
|
|
13
|
+
MultiPolygon,
|
|
14
|
+
Point,
|
|
15
|
+
Polygon,
|
|
16
|
+
Position,
|
|
17
|
+
} from 'geojson';
|
|
18
|
+
import {SpatialFilter, Tile} from '../types.js';
|
|
19
|
+
import {TileFeatureExtractOptions} from './tileFeatures.js';
|
|
20
|
+
import {featureCollection} from '@turf/helpers';
|
|
21
|
+
import {FeatureData} from '../types-internal.js';
|
|
22
|
+
import {
|
|
23
|
+
BinaryAttribute,
|
|
24
|
+
BinaryFeature,
|
|
25
|
+
BinaryGeometryType,
|
|
26
|
+
BinaryPointFeature,
|
|
27
|
+
TypedArrayConstructor,
|
|
28
|
+
} from '@loaders.gl/schema';
|
|
29
|
+
|
|
30
|
+
export const FEATURE_GEOM_PROPERTY = '__geomValue';
|
|
31
|
+
|
|
32
|
+
type TileMap = Map<unknown, unknown>;
|
|
33
|
+
|
|
34
|
+
type TileDataInternal = {
|
|
35
|
+
uniqueId: string | number | undefined;
|
|
36
|
+
properties: any;
|
|
37
|
+
numericProps: Record<string, number>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function tileFeaturesGeometries({
|
|
41
|
+
tiles,
|
|
42
|
+
tileFormat,
|
|
43
|
+
spatialFilter,
|
|
44
|
+
uniqueIdProperty,
|
|
45
|
+
options,
|
|
46
|
+
}: {
|
|
47
|
+
tiles: Tile[];
|
|
48
|
+
tileFormat?: TileFormat;
|
|
49
|
+
spatialFilter: SpatialFilter;
|
|
50
|
+
uniqueIdProperty?: string;
|
|
51
|
+
options?: {storeGeometry?: boolean};
|
|
52
|
+
}): FeatureData[] {
|
|
53
|
+
const map = new Map();
|
|
54
|
+
|
|
55
|
+
for (const tile of tiles) {
|
|
56
|
+
// Discard if it's not a visible tile (only check false value, not undefined)
|
|
57
|
+
// or tile has not data
|
|
58
|
+
if (tile.isVisible === false || !tile.data) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const bbox = [
|
|
63
|
+
tile.bbox.west,
|
|
64
|
+
tile.bbox.south,
|
|
65
|
+
tile.bbox.east,
|
|
66
|
+
tile.bbox.north,
|
|
67
|
+
] as BBox;
|
|
68
|
+
const bboxToGeom = bboxPolygon(bbox);
|
|
69
|
+
const tileIsFullyVisible = booleanWithin(bboxToGeom, spatialFilter);
|
|
70
|
+
|
|
71
|
+
// Clip the geometry to intersect with the tile
|
|
72
|
+
const spatialFilterFeature: Feature<Polygon | MultiPolygon> = {
|
|
73
|
+
type: 'Feature',
|
|
74
|
+
geometry: spatialFilter,
|
|
75
|
+
properties: {},
|
|
76
|
+
};
|
|
77
|
+
const clippedGeometryToIntersect = intersect(
|
|
78
|
+
featureCollection([bboxToGeom, spatialFilterFeature])
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (!clippedGeometryToIntersect) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// We assume that MVT tileFormat uses local coordinates so we transform the geometry to intersect to tile coordinates [0..1],
|
|
86
|
+
// while in the case of 'geojson' or binary, the geometries are already in WGS84
|
|
87
|
+
const transformedGeometryToIntersect =
|
|
88
|
+
tileFormat === TileFormat.MVT
|
|
89
|
+
? transformToTileCoords(clippedGeometryToIntersect.geometry, bbox)
|
|
90
|
+
: clippedGeometryToIntersect.geometry;
|
|
91
|
+
|
|
92
|
+
createIndicesForPoints(tile.data.points!);
|
|
93
|
+
|
|
94
|
+
calculateFeatures({
|
|
95
|
+
map,
|
|
96
|
+
tileIsFullyVisible,
|
|
97
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
98
|
+
data: tile.data.points!,
|
|
99
|
+
type: 'Point',
|
|
100
|
+
bbox,
|
|
101
|
+
tileFormat,
|
|
102
|
+
uniqueIdProperty,
|
|
103
|
+
options,
|
|
104
|
+
});
|
|
105
|
+
calculateFeatures({
|
|
106
|
+
map,
|
|
107
|
+
tileIsFullyVisible,
|
|
108
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
109
|
+
data: tile.data.lines!,
|
|
110
|
+
type: 'LineString',
|
|
111
|
+
bbox,
|
|
112
|
+
tileFormat,
|
|
113
|
+
uniqueIdProperty,
|
|
114
|
+
options,
|
|
115
|
+
});
|
|
116
|
+
calculateFeatures({
|
|
117
|
+
map,
|
|
118
|
+
tileIsFullyVisible,
|
|
119
|
+
geometryIntersection: transformedGeometryToIntersect,
|
|
120
|
+
data: tile.data.polygons!,
|
|
121
|
+
type: 'Polygon',
|
|
122
|
+
bbox,
|
|
123
|
+
tileFormat,
|
|
124
|
+
uniqueIdProperty,
|
|
125
|
+
options,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return Array.from(map.values());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function processTileFeatureProperties({
|
|
132
|
+
map,
|
|
133
|
+
data,
|
|
134
|
+
startIndex,
|
|
135
|
+
endIndex,
|
|
136
|
+
type,
|
|
137
|
+
bbox,
|
|
138
|
+
tileFormat,
|
|
139
|
+
uniqueIdProperty,
|
|
140
|
+
storeGeometry,
|
|
141
|
+
geometryIntersection,
|
|
142
|
+
}: {
|
|
143
|
+
map: TileMap;
|
|
144
|
+
data: BinaryFeature;
|
|
145
|
+
startIndex: number;
|
|
146
|
+
endIndex: number;
|
|
147
|
+
type: BinaryGeometryType;
|
|
148
|
+
bbox: BBox;
|
|
149
|
+
tileFormat?: TileFormat;
|
|
150
|
+
uniqueIdProperty?: string;
|
|
151
|
+
storeGeometry: boolean;
|
|
152
|
+
geometryIntersection?: Geometry;
|
|
153
|
+
}) {
|
|
154
|
+
const tileProps = getPropertiesFromTile(data, startIndex);
|
|
155
|
+
const uniquePropertyValue = getUniquePropertyValue(
|
|
156
|
+
tileProps,
|
|
157
|
+
uniqueIdProperty,
|
|
158
|
+
map
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (!uniquePropertyValue || map.has(uniquePropertyValue)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
let geometry: Geometry | null = null;
|
|
165
|
+
|
|
166
|
+
// Only calculate geometry if necessary
|
|
167
|
+
if (storeGeometry || geometryIntersection) {
|
|
168
|
+
const {positions} = data;
|
|
169
|
+
const ringCoordinates = getRingCoordinatesFor(
|
|
170
|
+
startIndex,
|
|
171
|
+
endIndex,
|
|
172
|
+
positions
|
|
173
|
+
);
|
|
174
|
+
geometry = getFeatureByType(ringCoordinates, type);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// If intersection is required, check before proceeding
|
|
178
|
+
if (
|
|
179
|
+
geometry &&
|
|
180
|
+
geometryIntersection &&
|
|
181
|
+
!intersects(geometry, geometryIntersection)
|
|
182
|
+
) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const properties = parseProperties(tileProps);
|
|
187
|
+
|
|
188
|
+
// Only save geometry if necessary
|
|
189
|
+
if (storeGeometry && geometry) {
|
|
190
|
+
properties[FEATURE_GEOM_PROPERTY] =
|
|
191
|
+
tileFormat === TileFormat.MVT
|
|
192
|
+
? transformTileCoordsToWGS84(geometry, bbox)
|
|
193
|
+
: geometry;
|
|
194
|
+
}
|
|
195
|
+
map.set(uniquePropertyValue, properties);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function addIntersectedFeaturesInTile({
|
|
199
|
+
map,
|
|
200
|
+
data,
|
|
201
|
+
geometryIntersection,
|
|
202
|
+
type,
|
|
203
|
+
bbox,
|
|
204
|
+
tileFormat,
|
|
205
|
+
uniqueIdProperty,
|
|
206
|
+
options,
|
|
207
|
+
}: {
|
|
208
|
+
map: TileMap;
|
|
209
|
+
data: BinaryFeature;
|
|
210
|
+
geometryIntersection: Geometry;
|
|
211
|
+
type: BinaryGeometryType;
|
|
212
|
+
bbox: BBox;
|
|
213
|
+
tileFormat?: TileFormat;
|
|
214
|
+
uniqueIdProperty?: string;
|
|
215
|
+
options?: TileFeatureExtractOptions;
|
|
216
|
+
}) {
|
|
217
|
+
const indices = getIndices(data);
|
|
218
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
219
|
+
|
|
220
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
221
|
+
const startIndex = indices[i];
|
|
222
|
+
const endIndex = indices[i + 1];
|
|
223
|
+
processTileFeatureProperties({
|
|
224
|
+
map,
|
|
225
|
+
data,
|
|
226
|
+
startIndex,
|
|
227
|
+
endIndex,
|
|
228
|
+
type,
|
|
229
|
+
bbox,
|
|
230
|
+
tileFormat,
|
|
231
|
+
uniqueIdProperty,
|
|
232
|
+
storeGeometry,
|
|
233
|
+
geometryIntersection,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function getIndices(data: BinaryFeature) {
|
|
239
|
+
let indices: BinaryAttribute;
|
|
240
|
+
switch (data.type) {
|
|
241
|
+
case 'Point':
|
|
242
|
+
// @ts-expect-error Missing or changed types?
|
|
243
|
+
indices = data.pointIndices;
|
|
244
|
+
break;
|
|
245
|
+
case 'LineString':
|
|
246
|
+
indices = data.pathIndices;
|
|
247
|
+
break;
|
|
248
|
+
case 'Polygon':
|
|
249
|
+
indices = data.primitivePolygonIndices;
|
|
250
|
+
break;
|
|
251
|
+
default:
|
|
252
|
+
throw new Error(`Unexpected type, "${(data as BinaryFeature).type}"`);
|
|
253
|
+
}
|
|
254
|
+
return indices.value;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function getFeatureId(data: BinaryFeature, startIndex: number) {
|
|
258
|
+
return data.featureIds.value[startIndex];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getPropertiesFromTile(data: BinaryFeature, startIndex: number) {
|
|
262
|
+
const featureId = getFeatureId(data, startIndex);
|
|
263
|
+
const {properties, numericProps, fields} = data;
|
|
264
|
+
const result: TileDataInternal = {
|
|
265
|
+
uniqueId: (fields?.[featureId] as {id: string | number})?.id,
|
|
266
|
+
properties: properties[featureId],
|
|
267
|
+
numericProps: {},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
for (const key in numericProps) {
|
|
271
|
+
result.numericProps[key] = numericProps[key].value[startIndex];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function parseProperties(tileProps: TileDataInternal) {
|
|
278
|
+
const {properties, numericProps} = tileProps;
|
|
279
|
+
return Object.assign({}, properties, numericProps);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function getUniquePropertyValue(
|
|
283
|
+
tileProps: TileDataInternal,
|
|
284
|
+
uniqueIdProperty: string | undefined,
|
|
285
|
+
map: TileMap
|
|
286
|
+
) {
|
|
287
|
+
if (uniqueIdProperty) {
|
|
288
|
+
return getValueFromTileProps(tileProps, uniqueIdProperty);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (tileProps.uniqueId) {
|
|
292
|
+
return tileProps.uniqueId;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const artificialId = map.size + 1; // a counter, assumed as a valid new id
|
|
296
|
+
return (
|
|
297
|
+
getValueFromTileProps(tileProps, 'cartodb_id') ||
|
|
298
|
+
getValueFromTileProps(tileProps, 'geoid') ||
|
|
299
|
+
artificialId
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function getValueFromTileProps(
|
|
304
|
+
tileProps: TileDataInternal,
|
|
305
|
+
propertyName: string
|
|
306
|
+
) {
|
|
307
|
+
const {properties, numericProps} = tileProps;
|
|
308
|
+
return numericProps[propertyName] || properties[propertyName];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function getFeatureByType(
|
|
312
|
+
coordinates: Position[],
|
|
313
|
+
type: BinaryGeometryType
|
|
314
|
+
): Polygon | LineString | Point {
|
|
315
|
+
switch (type) {
|
|
316
|
+
case 'Polygon':
|
|
317
|
+
return {type: 'Polygon', coordinates: [coordinates]};
|
|
318
|
+
case 'LineString':
|
|
319
|
+
return {type: 'LineString', coordinates};
|
|
320
|
+
case 'Point':
|
|
321
|
+
return {type: 'Point', coordinates: coordinates[0]};
|
|
322
|
+
default:
|
|
323
|
+
throw new Error('Invalid geometry type');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function getRingCoordinatesFor(
|
|
328
|
+
startIndex: number,
|
|
329
|
+
endIndex: number,
|
|
330
|
+
positions: BinaryAttribute
|
|
331
|
+
) {
|
|
332
|
+
const ringCoordinates = [];
|
|
333
|
+
|
|
334
|
+
for (let j = startIndex; j < endIndex; j++) {
|
|
335
|
+
ringCoordinates.push(
|
|
336
|
+
Array.from(
|
|
337
|
+
positions.value.subarray(j * positions.size, (j + 1) * positions.size)
|
|
338
|
+
)
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return ringCoordinates;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function calculateFeatures({
|
|
346
|
+
map,
|
|
347
|
+
tileIsFullyVisible,
|
|
348
|
+
geometryIntersection,
|
|
349
|
+
data,
|
|
350
|
+
type,
|
|
351
|
+
bbox,
|
|
352
|
+
tileFormat,
|
|
353
|
+
uniqueIdProperty,
|
|
354
|
+
options,
|
|
355
|
+
}: {
|
|
356
|
+
map: TileMap;
|
|
357
|
+
tileIsFullyVisible: boolean;
|
|
358
|
+
geometryIntersection: SpatialFilter;
|
|
359
|
+
data: BinaryFeature;
|
|
360
|
+
type: BinaryGeometryType;
|
|
361
|
+
bbox: BBox;
|
|
362
|
+
tileFormat?: TileFormat;
|
|
363
|
+
uniqueIdProperty?: string;
|
|
364
|
+
options?: TileFeatureExtractOptions;
|
|
365
|
+
}) {
|
|
366
|
+
if (!data?.properties.length) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (tileIsFullyVisible) {
|
|
371
|
+
addAllFeaturesInTile({
|
|
372
|
+
map,
|
|
373
|
+
data,
|
|
374
|
+
type,
|
|
375
|
+
bbox,
|
|
376
|
+
tileFormat,
|
|
377
|
+
uniqueIdProperty,
|
|
378
|
+
options,
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
addIntersectedFeaturesInTile({
|
|
382
|
+
map,
|
|
383
|
+
data,
|
|
384
|
+
geometryIntersection,
|
|
385
|
+
type,
|
|
386
|
+
bbox,
|
|
387
|
+
tileFormat,
|
|
388
|
+
uniqueIdProperty,
|
|
389
|
+
options,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function addAllFeaturesInTile({
|
|
395
|
+
map,
|
|
396
|
+
data,
|
|
397
|
+
type,
|
|
398
|
+
bbox,
|
|
399
|
+
tileFormat,
|
|
400
|
+
uniqueIdProperty,
|
|
401
|
+
options,
|
|
402
|
+
}: {
|
|
403
|
+
map: TileMap;
|
|
404
|
+
data: BinaryFeature;
|
|
405
|
+
type: BinaryGeometryType;
|
|
406
|
+
bbox: BBox;
|
|
407
|
+
tileFormat?: TileFormat;
|
|
408
|
+
uniqueIdProperty?: string;
|
|
409
|
+
options?: TileFeatureExtractOptions;
|
|
410
|
+
}) {
|
|
411
|
+
const indices = getIndices(data);
|
|
412
|
+
const storeGeometry = options?.storeGeometry || false;
|
|
413
|
+
for (let i = 0; i < indices.length - 1; i++) {
|
|
414
|
+
const startIndex = indices[i];
|
|
415
|
+
const endIndex = indices[i + 1];
|
|
416
|
+
processTileFeatureProperties({
|
|
417
|
+
map,
|
|
418
|
+
data,
|
|
419
|
+
startIndex,
|
|
420
|
+
endIndex,
|
|
421
|
+
type,
|
|
422
|
+
bbox,
|
|
423
|
+
tileFormat,
|
|
424
|
+
uniqueIdProperty,
|
|
425
|
+
storeGeometry,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function createIndicesForPoints(data: BinaryPointFeature) {
|
|
431
|
+
const featureIds = data.featureIds.value;
|
|
432
|
+
const lastFeatureId = featureIds[featureIds.length - 1];
|
|
433
|
+
const PointIndicesArray = featureIds.constructor as TypedArrayConstructor;
|
|
434
|
+
|
|
435
|
+
const pointIndices: BinaryAttribute = {
|
|
436
|
+
value: new PointIndicesArray(featureIds.length + 1),
|
|
437
|
+
size: 1,
|
|
438
|
+
};
|
|
439
|
+
pointIndices.value.set(featureIds);
|
|
440
|
+
pointIndices.value.set([lastFeatureId + 1], featureIds.length);
|
|
441
|
+
|
|
442
|
+
// @ts-expect-error Missing or changed types?
|
|
443
|
+
data.pointIndices = pointIndices;
|
|
444
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {SpatialIndex} from '../constants.js';
|
|
2
|
+
import {getResolution as quadbinGetResolution, geometryToCells} from 'quadbin';
|
|
3
|
+
import bboxClip from '@turf/bbox-clip';
|
|
4
|
+
import {SpatialFilter, SpatialIndexTile} from '../types.js';
|
|
5
|
+
import {BBox, Feature} from 'geojson';
|
|
6
|
+
import {getResolution as h3GetResolution, polygonToCells} from 'h3-js';
|
|
7
|
+
import {FeatureData} from '../types-internal.js';
|
|
8
|
+
import {SpatialDataType} from '../sources/types.js';
|
|
9
|
+
|
|
10
|
+
export type TileFeaturesSpatialIndexOptions = {
|
|
11
|
+
tiles: SpatialIndexTile[];
|
|
12
|
+
spatialFilter: SpatialFilter;
|
|
13
|
+
spatialDataColumn: string;
|
|
14
|
+
spatialDataType: SpatialDataType;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function tileFeaturesSpatialIndex({
|
|
18
|
+
tiles,
|
|
19
|
+
spatialFilter,
|
|
20
|
+
spatialDataColumn,
|
|
21
|
+
spatialDataType,
|
|
22
|
+
}: TileFeaturesSpatialIndexOptions): FeatureData[] {
|
|
23
|
+
const map = new Map();
|
|
24
|
+
const spatialIndex = getSpatialIndex(spatialDataType);
|
|
25
|
+
const resolution = getResolution(tiles, spatialIndex);
|
|
26
|
+
const spatialIndexIDName = spatialDataColumn
|
|
27
|
+
? spatialDataColumn
|
|
28
|
+
: spatialIndex;
|
|
29
|
+
|
|
30
|
+
if (!resolution) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const cells = getCellsCoverGeometry(spatialFilter, spatialIndex, resolution);
|
|
34
|
+
|
|
35
|
+
if (!cells?.length) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// We transform cells to Set to improve the performace
|
|
40
|
+
const cellsSet = new Set<bigint | string>(cells);
|
|
41
|
+
|
|
42
|
+
for (const tile of tiles) {
|
|
43
|
+
if (tile.isVisible === false || !tile.data) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
tile.data.forEach((d: Feature) => {
|
|
48
|
+
if (cellsSet.has(d.id as bigint | string)) {
|
|
49
|
+
map.set(d.id, {...d.properties, [spatialIndexIDName]: d.id});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return Array.from(map.values());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getResolution(
|
|
57
|
+
tiles: SpatialIndexTile[],
|
|
58
|
+
spatialIndex: SpatialIndex
|
|
59
|
+
): number | undefined {
|
|
60
|
+
const data = tiles.find((tile) => tile.data?.length)?.data;
|
|
61
|
+
|
|
62
|
+
if (!data) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (spatialIndex === SpatialIndex.QUADBIN) {
|
|
67
|
+
return Number(quadbinGetResolution(data[0].id));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (spatialIndex === SpatialIndex.H3) {
|
|
71
|
+
return h3GetResolution(data[0].id);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const bboxWest: BBox = [-180, -90, 0, 90];
|
|
76
|
+
const bboxEast: BBox = [0, -90, 180, 90];
|
|
77
|
+
|
|
78
|
+
function getCellsCoverGeometry(
|
|
79
|
+
geometry: SpatialFilter,
|
|
80
|
+
spatialIndex: SpatialIndex,
|
|
81
|
+
resolution: number
|
|
82
|
+
) {
|
|
83
|
+
if (spatialIndex === SpatialIndex.QUADBIN) {
|
|
84
|
+
// @ts-expect-error TODO: Probably ought to be stricter about number vs. bigint types in this file.
|
|
85
|
+
return geometryToCells(geometry, resolution);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (spatialIndex === SpatialIndex.H3) {
|
|
89
|
+
// The current H3 polyfill algorithm can't deal with polygon segments of greater than 180 degrees longitude
|
|
90
|
+
// so we clip the geometry to be sure that none of them is greater than 180 degrees
|
|
91
|
+
// https://github.com/uber/h3-js/issues/24#issuecomment-431893796
|
|
92
|
+
return polygonToCells(
|
|
93
|
+
bboxClip(geometry, bboxWest).geometry.coordinates as
|
|
94
|
+
| number[][]
|
|
95
|
+
| number[][][],
|
|
96
|
+
resolution,
|
|
97
|
+
true
|
|
98
|
+
).concat(
|
|
99
|
+
polygonToCells(
|
|
100
|
+
bboxClip(geometry, bboxEast).geometry.coordinates as
|
|
101
|
+
| number[][]
|
|
102
|
+
| number[][][],
|
|
103
|
+
resolution,
|
|
104
|
+
true
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getSpatialIndex(spatialDataType: SpatialDataType): SpatialIndex {
|
|
111
|
+
switch (spatialDataType) {
|
|
112
|
+
case 'h3':
|
|
113
|
+
return SpatialIndex.H3;
|
|
114
|
+
case 'quadbin':
|
|
115
|
+
return SpatialIndex.QUADBIN;
|
|
116
|
+
default:
|
|
117
|
+
throw new Error('Unexpected spatial data type');
|
|
118
|
+
}
|
|
119
|
+
}
|
package/src/filters.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {FilterType} from './constants';
|
|
2
|
-
import {Filter} from './types';
|
|
3
|
-
import {isEmptyObject} from './utils';
|
|
1
|
+
import {FilterType} from './constants.js';
|
|
2
|
+
import {Filter} from './types.js';
|
|
3
|
+
import {isEmptyObject} from './utils.js';
|
|
4
4
|
|
|
5
5
|
type FilterTypeOptions<T extends FilterType> = {
|
|
6
6
|
type: T;
|
|
@@ -121,7 +121,7 @@ export function getFilter<T extends FilterType>(
|
|
|
121
121
|
return null;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
if (!owner || owner === filter[type
|
|
124
|
+
if (!owner || owner === filter[type]?.owner) {
|
|
125
125
|
return filter[type] || null;
|
|
126
126
|
}
|
|
127
127
|
|
package/src/geo.ts
CHANGED
|
@@ -4,7 +4,7 @@ import union from '@turf/union';
|
|
|
4
4
|
import {getType} from '@turf/invariant';
|
|
5
5
|
import {polygon, multiPolygon, feature, featureCollection} from '@turf/helpers';
|
|
6
6
|
import type {BBox, Geometry, MultiPolygon, Polygon, Position} from 'geojson';
|
|
7
|
-
import {SpatialFilter} from './types';
|
|
7
|
+
import {SpatialFilter} from './types.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Returns a {@link SpatialFilter} for a given viewport, typically obtained
|
|
@@ -40,7 +40,7 @@ export function createPolygonSpatialFilter(
|
|
|
40
40
|
* Check if a viewport is large enough to represent a global coverage.
|
|
41
41
|
* In this case the spatial filter parameter for widget calculation is removed.
|
|
42
42
|
*
|
|
43
|
-
* @
|
|
43
|
+
* @privateRemarks Source: @carto/react-core
|
|
44
44
|
*/
|
|
45
45
|
function _isGlobalViewport(viewport: BBox) {
|
|
46
46
|
const [minx, miny, maxx, maxy] = viewport;
|
|
@@ -54,7 +54,7 @@ function _isGlobalViewport(viewport: BBox) {
|
|
|
54
54
|
*
|
|
55
55
|
* It results in a Polygon or MultiPolygon strictly inside the validity range.
|
|
56
56
|
*
|
|
57
|
-
* @
|
|
57
|
+
* @privateRemarks Source: @carto/react-core
|
|
58
58
|
*/
|
|
59
59
|
function _normalizeGeometry(
|
|
60
60
|
geometry: Polygon | MultiPolygon
|
|
@@ -100,19 +100,19 @@ function _normalizeGeometry(
|
|
|
100
100
|
return result;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/** @
|
|
103
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
104
104
|
function _cleanPolygonCoords(cc: Position[][]) {
|
|
105
105
|
const coords = cc.filter((c) => c.length > 0);
|
|
106
106
|
return coords.length > 0 ? coords : null;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/** @
|
|
109
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
110
110
|
function _cleanMultiPolygonCoords(ccc: Position[][][]) {
|
|
111
111
|
const coords = ccc.map(_cleanPolygonCoords).filter((cc) => cc);
|
|
112
112
|
return coords.length > 0 ? coords : null;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/** @
|
|
115
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
116
116
|
function _clean(
|
|
117
117
|
geometry: Polygon | MultiPolygon | null
|
|
118
118
|
): Polygon | MultiPolygon | null {
|
|
@@ -121,36 +121,34 @@ function _clean(
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
if (_isPolygon(geometry)) {
|
|
124
|
-
const coords = _cleanPolygonCoords(
|
|
124
|
+
const coords = _cleanPolygonCoords(geometry.coordinates);
|
|
125
125
|
return coords ? polygon(coords).geometry : null;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
if (_isMultiPolygon(geometry)) {
|
|
129
|
-
const coords = _cleanMultiPolygonCoords(
|
|
130
|
-
(geometry as MultiPolygon).coordinates
|
|
131
|
-
);
|
|
129
|
+
const coords = _cleanMultiPolygonCoords(geometry.coordinates);
|
|
132
130
|
return coords ? multiPolygon(coords as Position[][][]).geometry : null;
|
|
133
131
|
}
|
|
134
132
|
|
|
135
133
|
return null;
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
/** @
|
|
136
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
139
137
|
function _txContourCoords(cc: Position[], distance: number) {
|
|
140
138
|
return cc.map((c) => [c[0] + distance, c[1]]);
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
/** @
|
|
141
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
144
142
|
function _txPolygonCoords(ccc: Position[][], distance: number) {
|
|
145
143
|
return ccc.map((cc) => _txContourCoords(cc, distance));
|
|
146
144
|
}
|
|
147
145
|
|
|
148
|
-
/** @
|
|
146
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
149
147
|
function _txMultiPolygonCoords(cccc: Position[][][], distance: number) {
|
|
150
148
|
return cccc.map((ccc) => _txPolygonCoords(ccc, distance));
|
|
151
149
|
}
|
|
152
150
|
|
|
153
|
-
/** @
|
|
151
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
154
152
|
function _tx(geometry: Polygon | MultiPolygon, distance: number) {
|
|
155
153
|
if (geometry && getType(geometry) === 'Polygon') {
|
|
156
154
|
const coords = _txPolygonCoords(
|
package/src/global.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Injected by microbundle, with:
|
|
3
|
-
* ```
|
|
4
|
-
* --define __CARTO_API_CLIENT_VERSION=$npm_package_version
|
|
5
|
-
* ```
|
|
6
|
-
*/
|
|
7
|
-
declare const __CARTO_API_CLIENT_VERSION: string;
|
|
8
|
-
|
|
9
1
|
/** Defined by @deck.gl/core. */
|
|
10
2
|
declare const deck: {VERSION: string | undefined} | undefined;
|
|
3
|
+
|
|
4
|
+
/** Defined by tsup. */
|
|
5
|
+
declare const TSUP_FORMAT: 'esm' | 'cjs';
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './client.js';
|
|
2
2
|
export * from './constants.js';
|
|
3
|
+
export * from './deck/index.js';
|
|
3
4
|
export * from './filters.js';
|
|
4
5
|
export * from './geo.js';
|
|
5
6
|
export * from './sources/index.js';
|
|
@@ -18,3 +19,9 @@ export {
|
|
|
18
19
|
} from './api/index.js';
|
|
19
20
|
|
|
20
21
|
export {_getHexagonResolution} from './spatial-index.js';
|
|
22
|
+
|
|
23
|
+
// For unit testing only.
|
|
24
|
+
export * from './filters/index.js';
|
|
25
|
+
export * from './operations/index.js';
|
|
26
|
+
export * from './utils/makeIntervalComplete.js';
|
|
27
|
+
export * from './utils/transformToTileCoords.js';
|