@carto/api-client 0.5.27 → 0.5.29
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 +11 -0
- package/build/api-client.cjs +63 -6
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.d.cts +24 -2
- package/build/api-client.d.ts +24 -2
- package/build/api-client.js +63 -6
- package/build/api-client.js.map +1 -1
- package/package.json +1 -1
- package/src/fetch-map/fetch-map.ts +22 -0
- package/src/fetch-map/layer-map.ts +3 -2
- package/src/fetch-map/parse-map.ts +51 -1
- package/src/fetch-map/source.ts +2 -0
- package/src/fetch-map/types.ts +7 -0
- package/src/sources/vector-query-source.ts +14 -1
- package/src/sources/vector-table-source.ts +14 -1
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"homepage": "https://github.com/CartoDB/carto-api-client#readme",
|
|
9
9
|
"author": "Don McCurdy <donmccurdy@carto.com>",
|
|
10
10
|
"packageManager": "yarn@4.3.1",
|
|
11
|
-
"version": "0.5.
|
|
11
|
+
"version": "0.5.29",
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"publishConfig": {
|
|
14
14
|
"access": "public"
|
|
@@ -302,6 +302,28 @@ export async function fetchMap({
|
|
|
302
302
|
}
|
|
303
303
|
});
|
|
304
304
|
|
|
305
|
+
// Flag datasets that need feature bounding boxes for label positioning.
|
|
306
|
+
// Some data waraehouse (notably when using MVT) only support clipped
|
|
307
|
+
// geometry. The server-provided bbox enables stable label placement.
|
|
308
|
+
const layers = map.keplerMapConfig.config.visState.layers;
|
|
309
|
+
const datasetsWithLabels = new Set<string>();
|
|
310
|
+
for (const layer of layers) {
|
|
311
|
+
const hasTextLabel = layer.config?.textLabel?.some(
|
|
312
|
+
(t: any) => t.field?.name
|
|
313
|
+
);
|
|
314
|
+
if (hasTextLabel) {
|
|
315
|
+
datasetsWithLabels.add(layer.config.dataId);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
map.datasets.forEach((dataset: any) => {
|
|
319
|
+
if (
|
|
320
|
+
datasetsWithLabels.has(dataset.id) &&
|
|
321
|
+
(dataset.type === 'table' || dataset.type === 'query')
|
|
322
|
+
) {
|
|
323
|
+
dataset.featureBbox = true;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
305
327
|
const [basemap] = await Promise.all([
|
|
306
328
|
fetchBasemapProps({config: map.keplerMapConfig.config, errorContext}),
|
|
307
329
|
|
|
@@ -514,10 +514,11 @@ export function getMaxMarkerSize(
|
|
|
514
514
|
visConfig: VisConfig,
|
|
515
515
|
visualChannels: VisualChannels
|
|
516
516
|
): number {
|
|
517
|
-
const {radiusRange, radius} = visConfig;
|
|
517
|
+
const {radiusRange, radius, sizeMaxPixels} = visConfig;
|
|
518
518
|
const {radiusField, sizeField} = visualChannels;
|
|
519
519
|
const field = radiusField || sizeField;
|
|
520
|
-
|
|
520
|
+
const baseSize = radiusRange && field ? radiusRange[1] : radius;
|
|
521
|
+
return Math.ceil(sizeMaxPixels ?? baseSize);
|
|
521
522
|
}
|
|
522
523
|
|
|
523
524
|
type Accessor = number | ((d: any, i: any) => number);
|
|
@@ -128,6 +128,7 @@ export function getLayerDescriptor({
|
|
|
128
128
|
...createInteractionProps(interactionConfig),
|
|
129
129
|
...styleProps,
|
|
130
130
|
...channelProps,
|
|
131
|
+
...createZoomScaleProps(config, visualChannels),
|
|
131
132
|
...createParametersProp(layerBlending, styleProps.parameters || {}), // Must come after style
|
|
132
133
|
...createLoadOptions(data.accessToken),
|
|
133
134
|
},
|
|
@@ -208,6 +209,38 @@ function createInteractionProps(interactionConfig: any) {
|
|
|
208
209
|
};
|
|
209
210
|
}
|
|
210
211
|
|
|
212
|
+
function createZoomScaleProps(
|
|
213
|
+
config: MapLayerConfig,
|
|
214
|
+
visualChannels: VisualChannels
|
|
215
|
+
): Record<string, any> {
|
|
216
|
+
const {visConfig} = config;
|
|
217
|
+
if (
|
|
218
|
+
!visConfig.radiusScaleWithZoom ||
|
|
219
|
+
visualChannels.radiusField ||
|
|
220
|
+
visualChannels.sizeField
|
|
221
|
+
) {
|
|
222
|
+
return {};
|
|
223
|
+
}
|
|
224
|
+
// When `radiusScaleWithZoom` is enabled, render the point in `common`
|
|
225
|
+
// coordinate space so it scales proportionally with zoom.
|
|
226
|
+
const scale = Math.pow(2, -(visConfig.radiusReferenceZoom as number));
|
|
227
|
+
const result: Record<string, any> = {
|
|
228
|
+
pointRadiusUnits: 'common',
|
|
229
|
+
pointRadiusScale: scale,
|
|
230
|
+
iconSizeUnits: 'common',
|
|
231
|
+
iconSizeScale: scale,
|
|
232
|
+
};
|
|
233
|
+
if (visConfig.sizeMinPixels !== undefined) {
|
|
234
|
+
result.pointRadiusMinPixels = visConfig.sizeMinPixels;
|
|
235
|
+
result.iconSizeMinPixels = visConfig.sizeMinPixels;
|
|
236
|
+
}
|
|
237
|
+
if (visConfig.sizeMaxPixels !== undefined) {
|
|
238
|
+
result.pointRadiusMaxPixels = visConfig.sizeMaxPixels;
|
|
239
|
+
result.iconSizeMaxPixels = visConfig.sizeMaxPixels;
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
|
|
211
244
|
function mapProps(source: any, target: any, mapping: any) {
|
|
212
245
|
for (const sourceKey in mapping) {
|
|
213
246
|
const sourceValue = source[sourceKey];
|
|
@@ -667,7 +700,24 @@ function createChannelProps(
|
|
|
667
700
|
const getSecondaryText =
|
|
668
701
|
secondaryField && getTextAccessor(secondaryField, data);
|
|
669
702
|
|
|
670
|
-
|
|
703
|
+
// For line/polygon tileset layers, deck.gl's VectorTileLayer can synthesize
|
|
704
|
+
// point labels at line midpoints / polygon centroids via `autoLabels`. The
|
|
705
|
+
// optional `uniqueIdProperty` dedupes features that span multiple tiles so
|
|
706
|
+
// each feature gets one label instead of one-per-tile.
|
|
707
|
+
const geometry = data.tilestats?.layers?.[0]?.geometry;
|
|
708
|
+
const isLineOrPolygon =
|
|
709
|
+
geometry === 'Polygon' ||
|
|
710
|
+
geometry === 'MultiPolygon' ||
|
|
711
|
+
geometry === 'Line' ||
|
|
712
|
+
geometry === 'LineString' ||
|
|
713
|
+
geometry === 'MultiLineString';
|
|
714
|
+
if (isLineOrPolygon && (layerType === 'tileset' || layerType === 'mvt')) {
|
|
715
|
+
const uniqueIdProperty = visConfig.textLabelUniqueIdField;
|
|
716
|
+
result.autoLabels = uniqueIdProperty ? {uniqueIdProperty} : true;
|
|
717
|
+
result.pointType = 'text';
|
|
718
|
+
} else {
|
|
719
|
+
result.pointType = `${result.pointType}+text`;
|
|
720
|
+
}
|
|
671
721
|
result.textCharacterSet = 'auto';
|
|
672
722
|
result.textFontFamily = 'Inter, sans';
|
|
673
723
|
result.textFontSettings = {sdf: true};
|
package/src/fetch-map/source.ts
CHANGED
|
@@ -106,11 +106,13 @@ export function configureSource({
|
|
|
106
106
|
tileResolution,
|
|
107
107
|
...(queryParameters && {queryParameters}),
|
|
108
108
|
} as QuerySourceOptions;
|
|
109
|
+
const {featureBbox} = dataset;
|
|
109
110
|
const vectorOptions = {
|
|
110
111
|
spatialDataColumn,
|
|
111
112
|
...(columns && {columns}),
|
|
112
113
|
...(filters && {filters}),
|
|
113
114
|
...(aggregationExp && {aggregationExp}),
|
|
115
|
+
...(featureBbox && {featureBbox}),
|
|
114
116
|
} as VectorTableSourceOptions;
|
|
115
117
|
|
|
116
118
|
if (type === 'raster') {
|
package/src/fetch-map/types.ts
CHANGED
|
@@ -80,6 +80,11 @@ export type VisConfig = {
|
|
|
80
80
|
radiusAggregationExp?: string;
|
|
81
81
|
radiusAggregationDomain?: [number, number];
|
|
82
82
|
|
|
83
|
+
sizeMinPixels?: number;
|
|
84
|
+
sizeMaxPixels?: number;
|
|
85
|
+
radiusScaleWithZoom?: boolean;
|
|
86
|
+
radiusReferenceZoom?: number;
|
|
87
|
+
|
|
83
88
|
sizeAggregation?: string;
|
|
84
89
|
sizeAggregationExp?: string;
|
|
85
90
|
sizeAggregationDomain?: [number, number];
|
|
@@ -108,6 +113,7 @@ export type VisConfig = {
|
|
|
108
113
|
colorBands?: RasterLayerConfigColorBand[];
|
|
109
114
|
|
|
110
115
|
uniqueValuesColorRange?: ColorRange;
|
|
116
|
+
textLabelUniqueIdField?: string | null;
|
|
111
117
|
};
|
|
112
118
|
|
|
113
119
|
export type TextLabel = {
|
|
@@ -259,6 +265,7 @@ export type Dataset = {
|
|
|
259
265
|
name?: string | null;
|
|
260
266
|
spatialIndex?: string | null;
|
|
261
267
|
exportToBucketAvailable?: boolean;
|
|
268
|
+
featureBbox?: boolean;
|
|
262
269
|
};
|
|
263
270
|
|
|
264
271
|
export type AttributeType = 'String' | 'Number' | 'Timestamp' | 'Boolean';
|
|
@@ -23,7 +23,15 @@ import type {
|
|
|
23
23
|
export type VectorQuerySourceOptions = SourceOptions &
|
|
24
24
|
QuerySourceOptions &
|
|
25
25
|
FilterOptions &
|
|
26
|
-
ColumnsOption
|
|
26
|
+
ColumnsOption & {
|
|
27
|
+
/**
|
|
28
|
+
* If `true`, the server includes a `_carto_bbox` property on each polygon
|
|
29
|
+
* feature, containing the bounding box of the full (unclipped) geometry as
|
|
30
|
+
* a `"west,south,east,north"` string in WGS84. Used by clients to compute
|
|
31
|
+
* stable label positions for polygons that span multiple tiles.
|
|
32
|
+
*/
|
|
33
|
+
featureBbox?: boolean;
|
|
34
|
+
};
|
|
27
35
|
|
|
28
36
|
type UrlParameters = {
|
|
29
37
|
columns?: string;
|
|
@@ -34,6 +42,7 @@ type UrlParameters = {
|
|
|
34
42
|
q: string;
|
|
35
43
|
queryParameters?: Record<string, unknown> | unknown[];
|
|
36
44
|
aggregationExp?: string;
|
|
45
|
+
featureBbox?: boolean;
|
|
37
46
|
};
|
|
38
47
|
|
|
39
48
|
export type VectorQuerySourceResponse = TilejsonResult &
|
|
@@ -50,6 +59,7 @@ export const vectorQuerySource = async function (
|
|
|
50
59
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
51
60
|
queryParameters,
|
|
52
61
|
aggregationExp,
|
|
62
|
+
featureBbox,
|
|
53
63
|
} = options;
|
|
54
64
|
|
|
55
65
|
const spatialDataType = 'geo';
|
|
@@ -73,6 +83,9 @@ export const vectorQuerySource = async function (
|
|
|
73
83
|
if (aggregationExp) {
|
|
74
84
|
urlParameters.aggregationExp = aggregationExp;
|
|
75
85
|
}
|
|
86
|
+
if (featureBbox) {
|
|
87
|
+
urlParameters.featureBbox = true;
|
|
88
|
+
}
|
|
76
89
|
|
|
77
90
|
return baseSource<UrlParameters>('query', options, urlParameters).then(
|
|
78
91
|
(result) => ({
|
|
@@ -23,7 +23,15 @@ import type {
|
|
|
23
23
|
export type VectorTableSourceOptions = SourceOptions &
|
|
24
24
|
TableSourceOptions &
|
|
25
25
|
FilterOptions &
|
|
26
|
-
ColumnsOption
|
|
26
|
+
ColumnsOption & {
|
|
27
|
+
/**
|
|
28
|
+
* If `true`, the server includes a `_carto_bbox` property on each polygon
|
|
29
|
+
* feature, containing the bounding box of the full (unclipped) geometry as
|
|
30
|
+
* a `"west,south,east,north"` string in WGS84. Used by clients to compute
|
|
31
|
+
* stable label positions for polygons that span multiple tiles.
|
|
32
|
+
*/
|
|
33
|
+
featureBbox?: boolean;
|
|
34
|
+
};
|
|
27
35
|
|
|
28
36
|
type UrlParameters = {
|
|
29
37
|
columns?: string;
|
|
@@ -33,6 +41,7 @@ type UrlParameters = {
|
|
|
33
41
|
tileResolution?: string;
|
|
34
42
|
name: string;
|
|
35
43
|
aggregationExp?: string;
|
|
44
|
+
featureBbox?: boolean;
|
|
36
45
|
};
|
|
37
46
|
|
|
38
47
|
export type VectorTableSourceResponse = TilejsonResult &
|
|
@@ -48,6 +57,7 @@ export const vectorTableSource = async function (
|
|
|
48
57
|
tableName,
|
|
49
58
|
tileResolution = DEFAULT_TILE_RESOLUTION,
|
|
50
59
|
aggregationExp,
|
|
60
|
+
featureBbox,
|
|
51
61
|
} = options;
|
|
52
62
|
|
|
53
63
|
const spatialDataType = 'geo';
|
|
@@ -68,6 +78,9 @@ export const vectorTableSource = async function (
|
|
|
68
78
|
if (aggregationExp) {
|
|
69
79
|
urlParameters.aggregationExp = aggregationExp;
|
|
70
80
|
}
|
|
81
|
+
if (featureBbox) {
|
|
82
|
+
urlParameters.featureBbox = true;
|
|
83
|
+
}
|
|
71
84
|
|
|
72
85
|
return baseSource<UrlParameters>('table', options, urlParameters).then(
|
|
73
86
|
(result) => ({
|