@carto/api-client 0.5.31 → 0.5.32-alpha.307c0b4.122

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/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.31",
11
+ "version": "0.5.32-alpha.307c0b4.122",
12
12
  "license": "MIT",
13
13
  "publishConfig": {
14
14
  "access": "public"
@@ -125,7 +125,7 @@
125
125
  "typescript": "~5.9.2",
126
126
  "typescript-eslint": "^8.26.1",
127
127
  "vite": "^7.3.2",
128
- "vitest": "3.2.4"
128
+ "vitest": "3.2.6"
129
129
  },
130
130
  "resolutions": {
131
131
  "@carto/api-client": "portal:./",
@@ -32,6 +32,7 @@ import type {
32
32
  ColorRange,
33
33
  CustomMarkersRange,
34
34
  Dataset,
35
+ LineStyleRange,
35
36
  MapLayerConfig,
36
37
  VisConfig,
37
38
  VisualChannelField,
@@ -510,6 +511,25 @@ export function getIconUrlAccessor(
510
511
  return normalizeAccessor(accessor, data);
511
512
  }
512
513
 
514
+ export function getLineStyleAccessor(
515
+ field: VisualChannelField,
516
+ range: LineStyleRange,
517
+ data: any
518
+ ) {
519
+ const fallback = range.othersDashArray ?? [0, 0];
520
+ const mapping: Record<string, [number, number]> = {};
521
+ for (const {value, dashArray} of range.dashArrayMap) {
522
+ mapping[value] = dashArray;
523
+ }
524
+ const accessor = (properties: any) =>
525
+ mapping[properties[field.name]] ?? fallback;
526
+ return {
527
+ accessor: normalizeAccessor(accessor, data),
528
+ domain: range.dashArrayMap.map(({value}) => value),
529
+ range: range.dashArrayMap.map(({dashArray}) => dashArray),
530
+ };
531
+ }
532
+
513
533
  export function getMaxMarkerSize(
514
534
  visConfig: VisConfig,
515
535
  visualChannels: VisualChannels
@@ -10,6 +10,7 @@ import {
10
10
  getTextAccessor,
11
11
  opacityToAlpha,
12
12
  getIconUrlAccessor,
13
+ getLineStyleAccessor,
13
14
  negateAccessor,
14
15
  getMaxMarkerSize,
15
16
  type LayerType,
@@ -48,7 +49,8 @@ export type Scale = {
48
49
 
49
50
  /** Domain of the user to construct d3 scale */
50
51
  scaleDomain?: string[] | number[];
51
- range?: string[] | number[];
52
+ // `number[][]` carries per-category `[dash, gap]` tuples for the `lineStyle` scale.
53
+ range?: string[] | number[] | number[][];
52
54
  };
53
55
 
54
56
  export type ScaleKey =
@@ -57,7 +59,8 @@ export type ScaleKey =
57
59
  | 'lineColor'
58
60
  | 'lineWidth'
59
61
  | 'elevation'
60
- | 'weight';
62
+ | 'weight'
63
+ | 'lineStyle';
61
64
 
62
65
  export type Scales = Partial<Record<ScaleKey, Scale>>;
63
66
 
@@ -377,6 +380,14 @@ function createChannelProps(
377
380
 
378
381
  const scales: Record<string, Scale> = {};
379
382
 
383
+ const isVectorTile = layerType === 'mvt' || layerType === 'tileset';
384
+ const geometry = data.tilestats?.layers?.[0]?.geometry;
385
+ const isLine =
386
+ geometry === 'Line' ||
387
+ geometry === 'LineString' ||
388
+ geometry === 'MultiLineString';
389
+ const isPolygon = geometry === 'Polygon' || geometry === 'MultiPolygon';
390
+
380
391
  // fill color
381
392
  {
382
393
  const {colorField, colorScale} = visualChannels;
@@ -559,6 +570,54 @@ function createChannelProps(
559
570
  }
560
571
  }
561
572
 
573
+ // stroke dash style — only VectorTileLayer (mvt/tileset) carries dashes; H3/Quadbin excluded.
574
+ {
575
+ // A line is always stroked; a polygon border only when `stroked`. Points are excluded.
576
+ const strokeVisible = isLine || (isPolygon && Boolean(visConfig.stroked));
577
+ if (isVectorTile && strokeVisible) {
578
+ const {lineStyleField, lineStyleScale} = visualChannels;
579
+ const {lineStyleRange} = visConfig;
580
+
581
+ // Fixed preset (used when no column drives the style).
582
+ if (visConfig.lineStyle && visConfig.lineStyle !== 'solid') {
583
+ if (visConfig.lineStyle === 'dotted') {
584
+ result.lineCapRounded = true;
585
+ }
586
+ result.lineStyle = visConfig.lineStyle;
587
+ if (visConfig.dashArray) {
588
+ result.dashArray = visConfig.dashArray;
589
+ result.getDashArray = visConfig.dashArray;
590
+ }
591
+ }
592
+
593
+ // Data-driven — mirrors getLineColor/getFillColor: computed whenever the field/scale/range
594
+ // are set, independent of the fixed `lineStyle`. Overrides the constant accessor above.
595
+ if (lineStyleField && lineStyleScale && lineStyleRange) {
596
+ const {accessor, ...scaleProps} = getLineStyleAccessor(
597
+ lineStyleField,
598
+ lineStyleRange,
599
+ data
600
+ );
601
+ result.getDashArray = accessor;
602
+ scales.lineStyle = updateTriggers.getDashArray = {
603
+ field: lineStyleField,
604
+ type: lineStyleScale,
605
+ ...scaleProps,
606
+ };
607
+ // Round caps so per-category dotted values ([0, gap]) render as dots, not squares.
608
+ const dashArrays = [
609
+ ...lineStyleRange.dashArrayMap.map(({dashArray}) => dashArray),
610
+ ...(lineStyleRange.othersDashArray
611
+ ? [lineStyleRange.othersDashArray]
612
+ : []),
613
+ ];
614
+ if (dashArrays.some(([dash]) => dash === 0)) {
615
+ result.lineCapRounded = true;
616
+ }
617
+ }
618
+ }
619
+ }
620
+
562
621
  // height / elevation
563
622
  {
564
623
  const {heightField, heightScale} = visualChannels;
@@ -704,14 +763,7 @@ function createChannelProps(
704
763
  // point labels at line midpoints / polygon centroids via `autoLabels`. The
705
764
  // optional `uniqueIdProperty` dedupes features that span multiple tiles so
706
765
  // 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')) {
766
+ if ((isLine || isPolygon) && isVectorTile) {
715
767
  const uniqueIdProperty = visConfig.textLabelUniqueIdField;
716
768
  result.autoLabels = uniqueIdProperty ? {uniqueIdProperty} : true;
717
769
  result.pointType = 'text';
@@ -28,6 +28,9 @@ export type VisualChannels = {
28
28
  strokeColorField?: VisualChannelField;
29
29
  strokeColorScale?: ScaleType;
30
30
 
31
+ lineStyleField?: VisualChannelField;
32
+ lineStyleScale?: ScaleType;
33
+
31
34
  heightField?: VisualChannelField;
32
35
  heightScale?: ScaleType;
33
36
 
@@ -52,6 +55,16 @@ export type CustomMarkersRange = {
52
55
  othersMarker?: string;
53
56
  };
54
57
 
58
+ // Per-category mapping for by-column stroke styles. `dashArray` is the [dash, gap]
59
+ // tuple that flows to deck.gl's `getDashArray`.
60
+ export type LineStyleRange = {
61
+ dashArrayMap: {
62
+ value: string;
63
+ dashArray: [number, number];
64
+ }[];
65
+ othersDashArray?: [number, number];
66
+ };
67
+
55
68
  export type ColorBand = 'red' | 'green' | 'blue' | 'alpha';
56
69
 
57
70
  export type RasterLayerConfigColorBand = {
@@ -95,6 +108,12 @@ export type VisConfig = {
95
108
  strokeColorAggregationDomain?: [number, number];
96
109
  strokeOpacity?: number;
97
110
  strokeColorRange?: ColorRange;
111
+ stroked?: boolean;
112
+
113
+ // `dashArray` is [dash, gap] relative to stroke width. Dotted is [0, gap] + lineCapRounded.
114
+ lineStyle?: 'solid' | 'dashed' | 'dotted';
115
+ dashArray?: [number, number];
116
+ lineStyleRange?: LineStyleRange | null;
98
117
 
99
118
  heightRange?: number[];
100
119
  heightAggregation?: string;