@carto/api-client 0.5.31 → 0.5.32-alpha.04d6222.126
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 +3 -0
- package/build/api-client.cjs +295 -3
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.d.cts +42 -5
- package/build/api-client.d.ts +42 -5
- package/build/api-client.js +284 -3
- package/build/api-client.js.map +1 -1
- package/package.json +2 -2
- package/src/fetch-map/layer-map.ts +48 -0
- package/src/fetch-map/parse-map.ts +117 -10
- package/src/fetch-map/pattern-atlas-baked.ts +56 -0
- package/src/fetch-map/pattern-atlas-runtime.ts +134 -0
- package/src/fetch-map/pattern-atlas.ts +25 -0
- package/src/fetch-map/patterns/checker-large.png +0 -0
- package/src/fetch-map/patterns/checker-medium.png +0 -0
- package/src/fetch-map/patterns/checker-small.png +0 -0
- package/src/fetch-map/patterns/cross-hatch-large.png +0 -0
- package/src/fetch-map/patterns/cross-hatch-medium.png +0 -0
- package/src/fetch-map/patterns/cross-hatch-small.png +0 -0
- package/src/fetch-map/patterns/diag-left-large.png +0 -0
- package/src/fetch-map/patterns/diag-left-medium.png +0 -0
- package/src/fetch-map/patterns/diag-left-small.png +0 -0
- package/src/fetch-map/patterns/diag-right-large.png +0 -0
- package/src/fetch-map/patterns/diag-right-medium.png +0 -0
- package/src/fetch-map/patterns/diag-right-small.png +0 -0
- package/src/fetch-map/patterns/dots-large.png +0 -0
- package/src/fetch-map/patterns/dots-medium.png +0 -0
- package/src/fetch-map/patterns/dots-small.png +0 -0
- package/src/fetch-map/patterns/hlines-large.png +0 -0
- package/src/fetch-map/patterns/hlines-medium.png +0 -0
- package/src/fetch-map/patterns/hlines-small.png +0 -0
- package/src/fetch-map/patterns/solid.png +0 -0
- package/src/fetch-map/patterns/vlines-large.png +0 -0
- package/src/fetch-map/patterns/vlines-medium.png +0 -0
- package/src/fetch-map/patterns/vlines-small.png +0 -0
- package/src/fetch-map/types.ts +44 -0
- package/src/global.d.ts +10 -0
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.32-alpha.04d6222.126",
|
|
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.
|
|
128
|
+
"vitest": "3.2.6"
|
|
129
129
|
},
|
|
130
130
|
"resolutions": {
|
|
131
131
|
"@carto/api-client": "portal:./",
|
|
@@ -32,6 +32,8 @@ import type {
|
|
|
32
32
|
ColorRange,
|
|
33
33
|
CustomMarkersRange,
|
|
34
34
|
Dataset,
|
|
35
|
+
FillPatternRange,
|
|
36
|
+
LineStyleRange,
|
|
35
37
|
MapLayerConfig,
|
|
36
38
|
VisConfig,
|
|
37
39
|
VisualChannelField,
|
|
@@ -510,6 +512,52 @@ export function getIconUrlAccessor(
|
|
|
510
512
|
return normalizeAccessor(accessor, data);
|
|
511
513
|
}
|
|
512
514
|
|
|
515
|
+
export function getLineStyleAccessor(
|
|
516
|
+
field: VisualChannelField,
|
|
517
|
+
range: LineStyleRange,
|
|
518
|
+
data: any
|
|
519
|
+
) {
|
|
520
|
+
const fallback = range.othersDashArray ?? [0, 0];
|
|
521
|
+
const mapping: Record<string, [number, number]> = {};
|
|
522
|
+
for (const {value, dashArray} of range.dashArrayMap) {
|
|
523
|
+
mapping[value] = dashArray;
|
|
524
|
+
}
|
|
525
|
+
const accessor = (properties: any) =>
|
|
526
|
+
mapping[properties[field.name]] ?? fallback;
|
|
527
|
+
return {
|
|
528
|
+
accessor: normalizeAccessor(accessor, data),
|
|
529
|
+
domain: range.dashArrayMap.map(({value}) => value),
|
|
530
|
+
range: range.dashArrayMap.map(({dashArray}) => dashArray),
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function getFillPatternAccessor(
|
|
535
|
+
field: VisualChannelField,
|
|
536
|
+
range: FillPatternRange,
|
|
537
|
+
density: 'small' | 'medium' | 'large' | undefined,
|
|
538
|
+
data: any
|
|
539
|
+
) {
|
|
540
|
+
const d = density ?? 'medium';
|
|
541
|
+
// `solid` / `none` are density-agnostic atlas cells; every other pattern resolves to
|
|
542
|
+
// `${pattern}-${density}`.
|
|
543
|
+
const toKey = (pattern: string) =>
|
|
544
|
+
pattern === 'solid' || pattern === 'none' ? pattern : `${pattern}-${d}`;
|
|
545
|
+
// Always resolve to a real key — an unmapped key would sample atlas bounds [0,0,0,0]
|
|
546
|
+
// (the wrong (0,0) sprite). `none` is a real transparent cell = "paints nothing".
|
|
547
|
+
const fallback = toKey(range.othersPattern ?? 'none');
|
|
548
|
+
const mapping: Record<string, string> = {};
|
|
549
|
+
for (const {value, pattern} of range.patternMap) {
|
|
550
|
+
mapping[value] = toKey(pattern);
|
|
551
|
+
}
|
|
552
|
+
const accessor = (properties: any) =>
|
|
553
|
+
mapping[properties[field.name]] ?? fallback;
|
|
554
|
+
return {
|
|
555
|
+
accessor: normalizeAccessor(accessor, data),
|
|
556
|
+
domain: range.patternMap.map(({value}) => value),
|
|
557
|
+
range: range.patternMap.map(({pattern}) => pattern),
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
513
561
|
export function getMaxMarkerSize(
|
|
514
562
|
visConfig: VisConfig,
|
|
515
563
|
visualChannels: VisualChannels
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
getTextAccessor,
|
|
11
11
|
opacityToAlpha,
|
|
12
12
|
getIconUrlAccessor,
|
|
13
|
+
getLineStyleAccessor,
|
|
14
|
+
getFillPatternAccessor,
|
|
13
15
|
negateAccessor,
|
|
14
16
|
getMaxMarkerSize,
|
|
15
17
|
type LayerType,
|
|
@@ -32,6 +34,7 @@ import type {
|
|
|
32
34
|
VisualChannelField,
|
|
33
35
|
} from './types.js';
|
|
34
36
|
import {isRemoteCalculationSupported} from './utils.js';
|
|
37
|
+
import {getPatternAtlas, PATTERN_ATLAS_MAPPING} from './pattern-atlas.js';
|
|
35
38
|
import {
|
|
36
39
|
getRasterTileLayerStylePropsRgb,
|
|
37
40
|
getRasterTileLayerStylePropsScaledBand,
|
|
@@ -48,7 +51,8 @@ export type Scale = {
|
|
|
48
51
|
|
|
49
52
|
/** Domain of the user to construct d3 scale */
|
|
50
53
|
scaleDomain?: string[] | number[];
|
|
51
|
-
|
|
54
|
+
// `number[][]` carries per-category `[dash, gap]` tuples for the `lineStyle` scale.
|
|
55
|
+
range?: string[] | number[] | number[][];
|
|
52
56
|
};
|
|
53
57
|
|
|
54
58
|
export type ScaleKey =
|
|
@@ -57,7 +61,9 @@ export type ScaleKey =
|
|
|
57
61
|
| 'lineColor'
|
|
58
62
|
| 'lineWidth'
|
|
59
63
|
| 'elevation'
|
|
60
|
-
| 'weight'
|
|
64
|
+
| 'weight'
|
|
65
|
+
| 'lineStyle'
|
|
66
|
+
| 'fillPattern';
|
|
61
67
|
|
|
62
68
|
export type Scales = Partial<Record<ScaleKey, Scale>>;
|
|
63
69
|
|
|
@@ -377,6 +383,14 @@ function createChannelProps(
|
|
|
377
383
|
|
|
378
384
|
const scales: Record<string, Scale> = {};
|
|
379
385
|
|
|
386
|
+
const isVectorTile = layerType === 'mvt' || layerType === 'tileset';
|
|
387
|
+
const geometry = data.tilestats?.layers?.[0]?.geometry;
|
|
388
|
+
const isLine =
|
|
389
|
+
geometry === 'Line' ||
|
|
390
|
+
geometry === 'LineString' ||
|
|
391
|
+
geometry === 'MultiLineString';
|
|
392
|
+
const isPolygon = geometry === 'Polygon' || geometry === 'MultiPolygon';
|
|
393
|
+
|
|
380
394
|
// fill color
|
|
381
395
|
{
|
|
382
396
|
const {colorField, colorScale} = visualChannels;
|
|
@@ -559,6 +573,106 @@ function createChannelProps(
|
|
|
559
573
|
}
|
|
560
574
|
}
|
|
561
575
|
|
|
576
|
+
// stroke dash style — only VectorTileLayer (mvt/tileset) carries dashes; H3/Quadbin excluded.
|
|
577
|
+
{
|
|
578
|
+
// A line is always stroked; a polygon border only when `stroked`. Points are excluded.
|
|
579
|
+
const strokeVisible = isLine || (isPolygon && Boolean(visConfig.stroked));
|
|
580
|
+
if (isVectorTile && strokeVisible) {
|
|
581
|
+
const {lineStyleField, lineStyleScale} = visualChannels;
|
|
582
|
+
const {lineStyleRange} = visConfig;
|
|
583
|
+
|
|
584
|
+
// Fixed preset (used when no column drives the style).
|
|
585
|
+
if (visConfig.lineStyle && visConfig.lineStyle !== 'solid') {
|
|
586
|
+
if (visConfig.lineStyle === 'dotted') {
|
|
587
|
+
result.lineCapRounded = true;
|
|
588
|
+
}
|
|
589
|
+
result.lineStyle = visConfig.lineStyle;
|
|
590
|
+
if (visConfig.dashArray) {
|
|
591
|
+
result.dashArray = visConfig.dashArray;
|
|
592
|
+
result.getDashArray = visConfig.dashArray;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Data-driven — mirrors getLineColor/getFillColor: computed whenever the field/scale/range
|
|
597
|
+
// are set, independent of the fixed `lineStyle`. Overrides the constant accessor above.
|
|
598
|
+
if (lineStyleField && lineStyleScale && lineStyleRange) {
|
|
599
|
+
const {accessor, ...scaleProps} = getLineStyleAccessor(
|
|
600
|
+
lineStyleField,
|
|
601
|
+
lineStyleRange,
|
|
602
|
+
data
|
|
603
|
+
);
|
|
604
|
+
result.getDashArray = accessor;
|
|
605
|
+
scales.lineStyle = updateTriggers.getDashArray = {
|
|
606
|
+
field: lineStyleField,
|
|
607
|
+
type: lineStyleScale,
|
|
608
|
+
...scaleProps,
|
|
609
|
+
};
|
|
610
|
+
// Round caps so per-category dotted values ([0, gap]) render as dots, not squares.
|
|
611
|
+
const dashArrays = [
|
|
612
|
+
...lineStyleRange.dashArrayMap.map(({dashArray}) => dashArray),
|
|
613
|
+
...(lineStyleRange.othersDashArray
|
|
614
|
+
? [lineStyleRange.othersDashArray]
|
|
615
|
+
: []),
|
|
616
|
+
];
|
|
617
|
+
if (dashArrays.some(([dash]) => dash === 0)) {
|
|
618
|
+
result.lineCapRounded = true;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// fill pattern — Phase 2. The pattern is a stencil tinted by the existing fillColor
|
|
625
|
+
// (mask:true) — no separate pattern-color channel. The consumer attaches
|
|
626
|
+
// FillStyleExtension unconditionally and reads `fillPatternEnabled` as the on/off
|
|
627
|
+
// switch, mirroring how MaskExtension is gated by `maskId`. Data only — no extension
|
|
628
|
+
// instantiation here. Applies to any filled polygon layer (mvt/tileset + H3/Quadbin);
|
|
629
|
+
// the stroke-dash block above is the VectorTile-only one (per OQ 10).
|
|
630
|
+
{
|
|
631
|
+
result.fillPatternEnabled = Boolean(visConfig.fillPatternEnabled);
|
|
632
|
+
|
|
633
|
+
if (visConfig.filled && visConfig.fillPatternEnabled) {
|
|
634
|
+
result.fillPatternAtlas = getPatternAtlas();
|
|
635
|
+
result.fillPatternMapping = PATTERN_ATLAS_MAPPING;
|
|
636
|
+
result.fillPatternMask = true;
|
|
637
|
+
// The tiles are pixel art at Nyquist frequency (checker-small is a 2-texel
|
|
638
|
+
// checker): nearest magnification keeps them crisp when zoomed in, but
|
|
639
|
+
// minification must stay linear — nearest picks one arbitrary texel per pixel
|
|
640
|
+
// and dissolves the pattern into per-frame noise when zoomed out.
|
|
641
|
+
// Merged into the atlas texture's sampler by deck's image-prop transform.
|
|
642
|
+
result.textureParameters = {
|
|
643
|
+
magFilter: 'nearest',
|
|
644
|
+
minFilter: 'linear',
|
|
645
|
+
};
|
|
646
|
+
result.getFillPatternScale = visConfig.fillPatternSize ?? 1;
|
|
647
|
+
// Flat prop for legend consumers (fallback when there is no by-column scale).
|
|
648
|
+
result.fillPattern = visConfig.fillPattern;
|
|
649
|
+
|
|
650
|
+
const {fillPatternField, fillPatternScale} = visualChannels;
|
|
651
|
+
const {fillPatternRange, fillPatternDensity} = visConfig;
|
|
652
|
+
|
|
653
|
+
if (fillPatternField && fillPatternScale && fillPatternRange) {
|
|
654
|
+
const {accessor, ...scaleProps} = getFillPatternAccessor(
|
|
655
|
+
fillPatternField,
|
|
656
|
+
fillPatternRange,
|
|
657
|
+
fillPatternDensity,
|
|
658
|
+
data
|
|
659
|
+
);
|
|
660
|
+
result.getFillPattern = accessor;
|
|
661
|
+
scales.fillPattern = updateTriggers.getFillPattern = {
|
|
662
|
+
field: fillPatternField,
|
|
663
|
+
type: fillPatternScale,
|
|
664
|
+
...scaleProps,
|
|
665
|
+
};
|
|
666
|
+
} else {
|
|
667
|
+
// Single mode: one real pattern (never solid/none) for every feature.
|
|
668
|
+
const key = `${visConfig.fillPattern}-${fillPatternDensity ?? 'medium'}`;
|
|
669
|
+
result.getFillPattern = () => key;
|
|
670
|
+
}
|
|
671
|
+
// getFillColor is left exactly as the fillColor channel set it; under
|
|
672
|
+
// fillPatternMask:true that IS the pattern tint.
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
562
676
|
// height / elevation
|
|
563
677
|
{
|
|
564
678
|
const {heightField, heightScale} = visualChannels;
|
|
@@ -704,14 +818,7 @@ function createChannelProps(
|
|
|
704
818
|
// point labels at line midpoints / polygon centroids via `autoLabels`. The
|
|
705
819
|
// optional `uniqueIdProperty` dedupes features that span multiple tiles so
|
|
706
820
|
// each feature gets one label instead of one-per-tile.
|
|
707
|
-
|
|
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')) {
|
|
821
|
+
if ((isLine || isPolygon) && isVectorTile) {
|
|
715
822
|
const uniqueIdProperty = visConfig.textLabelUniqueIdField;
|
|
716
823
|
result.autoLabels = uniqueIdProperty ? {uniqueIdProperty} : true;
|
|
717
824
|
result.pointType = 'text';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Fill-pattern sprite atlas — option A (baked), the default.
|
|
2
|
+
//
|
|
3
|
+
// The definitive Design atlas (192x512, proper space-filling tiles) inlined as a base64
|
|
4
|
+
// data URL, so it ships in the bundle with no asset file and no HTTP request (hence no
|
|
5
|
+
// CORS surface) — the same mechanism api-client already uses for FALLBACK_ICON. deck.gl
|
|
6
|
+
// decodes the string and uploads the GPU texture lazily, only when a FillStyleExtension
|
|
7
|
+
// layer actually draws a pattern. Source of truth is the vector master (kept out of the
|
|
8
|
+
// repo for now) rendered to PNG; the runtime assembler (option B) remains a PoC.
|
|
9
|
+
//
|
|
10
|
+
// 7 patterns x 3 densities = 21 cells, plus a `solid` (opaque, middle of the last row)
|
|
11
|
+
// and a `none` (transparent) cell = 23, each 64x64. Columns are density sparse->dense
|
|
12
|
+
// (large/medium/small at x 0/64/128). Every cell is `mask: true`, so the sprite is a
|
|
13
|
+
// stencil tinted by getFillColor: opaque texels paint the fill color, transparent texels
|
|
14
|
+
// leave gaps. `none` must be a real transparent cell — a null pattern key resolves to
|
|
15
|
+
// atlas bounds [0,0,0,0] and samples the wrong (0,0) cell.
|
|
16
|
+
export const BAKED_ATLAS_URL =
|
|
17
|
+
'data:image/png;base64,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';
|
|
18
|
+
|
|
19
|
+
export type PatternAtlasFrame = {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
mask: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const PATTERN_ATLAS_MAPPING: Record<string, PatternAtlasFrame> = {
|
|
28
|
+
'hlines-large': {x: 0, y: 0, width: 64, height: 64, mask: true},
|
|
29
|
+
'hlines-medium': {x: 64, y: 0, width: 64, height: 64, mask: true},
|
|
30
|
+
'hlines-small': {x: 128, y: 0, width: 64, height: 64, mask: true},
|
|
31
|
+
'vlines-large': {x: 0, y: 64, width: 64, height: 64, mask: true},
|
|
32
|
+
'vlines-medium': {x: 64, y: 64, width: 64, height: 64, mask: true},
|
|
33
|
+
'vlines-small': {x: 128, y: 64, width: 64, height: 64, mask: true},
|
|
34
|
+
'diag-left-large': {x: 0, y: 128, width: 64, height: 64, mask: true},
|
|
35
|
+
'diag-left-medium': {x: 64, y: 128, width: 64, height: 64, mask: true},
|
|
36
|
+
'diag-left-small': {x: 128, y: 128, width: 64, height: 64, mask: true},
|
|
37
|
+
'diag-right-large': {x: 0, y: 192, width: 64, height: 64, mask: true},
|
|
38
|
+
'diag-right-medium': {x: 64, y: 192, width: 64, height: 64, mask: true},
|
|
39
|
+
'diag-right-small': {x: 128, y: 192, width: 64, height: 64, mask: true},
|
|
40
|
+
'cross-hatch-large': {x: 0, y: 256, width: 64, height: 64, mask: true},
|
|
41
|
+
'cross-hatch-medium': {x: 64, y: 256, width: 64, height: 64, mask: true},
|
|
42
|
+
'cross-hatch-small': {x: 128, y: 256, width: 64, height: 64, mask: true},
|
|
43
|
+
'dots-large': {x: 0, y: 320, width: 64, height: 64, mask: true},
|
|
44
|
+
'dots-medium': {x: 64, y: 320, width: 64, height: 64, mask: true},
|
|
45
|
+
'dots-small': {x: 128, y: 320, width: 64, height: 64, mask: true},
|
|
46
|
+
'checker-large': {x: 0, y: 384, width: 64, height: 64, mask: true},
|
|
47
|
+
'checker-medium': {x: 64, y: 384, width: 64, height: 64, mask: true},
|
|
48
|
+
'checker-small': {x: 128, y: 384, width: 64, height: 64, mask: true},
|
|
49
|
+
solid: {x: 64, y: 448, width: 64, height: 64, mask: true},
|
|
50
|
+
none: {x: 0, y: 448, width: 64, height: 64, mask: true},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Atlas geometry shared by the baked sheet above and the runtime assembler.
|
|
54
|
+
export const CELL = 64;
|
|
55
|
+
export const ATLAS_W = CELL * 3; // 192 — columns: [large, medium, small]
|
|
56
|
+
export const ATLAS_H = CELL * 8; // 512 — 7 pattern rows + 1 solid/none row
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Runtime fill-pattern atlas — option B (the proposal).
|
|
2
|
+
//
|
|
3
|
+
// The patterns live as individual, developer-editable assets (src/fetch-map/patterns/*.png,
|
|
4
|
+
// one 64x64 space-filling tile per pattern+density) rather than a compiled base64 blob.
|
|
5
|
+
// tsup inlines each via its `dataurl` loader, and we composite them into the 192x512 sheet
|
|
6
|
+
// on a canvas the first time a pattern is needed. The result is memoized and handed to
|
|
7
|
+
// deck.gl's async `fillPatternAtlas` prop as a Promise of a decoded image (so this stays
|
|
8
|
+
// internal — the consumer just spreads descriptor.props). SVG-vs-PNG is a later experiment.
|
|
9
|
+
|
|
10
|
+
import hlinesLarge from './patterns/hlines-large.png';
|
|
11
|
+
import hlinesMedium from './patterns/hlines-medium.png';
|
|
12
|
+
import hlinesSmall from './patterns/hlines-small.png';
|
|
13
|
+
import vlinesLarge from './patterns/vlines-large.png';
|
|
14
|
+
import vlinesMedium from './patterns/vlines-medium.png';
|
|
15
|
+
import vlinesSmall from './patterns/vlines-small.png';
|
|
16
|
+
import diagLeftLarge from './patterns/diag-left-large.png';
|
|
17
|
+
import diagLeftMedium from './patterns/diag-left-medium.png';
|
|
18
|
+
import diagLeftSmall from './patterns/diag-left-small.png';
|
|
19
|
+
import diagRightLarge from './patterns/diag-right-large.png';
|
|
20
|
+
import diagRightMedium from './patterns/diag-right-medium.png';
|
|
21
|
+
import diagRightSmall from './patterns/diag-right-small.png';
|
|
22
|
+
import crossHatchLarge from './patterns/cross-hatch-large.png';
|
|
23
|
+
import crossHatchMedium from './patterns/cross-hatch-medium.png';
|
|
24
|
+
import crossHatchSmall from './patterns/cross-hatch-small.png';
|
|
25
|
+
import dotsLarge from './patterns/dots-large.png';
|
|
26
|
+
import dotsMedium from './patterns/dots-medium.png';
|
|
27
|
+
import dotsSmall from './patterns/dots-small.png';
|
|
28
|
+
import checkerLarge from './patterns/checker-large.png';
|
|
29
|
+
import checkerMedium from './patterns/checker-medium.png';
|
|
30
|
+
import checkerSmall from './patterns/checker-small.png';
|
|
31
|
+
import solid from './patterns/solid.png';
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
ATLAS_W,
|
|
35
|
+
ATLAS_H,
|
|
36
|
+
CELL,
|
|
37
|
+
PATTERN_ATLAS_MAPPING,
|
|
38
|
+
} from './pattern-atlas-baked.js';
|
|
39
|
+
|
|
40
|
+
// atlas key -> inlined data URL of its editable source tile
|
|
41
|
+
const CELL_URLS: Record<string, string> = {
|
|
42
|
+
'hlines-large': hlinesLarge,
|
|
43
|
+
'hlines-medium': hlinesMedium,
|
|
44
|
+
'hlines-small': hlinesSmall,
|
|
45
|
+
'vlines-large': vlinesLarge,
|
|
46
|
+
'vlines-medium': vlinesMedium,
|
|
47
|
+
'vlines-small': vlinesSmall,
|
|
48
|
+
'diag-left-large': diagLeftLarge,
|
|
49
|
+
'diag-left-medium': diagLeftMedium,
|
|
50
|
+
'diag-left-small': diagLeftSmall,
|
|
51
|
+
'diag-right-large': diagRightLarge,
|
|
52
|
+
'diag-right-medium': diagRightMedium,
|
|
53
|
+
'diag-right-small': diagRightSmall,
|
|
54
|
+
'cross-hatch-large': crossHatchLarge,
|
|
55
|
+
'cross-hatch-medium': crossHatchMedium,
|
|
56
|
+
'cross-hatch-small': crossHatchSmall,
|
|
57
|
+
'dots-large': dotsLarge,
|
|
58
|
+
'dots-medium': dotsMedium,
|
|
59
|
+
'dots-small': dotsSmall,
|
|
60
|
+
'checker-large': checkerLarge,
|
|
61
|
+
'checker-medium': checkerMedium,
|
|
62
|
+
'checker-small': checkerSmall,
|
|
63
|
+
solid,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type AnyCanvas = HTMLCanvasElement | OffscreenCanvas;
|
|
67
|
+
|
|
68
|
+
function createCanvas(w: number, h: number): AnyCanvas {
|
|
69
|
+
if (typeof OffscreenCanvas !== 'undefined') return new OffscreenCanvas(w, h);
|
|
70
|
+
if (typeof document !== 'undefined') {
|
|
71
|
+
const c = document.createElement('canvas');
|
|
72
|
+
c.width = w;
|
|
73
|
+
c.height = h;
|
|
74
|
+
return c;
|
|
75
|
+
}
|
|
76
|
+
throw new Error(
|
|
77
|
+
'carto-api-client: no Canvas available to assemble the pattern atlas'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function loadImage(dataUrl: string): Promise<CanvasImageSource> {
|
|
82
|
+
if (
|
|
83
|
+
typeof createImageBitmap !== 'undefined' &&
|
|
84
|
+
typeof fetch !== 'undefined'
|
|
85
|
+
) {
|
|
86
|
+
const blob = await (await fetch(dataUrl)).blob();
|
|
87
|
+
return createImageBitmap(blob);
|
|
88
|
+
}
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const img = new Image();
|
|
91
|
+
img.onload = () => resolve(img);
|
|
92
|
+
img.onerror = reject;
|
|
93
|
+
img.src = dataUrl;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type AssembledAtlas = ImageBitmap | HTMLCanvasElement;
|
|
98
|
+
|
|
99
|
+
let atlasPromise: Promise<AssembledAtlas> | undefined;
|
|
100
|
+
|
|
101
|
+
/** Assemble the sprite sheet from the individual editable pattern tiles. Memoized. */
|
|
102
|
+
export function assemblePatternAtlas(): Promise<AssembledAtlas> {
|
|
103
|
+
if (!atlasPromise) atlasPromise = build();
|
|
104
|
+
return atlasPromise;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Must resolve to a decoded image, never a data-URL string: deck.gl only URL-loads
|
|
108
|
+
// string prop *values* — a string resolved from a Promise goes straight to texture
|
|
109
|
+
// creation, where luma.gl rejects it (1x1 texture, "ArrayBufferView not big enough").
|
|
110
|
+
async function build(): Promise<AssembledAtlas> {
|
|
111
|
+
const canvas = createCanvas(ATLAS_W, ATLAS_H);
|
|
112
|
+
const ctx = canvas.getContext('2d');
|
|
113
|
+
if (!ctx)
|
|
114
|
+
throw new Error(
|
|
115
|
+
'carto-api-client: 2D context unavailable for pattern atlas'
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const images: Record<string, CanvasImageSource> = {};
|
|
119
|
+
await Promise.all(
|
|
120
|
+
Object.entries(CELL_URLS).map(async ([key, url]) => {
|
|
121
|
+
images[key] = await loadImage(url);
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Place each editable 64x64 tile at its slot; `none` is left transparent (no tile).
|
|
126
|
+
for (const [key, frame] of Object.entries(PATTERN_ATLAS_MAPPING)) {
|
|
127
|
+
const img = images[key];
|
|
128
|
+
if (img) ctx.drawImage(img, frame.x, frame.y, CELL, CELL);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (typeof createImageBitmap !== 'undefined')
|
|
132
|
+
return createImageBitmap(canvas);
|
|
133
|
+
return canvas as HTMLCanvasElement;
|
|
134
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Fill-pattern atlas — internal to carto-api-client.
|
|
2
|
+
//
|
|
3
|
+
// parse-map sets `result.fillPatternAtlas = getPatternAtlas()`. deck.gl's
|
|
4
|
+
// `fillPatternAtlas` prop is async, so it accepts either the baked data-URL string or a
|
|
5
|
+
// Promise of the runtime-assembled sheet (a decoded image — deck URL-loads string prop
|
|
6
|
+
// values but not promise-resolved strings) — the consumer just spreads `descriptor.props`
|
|
7
|
+
// and never sees this choice.
|
|
8
|
+
//
|
|
9
|
+
// PoC switch: set `globalThis.__CARTO_RUNTIME_ATLAS__ = true` (e.g. in the browser
|
|
10
|
+
// console) and reload the map to A/B the runtime-assembled atlas against the baked one.
|
|
11
|
+
|
|
12
|
+
import {BAKED_ATLAS_URL, PATTERN_ATLAS_MAPPING} from './pattern-atlas-baked.js';
|
|
13
|
+
import {
|
|
14
|
+
assemblePatternAtlas,
|
|
15
|
+
type AssembledAtlas,
|
|
16
|
+
} from './pattern-atlas-runtime.js';
|
|
17
|
+
|
|
18
|
+
export {PATTERN_ATLAS_MAPPING};
|
|
19
|
+
|
|
20
|
+
export function getPatternAtlas(): string | Promise<AssembledAtlas> {
|
|
21
|
+
const runtime =
|
|
22
|
+
(globalThis as {__CARTO_RUNTIME_ATLAS__?: boolean})
|
|
23
|
+
.__CARTO_RUNTIME_ATLAS__ === true;
|
|
24
|
+
return runtime ? assemblePatternAtlas() : BAKED_ATLAS_URL;
|
|
25
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/fetch-map/types.ts
CHANGED
|
@@ -28,6 +28,12 @@ export type VisualChannels = {
|
|
|
28
28
|
strokeColorField?: VisualChannelField;
|
|
29
29
|
strokeColorScale?: ScaleType;
|
|
30
30
|
|
|
31
|
+
lineStyleField?: VisualChannelField;
|
|
32
|
+
lineStyleScale?: ScaleType;
|
|
33
|
+
|
|
34
|
+
fillPatternField?: VisualChannelField;
|
|
35
|
+
fillPatternScale?: ScaleType;
|
|
36
|
+
|
|
31
37
|
heightField?: VisualChannelField;
|
|
32
38
|
heightScale?: ScaleType;
|
|
33
39
|
|
|
@@ -52,6 +58,28 @@ export type CustomMarkersRange = {
|
|
|
52
58
|
othersMarker?: string;
|
|
53
59
|
};
|
|
54
60
|
|
|
61
|
+
// Per-category mapping for by-column stroke styles. `dashArray` is the [dash, gap]
|
|
62
|
+
// tuple that flows to deck.gl's `getDashArray`.
|
|
63
|
+
export type LineStyleRange = {
|
|
64
|
+
dashArrayMap: {
|
|
65
|
+
value: string;
|
|
66
|
+
dashArray: [number, number];
|
|
67
|
+
}[];
|
|
68
|
+
othersDashArray?: [number, number];
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Per-category mapping for by-column fill patterns. `pattern` is the base pattern name
|
|
72
|
+
// (e.g. 'hlines') or the density-agnostic 'solid' / 'none'; density is applied uniformly
|
|
73
|
+
// from `visConfig.fillPatternDensity`, so the resolved atlas key is `${pattern}-${density}`
|
|
74
|
+
// (or the bare 'solid' / 'none').
|
|
75
|
+
export type FillPatternRange = {
|
|
76
|
+
patternMap: {
|
|
77
|
+
value: string;
|
|
78
|
+
pattern: string;
|
|
79
|
+
}[];
|
|
80
|
+
othersPattern?: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
55
83
|
export type ColorBand = 'red' | 'green' | 'blue' | 'alpha';
|
|
56
84
|
|
|
57
85
|
export type RasterLayerConfigColorBand = {
|
|
@@ -95,6 +123,22 @@ export type VisConfig = {
|
|
|
95
123
|
strokeColorAggregationDomain?: [number, number];
|
|
96
124
|
strokeOpacity?: number;
|
|
97
125
|
strokeColorRange?: ColorRange;
|
|
126
|
+
stroked?: boolean;
|
|
127
|
+
|
|
128
|
+
// `dashArray` is [dash, gap] relative to stroke width. Dotted is [0, gap] + lineCapRounded.
|
|
129
|
+
lineStyle?: 'solid' | 'dashed' | 'dotted';
|
|
130
|
+
dashArray?: [number, number];
|
|
131
|
+
lineStyleRange?: LineStyleRange | null;
|
|
132
|
+
|
|
133
|
+
// Fill pattern (Phase 2). `fillPatternEnabled` is the Solid|Pattern master toggle the
|
|
134
|
+
// (always-attached) FillStyleExtension reads. The pattern is tinted by the existing
|
|
135
|
+
// `fillColor` — there is no separate pattern-color field. `fillPatternSize` maps to
|
|
136
|
+
// deck.gl's getFillPatternScale (1 = 100%).
|
|
137
|
+
fillPatternEnabled?: boolean;
|
|
138
|
+
fillPattern?: string;
|
|
139
|
+
fillPatternDensity?: 'small' | 'medium' | 'large';
|
|
140
|
+
fillPatternSize?: number;
|
|
141
|
+
fillPatternRange?: FillPatternRange | null;
|
|
98
142
|
|
|
99
143
|
heightRange?: number[];
|
|
100
144
|
heightAggregation?: string;
|
package/src/global.d.ts
CHANGED
|
@@ -3,3 +3,13 @@ declare const deck: {VERSION: string | undefined} | undefined;
|
|
|
3
3
|
|
|
4
4
|
/** Defined by tsup. */
|
|
5
5
|
declare const TSUP_FORMAT: 'esm' | 'cjs';
|
|
6
|
+
|
|
7
|
+
/** Pattern assets imported as inline data URLs (tsup `dataurl` loader). */
|
|
8
|
+
declare module '*.png' {
|
|
9
|
+
const dataUrl: string;
|
|
10
|
+
export default dataUrl;
|
|
11
|
+
}
|
|
12
|
+
declare module '*.svg' {
|
|
13
|
+
const dataUrl: string;
|
|
14
|
+
export default dataUrl;
|
|
15
|
+
}
|