@carto/api-client 0.5.0-alpha.13 → 0.5.0-alpha.15

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/build/api-client.cjs +9849 -2868
  3. package/build/api-client.cjs.map +1 -1
  4. package/build/api-client.d.cts +510 -171
  5. package/build/api-client.d.ts +510 -171
  6. package/build/api-client.js +9558 -2627
  7. package/build/api-client.js.map +1 -1
  8. package/build/worker.js +122 -469
  9. package/build/worker.js.map +1 -1
  10. package/package.json +36 -22
  11. package/src/api/query.ts +2 -1
  12. package/src/constants-internal.ts +10 -0
  13. package/src/constants.ts +5 -1
  14. package/src/fetch-map/basemap-styles.ts +159 -0
  15. package/src/fetch-map/basemap.ts +120 -0
  16. package/src/fetch-map/fetch-map.ts +331 -0
  17. package/src/fetch-map/index.ts +13 -0
  18. package/src/fetch-map/layer-map.ts +461 -0
  19. package/src/fetch-map/parse-map.ts +425 -0
  20. package/src/fetch-map/source.ts +233 -0
  21. package/src/fetch-map/types.ts +268 -0
  22. package/src/fetch-map/utils.ts +69 -0
  23. package/src/filters/tileFeatures.ts +26 -10
  24. package/src/filters/tileFeaturesRaster.ts +122 -0
  25. package/src/index.ts +1 -0
  26. package/src/models/model.ts +0 -7
  27. package/src/sources/base-source.ts +4 -2
  28. package/src/sources/h3-tileset-source.ts +1 -1
  29. package/src/sources/quadbin-tileset-source.ts +1 -1
  30. package/src/sources/raster-source.ts +18 -5
  31. package/src/sources/types.ts +14 -7
  32. package/src/sources/vector-tileset-source.ts +1 -1
  33. package/src/spatial-index.ts +3 -84
  34. package/src/types.ts +16 -2
  35. package/src/widget-sources/index.ts +1 -0
  36. package/src/widget-sources/types.ts +0 -2
  37. package/src/widget-sources/widget-raster-source.ts +14 -0
  38. package/src/widget-sources/widget-remote-source.ts +8 -76
  39. package/src/widget-sources/widget-source.ts +6 -24
  40. package/src/widget-sources/widget-tileset-source-impl.ts +13 -16
  41. package/src/widget-sources/widget-tileset-source.ts +20 -7
@@ -0,0 +1,425 @@
1
+ import type {ColorParameters} from '@luma.gl/core';
2
+ import {
3
+ AGGREGATION,
4
+ getLayerProps,
5
+ getColorAccessor,
6
+ getColorValueAccessor,
7
+ getSizeAccessor,
8
+ getTextAccessor,
9
+ OPACITY_MAP,
10
+ opacityToAlpha,
11
+ getIconUrlAccessor,
12
+ negateAccessor,
13
+ getMaxMarkerSize,
14
+ LayerType,
15
+ } from './layer-map.js';
16
+
17
+ import {assert, isEmptyObject} from '../utils.js';
18
+ import {Filters} from '../types.js';
19
+ import {
20
+ KeplerMapConfig,
21
+ MapLayerConfig,
22
+ VisualChannels,
23
+ VisConfig,
24
+ MapConfigLayer,
25
+ Dataset,
26
+ } from './types.js';
27
+ import {isRemoteCalculationSupported} from './utils.js';
28
+
29
+ export type LayerDescriptor = {
30
+ type: LayerType;
31
+ props: Record<string, any>;
32
+ filters?: Filters;
33
+ };
34
+
35
+ export type ParseMapResult = {
36
+ /** Map id. */
37
+ id: string;
38
+
39
+ /** Title of map. */
40
+ title: string;
41
+
42
+ /** Description of map. */
43
+ description?: string;
44
+ createdAt: string;
45
+ updatedAt: string;
46
+ initialViewState: any;
47
+
48
+ /** @deprecated Use `basemap`. */
49
+ mapStyle: any;
50
+ popupSettings: any;
51
+ token: string;
52
+
53
+ layers: LayerDescriptor[];
54
+ };
55
+
56
+ export function parseMap(json: any) {
57
+ const {keplerMapConfig, datasets, token} = json;
58
+ assert(keplerMapConfig.version === 'v1', 'Only support Kepler v1');
59
+ const config = keplerMapConfig.config as KeplerMapConfig;
60
+ const {filters, mapState, mapStyle, popupSettings} = config;
61
+ const {layers, layerBlending, interactionConfig} = config.visState;
62
+
63
+ return {
64
+ id: json.id,
65
+ title: json.title,
66
+ description: json.description,
67
+ createdAt: json.createdAt,
68
+ updatedAt: json.updatedAt,
69
+ initialViewState: mapState,
70
+ /** @deprecated Use `basemap`. */
71
+ mapStyle,
72
+ popupSettings,
73
+ token,
74
+ layers: layers
75
+ .reverse()
76
+ .map(({id, type, config, visualChannels}: MapConfigLayer) => {
77
+ try {
78
+ const {dataId} = config;
79
+ const dataset: Dataset | null = datasets.find(
80
+ (d: any) => d.id === dataId
81
+ );
82
+ assert(dataset, `No dataset matching dataId: ${dataId}`);
83
+ const {data} = dataset;
84
+ assert(data, `No data loaded for dataId: ${dataId}`);
85
+
86
+ const {propMap, defaultProps} = getLayerProps(type, config, dataset);
87
+
88
+ const styleProps = createStyleProps(config, propMap);
89
+
90
+ const layer: LayerDescriptor = {
91
+ type,
92
+ filters:
93
+ isEmptyObject(filters) || isRemoteCalculationSupported(dataset)
94
+ ? undefined
95
+ : filters[dataId],
96
+ props: {
97
+ id,
98
+ data,
99
+ ...defaultProps,
100
+ ...createInteractionProps(interactionConfig),
101
+ ...styleProps,
102
+ ...createChannelProps(id, type, config, visualChannels, data), // Must come after style
103
+ ...createParametersProp(
104
+ layerBlending,
105
+ styleProps.parameters || {}
106
+ ), // Must come after style
107
+ ...createLoadOptions(token),
108
+ },
109
+ };
110
+ return layer;
111
+ } catch (e: any) {
112
+ console.error(e.message);
113
+ return undefined;
114
+ }
115
+ }),
116
+ };
117
+ }
118
+
119
+ function createParametersProp(
120
+ layerBlending: string,
121
+ parameters: ColorParameters
122
+ ) {
123
+ if (layerBlending === 'additive') {
124
+ parameters.blendColorSrcFactor = parameters.blendAlphaSrcFactor =
125
+ 'src-alpha';
126
+ parameters.blendColorDstFactor = parameters.blendAlphaDstFactor =
127
+ 'dst-alpha';
128
+ parameters.blendColorOperation = parameters.blendAlphaOperation = 'add';
129
+ } else if (layerBlending === 'subtractive') {
130
+ parameters.blendColorSrcFactor = 'one';
131
+ parameters.blendColorDstFactor = 'one-minus-dst-color';
132
+ parameters.blendAlphaSrcFactor = 'src-alpha';
133
+ parameters.blendAlphaDstFactor = 'dst-alpha';
134
+ parameters.blendColorOperation = 'subtract';
135
+ parameters.blendAlphaOperation = 'add';
136
+ }
137
+
138
+ return Object.keys(parameters).length ? {parameters} : {};
139
+ }
140
+
141
+ function createInteractionProps(interactionConfig: any) {
142
+ const pickable = interactionConfig && interactionConfig.tooltip.enabled;
143
+ return {
144
+ autoHighlight: pickable,
145
+ pickable,
146
+ };
147
+ }
148
+
149
+ function mapProps(source: any, target: any, mapping: any) {
150
+ for (const sourceKey in mapping) {
151
+ const sourceValue = source[sourceKey];
152
+ const targetKey = mapping[sourceKey];
153
+ if (sourceValue === undefined) {
154
+ continue;
155
+ }
156
+ if (typeof targetKey === 'string') {
157
+ target[targetKey] = sourceValue;
158
+ } else if (typeof targetKey === 'function') {
159
+ const [key, value] = Object.entries(targetKey(sourceValue))[0];
160
+ target[key] = value;
161
+ } else if (typeof targetKey === 'object') {
162
+ // Nested definition, recurse down one level (also handles arrays)
163
+ mapProps(sourceValue, target, targetKey);
164
+ }
165
+ }
166
+ }
167
+
168
+ function createStyleProps(config: MapLayerConfig, mapping: any) {
169
+ const result: Record<string, any> = {};
170
+ mapProps(config, result, mapping);
171
+
172
+ // Kepler format sometimes omits strokeColor. TODO: remove once we can rely on
173
+ // `strokeColor` always being set when `stroke: true`.
174
+ if (result.stroked && !result.getLineColor) {
175
+ result.getLineColor = result.getFillColor;
176
+ }
177
+
178
+ for (const colorAccessor in OPACITY_MAP) {
179
+ if (Array.isArray(result[colorAccessor])) {
180
+ const color = [...result[colorAccessor]];
181
+ const opacityKey = OPACITY_MAP[colorAccessor];
182
+ const opacity = config.visConfig[opacityKey as keyof VisConfig];
183
+ color[3] = opacityToAlpha(opacity);
184
+ result[colorAccessor] = color;
185
+ }
186
+ }
187
+
188
+ result.highlightColor = config.visConfig.enable3d
189
+ ? [255, 255, 255, 60]
190
+ : [252, 242, 26, 255];
191
+ return result;
192
+ }
193
+
194
+ function createChannelProps(
195
+ id: string,
196
+ type: string,
197
+ config: MapLayerConfig,
198
+ visualChannels: VisualChannels,
199
+ data: any
200
+ ) {
201
+ const {
202
+ colorField,
203
+ colorScale,
204
+ radiusField,
205
+ radiusScale,
206
+ sizeField,
207
+ sizeScale,
208
+ strokeColorField,
209
+ strokeColorScale,
210
+ weightField,
211
+ } = visualChannels;
212
+ let {heightField, heightScale} = visualChannels;
213
+ if (type === 'hexagonId') {
214
+ heightField = sizeField;
215
+ heightScale = sizeScale;
216
+ }
217
+ const {textLabel, visConfig} = config;
218
+ const result: Record<string, any> = {};
219
+
220
+ if (type === 'grid' || type === 'hexagon') {
221
+ result.colorScaleType = colorScale;
222
+ if (colorField) {
223
+ const {colorAggregation} = config.visConfig;
224
+ if (!AGGREGATION[colorAggregation]) {
225
+ result.getColorValue = getColorValueAccessor(
226
+ colorField,
227
+ colorAggregation,
228
+ data
229
+ );
230
+ } else {
231
+ result.getColorWeight = (d: any) => d[colorField.name];
232
+ }
233
+ }
234
+ } else if (colorField) {
235
+ const {colorAggregation: aggregation, colorRange: range} = visConfig;
236
+ result.getFillColor = getColorAccessor(
237
+ colorField,
238
+ // @ts-ignore
239
+ colorScale,
240
+ {aggregation, range},
241
+ visConfig.opacity,
242
+ data
243
+ );
244
+ }
245
+
246
+ if (type === 'point') {
247
+ const altitude = config.columns?.altitude;
248
+ if (altitude) {
249
+ result.dataTransform = (data: any) => {
250
+ data.features.forEach(
251
+ ({geometry, properties}: {geometry: any; properties: any}) => {
252
+ const {type, coordinates} = geometry;
253
+ if (type === 'Point') {
254
+ coordinates[2] = properties[altitude];
255
+ }
256
+ }
257
+ );
258
+ return data;
259
+ };
260
+ }
261
+ }
262
+
263
+ if (radiusField || sizeField) {
264
+ result.getPointRadius = getSizeAccessor(
265
+ // @ts-ignore
266
+ radiusField || sizeField,
267
+ // @ts-ignore
268
+ radiusScale || sizeScale,
269
+ visConfig.sizeAggregation,
270
+ visConfig.radiusRange || visConfig.sizeRange,
271
+ data
272
+ );
273
+ }
274
+
275
+ if (strokeColorField) {
276
+ const fallbackOpacity = type === 'point' ? visConfig.opacity : 1;
277
+ const opacity =
278
+ visConfig.strokeOpacity !== undefined
279
+ ? visConfig.strokeOpacity
280
+ : fallbackOpacity;
281
+ const {strokeColorAggregation: aggregation, strokeColorRange: range} =
282
+ visConfig;
283
+ result.getLineColor = getColorAccessor(
284
+ strokeColorField,
285
+ // @ts-ignore
286
+ strokeColorScale,
287
+ // @ts-ignore
288
+ {aggregation, range},
289
+ opacity,
290
+ data
291
+ );
292
+ }
293
+ if (heightField && visConfig.enable3d) {
294
+ result.getElevation = getSizeAccessor(
295
+ heightField,
296
+ // @ts-ignore
297
+ heightScale,
298
+ visConfig.heightAggregation,
299
+ visConfig.heightRange || visConfig.sizeRange,
300
+ data
301
+ );
302
+ }
303
+
304
+ if (weightField) {
305
+ result.getWeight = getSizeAccessor(
306
+ weightField,
307
+ undefined,
308
+ visConfig.weightAggregation,
309
+ undefined,
310
+ data
311
+ );
312
+ }
313
+
314
+ if (visConfig.customMarkers) {
315
+ const maxIconSize = getMaxMarkerSize(visConfig, visualChannels);
316
+ const {getPointRadius, getFillColor} = result;
317
+ const {
318
+ customMarkersUrl,
319
+ customMarkersRange,
320
+ filled: useMaskedIcons,
321
+ } = visConfig;
322
+
323
+ result.pointType = 'icon';
324
+ result.getIcon = getIconUrlAccessor(
325
+ visualChannels.customMarkersField,
326
+ customMarkersRange,
327
+ {fallbackUrl: customMarkersUrl, maxIconSize, useMaskedIcons},
328
+ data
329
+ );
330
+ result._subLayerProps = {
331
+ 'points-icon': {
332
+ loadOptions: {
333
+ image: {
334
+ type: 'imagebitmap',
335
+ },
336
+ imagebitmap: {
337
+ resizeWidth: maxIconSize,
338
+ resizeHeight: maxIconSize,
339
+ resizeQuality: 'high',
340
+ },
341
+ },
342
+ },
343
+ };
344
+
345
+ if (getFillColor && useMaskedIcons) {
346
+ result.getIconColor = getFillColor;
347
+ }
348
+
349
+ if (getPointRadius) {
350
+ result.getIconSize = getPointRadius;
351
+ }
352
+
353
+ if (visualChannels.rotationField) {
354
+ result.getIconAngle = negateAccessor(
355
+ getSizeAccessor(
356
+ visualChannels.rotationField,
357
+ undefined,
358
+ null,
359
+ undefined,
360
+ data
361
+ )
362
+ );
363
+ }
364
+ } else if (type === 'point' || type === 'tileset') {
365
+ result.pointType = 'circle';
366
+ }
367
+
368
+ if (textLabel && textLabel.length && textLabel[0].field) {
369
+ const [mainLabel, secondaryLabel] = textLabel;
370
+ const collisionGroup = id;
371
+
372
+ ({
373
+ alignment: result.getTextAlignmentBaseline,
374
+ anchor: result.getTextAnchor,
375
+ color: result.getTextColor,
376
+ outlineColor: result.textOutlineColor,
377
+ size: result.textSizeScale,
378
+ } = mainLabel);
379
+ const {
380
+ color: getSecondaryColor,
381
+ field: secondaryField,
382
+ outlineColor: secondaryOutlineColor,
383
+ size: secondarySizeScale,
384
+ } = secondaryLabel || {};
385
+
386
+ result.getText = mainLabel.field && getTextAccessor(mainLabel.field, data);
387
+ const getSecondaryText =
388
+ secondaryField && getTextAccessor(secondaryField, data);
389
+
390
+ result.pointType = `${result.pointType}+text`;
391
+ result.textCharacterSet = 'auto';
392
+ result.textFontFamily = 'Inter, sans';
393
+ result.textFontSettings = {sdf: true};
394
+ result.textFontWeight = 600;
395
+ result.textOutlineWidth = 3;
396
+
397
+ result._subLayerProps = {
398
+ ...result._subLayerProps,
399
+ 'points-text': {
400
+ collisionEnabled: true,
401
+ collisionGroup,
402
+
403
+ // getPointRadius already has radiusScale baked in, so only pass one or the other
404
+ ...(result.getPointRadius
405
+ ? {getRadius: result.getPointRadius}
406
+ : {radiusScale: visConfig.radius}),
407
+
408
+ ...(secondaryField && {
409
+ getSecondaryText,
410
+ getSecondaryColor,
411
+ secondarySizeScale,
412
+ secondaryOutlineColor,
413
+ }),
414
+ },
415
+ };
416
+ }
417
+
418
+ return result;
419
+ }
420
+
421
+ function createLoadOptions(accessToken: string) {
422
+ return {
423
+ loadOptions: {fetch: {headers: {Authorization: `Bearer ${accessToken}`}}},
424
+ };
425
+ }
@@ -0,0 +1,233 @@
1
+ import {Dataset} from './types.js';
2
+ import {SpatialIndex, SpatialIndexColumn} from '../constants.js';
3
+ import {
4
+ QuerySourceOptions,
5
+ TableSourceOptions,
6
+ TilejsonResult,
7
+ TileResolution,
8
+ } from '../sources/types.js';
9
+ import {
10
+ DEFAULT_AGGREGATION_EXP,
11
+ DEFAULT_AGGREGATION_RES_LEVEL_H3,
12
+ DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
13
+ DEFAULT_TILE_RESOLUTION,
14
+ REDUCED_QUERIES_TILE_RESOLUTION,
15
+ } from '../constants-internal.js';
16
+ import {
17
+ h3QuerySource,
18
+ H3QuerySourceOptions,
19
+ h3TableSource,
20
+ H3TableSourceOptions,
21
+ quadbinQuerySource,
22
+ QuadbinQuerySourceOptions,
23
+ quadbinTableSource,
24
+ QuadbinTableSourceOptions,
25
+ rasterSource,
26
+ vectorQuerySource,
27
+ VectorQuerySourceOptions,
28
+ vectorTableSource,
29
+ VectorTableSourceOptions,
30
+ vectorTilesetSource,
31
+ VectorTilesetSourceOptions,
32
+ } from '../sources/index.js';
33
+ import {Filter} from '../types.js';
34
+
35
+ type FetchDatasetOptions = {
36
+ accessToken: string;
37
+ apiBaseUrl: string;
38
+ connection: string;
39
+ headers?: Record<string, string>;
40
+ localCache?: {
41
+ cacheControl: 'no-cache'[];
42
+ };
43
+ maxLengthURL?: number;
44
+ };
45
+
46
+ type FetchDataset = {
47
+ dataset: Dataset;
48
+ filters?: Filter;
49
+ options: FetchDatasetOptions;
50
+ };
51
+
52
+ // Copy of getCartoSource from cloud-native:
53
+ // https://github.com/CartoDB/cloud-native/blob/main/workspace-www/src/features/common/utils/cartoDeckGL.ts#L79
54
+ export function configureSource({
55
+ dataset,
56
+ filters,
57
+ options,
58
+ }: FetchDataset): Promise<TilejsonResult> {
59
+ const {
60
+ geoColumn,
61
+ columns,
62
+ type,
63
+ source,
64
+ queryParameters,
65
+ aggregationExp,
66
+ aggregationResLevel: originalAggregationResLevel,
67
+ } = dataset;
68
+ const sourceOptions = getSourceOptions(options);
69
+ const spatialDataColumn = getColumnNameFromGeoColumn(geoColumn) || undefined;
70
+ const spatialIndex = geoColumn
71
+ ? getSpatialIndexFromGeoColumn(geoColumn)
72
+ : undefined;
73
+ const tileResolution = getDynamicTileResolution(spatialIndex);
74
+ const isH3 = spatialIndex === SpatialIndex.H3;
75
+ const isQuadbin = spatialIndex === SpatialIndex.QUADBIN;
76
+ let aggregationResLevel = originalAggregationResLevel;
77
+
78
+ if (typeof originalAggregationResLevel !== 'number' && isH3) {
79
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3;
80
+ } else if (typeof originalAggregationResLevel !== 'number' && isQuadbin) {
81
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN;
82
+ }
83
+
84
+ const spatialIndexOptions = {
85
+ aggregationExp: !aggregationExp ? DEFAULT_AGGREGATION_EXP : aggregationExp,
86
+ aggregationResLevel: scaleAggregationResLevel(
87
+ aggregationResLevel,
88
+ tileResolution
89
+ ),
90
+ spatialDataColumn,
91
+ ...(filters && {filters}),
92
+ } as H3QuerySourceOptions;
93
+ const tilesetOptions = {
94
+ ...sourceOptions,
95
+ tableName: source,
96
+ } as VectorTilesetSourceOptions;
97
+ const tableOptions = {
98
+ ...sourceOptions,
99
+ tableName: source,
100
+ tileResolution,
101
+ } as TableSourceOptions;
102
+ const queryOptions = {
103
+ ...sourceOptions,
104
+ sqlQuery: source,
105
+ tileResolution,
106
+ ...(queryParameters && {queryParameters}),
107
+ } as QuerySourceOptions;
108
+ const vectorOptions = {
109
+ spatialDataColumn,
110
+ ...(columns && {columns}),
111
+ ...(filters && {filters}),
112
+ ...(aggregationExp && {aggregationExp}),
113
+ } as VectorTableSourceOptions;
114
+
115
+ if (type === 'raster') {
116
+ return rasterSource({
117
+ ...sourceOptions,
118
+ tableName: source,
119
+ ...(filters && {filters: filters as any}),
120
+ });
121
+ }
122
+ if (type === 'tileset') {
123
+ return vectorTilesetSource({...tilesetOptions});
124
+ }
125
+
126
+ if (type === 'table') {
127
+ if (isH3) {
128
+ return h3TableSource({
129
+ ...(tableOptions as H3TableSourceOptions),
130
+ ...spatialIndexOptions,
131
+ });
132
+ } else if (isQuadbin) {
133
+ return quadbinTableSource({
134
+ ...(tableOptions as QuadbinTableSourceOptions),
135
+ ...spatialIndexOptions,
136
+ });
137
+ } else {
138
+ return vectorTableSource({
139
+ ...(tableOptions as VectorTableSourceOptions),
140
+ ...vectorOptions,
141
+ });
142
+ }
143
+ }
144
+
145
+ if (type === 'query') {
146
+ if (isH3) {
147
+ return h3QuerySource({
148
+ ...(queryOptions as H3QuerySourceOptions),
149
+ ...spatialIndexOptions,
150
+ });
151
+ } else if (isQuadbin) {
152
+ return quadbinQuerySource({
153
+ ...(queryOptions as QuadbinQuerySourceOptions),
154
+ ...spatialIndexOptions,
155
+ });
156
+ } else {
157
+ return vectorQuerySource({
158
+ ...(queryOptions as VectorQuerySourceOptions),
159
+ ...vectorOptions,
160
+ });
161
+ }
162
+ }
163
+ throw new Error(`Invalid source type: ${type}`);
164
+ }
165
+
166
+ function getSourceOptions({
167
+ accessToken,
168
+ apiBaseUrl,
169
+ connection,
170
+ headers,
171
+ maxLengthURL,
172
+ }: FetchDatasetOptions) {
173
+ return {
174
+ accessToken,
175
+ connectionName: connection,
176
+ apiBaseUrl,
177
+ headers,
178
+ maxLengthURL,
179
+ ...(headers?.['Cache-Control']?.includes('no-cache') && {
180
+ localCache: {
181
+ cacheControl: ['no-cache'] as 'no-cache'[],
182
+ },
183
+ }),
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Returns default tile resolution for dynamic tilesets, based on layer configuration
189
+ * Result is not applicable for static tilesets, for which tile resolution is defined
190
+ * by tilejson.
191
+ */
192
+ function getDynamicTileResolution(
193
+ spatialIndex?: SpatialIndex | null
194
+ ): TileResolution {
195
+ // TODO: Support increased tile size and resolution for dynamic H3 spatial indexes.
196
+ if (spatialIndex !== SpatialIndex.H3) {
197
+ return REDUCED_QUERIES_TILE_RESOLUTION;
198
+ }
199
+
200
+ return DEFAULT_TILE_RESOLUTION;
201
+ }
202
+
203
+ /**
204
+ * State of `aggregationResLevel` in the UI and backend config is based on an assumption of
205
+ * 512x512px tiles. Because we may change tile resolution for performance goals, the
206
+ * `aggregationResLevel` passed to the deck.gl layer must be scaled with tile resolution.
207
+ */
208
+ function scaleAggregationResLevel(
209
+ aggregationResLevel: number,
210
+ tileResolution: number
211
+ ): number | undefined {
212
+ if (typeof aggregationResLevel !== 'number') return;
213
+ return aggregationResLevel - Math.log2(0.5 / tileResolution);
214
+ }
215
+
216
+ function getColumnNameFromGeoColumn(geoColumn: string | null | undefined) {
217
+ if (!geoColumn) {
218
+ return geoColumn;
219
+ }
220
+ const parts = geoColumn.split(':');
221
+ return parts.length === 1 ? parts[0] : parts.length === 2 ? parts[1] : null;
222
+ }
223
+
224
+ function getSpatialIndexFromGeoColumn(geoColumn: string) {
225
+ const spatialIndexToSearch = geoColumn.split(':')[0];
226
+
227
+ for (const index of Object.values(SpatialIndex)) {
228
+ if (SpatialIndexColumn[index].includes(spatialIndexToSearch)) {
229
+ return index;
230
+ }
231
+ }
232
+ return null;
233
+ }