maplibre-gl 3.2.2 → 3.3.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/generate-struct-arrays.ts +3 -1
- package/build/generate-style-code.ts +7 -8
- package/dist/maplibre-gl-csp-worker.js +1 -1
- package/dist/maplibre-gl-csp-worker.js.map +1 -1
- package/dist/maplibre-gl-csp.js +1 -1
- package/dist/maplibre-gl-csp.js.map +1 -1
- package/dist/maplibre-gl-dev.js +424 -168
- package/dist/maplibre-gl-dev.js.map +1 -1
- package/dist/maplibre-gl.d.ts +44 -7
- package/dist/maplibre-gl.js +3 -3
- package/dist/maplibre-gl.js.map +1 -1
- package/package.json +10 -10
- package/src/data/array_types.g.ts +78 -14
- package/src/data/bucket/symbol_attributes.ts +7 -1
- package/src/data/bucket/symbol_bucket.ts +4 -1
- package/src/render/draw_symbol.ts +8 -9
- package/src/style/properties.ts +4 -0
- package/src/style/style_layer/background_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/circle_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/fill_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/heatmap_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/hillshade_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/line_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/raster_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/symbol_style_layer_properties.g.ts +4 -6
- package/src/style/style_layer/variable_text_anchor.test.ts +117 -0
- package/src/style/style_layer/variable_text_anchor.ts +163 -0
- package/src/symbol/placement.ts +52 -40
- package/src/symbol/symbol_layout.ts +42 -116
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maplibre-gl",
|
|
3
3
|
"description": "BSD licensed community fork of mapbox-gl, a WebGL interactive maps library",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"main": "dist/maplibre-gl.js",
|
|
6
6
|
"style": "dist/maplibre-gl.css",
|
|
7
7
|
"license": "BSD-3-Clause",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@mapbox/unitbezier": "^0.0.1",
|
|
21
21
|
"@mapbox/vector-tile": "^1.3.1",
|
|
22
22
|
"@mapbox/whoots-js": "^3.1.0",
|
|
23
|
-
"@maplibre/maplibre-gl-style-spec": "^19.
|
|
23
|
+
"@maplibre/maplibre-gl-style-spec": "^19.3.0",
|
|
24
24
|
"@types/geojson": "^7946.0.10",
|
|
25
25
|
"@types/mapbox__point-geometry": "^0.1.2",
|
|
26
26
|
"@types/mapbox__vector-tile": "^1.3.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@types/d3": "^7.4.0",
|
|
55
55
|
"@types/diff": "^5.0.3",
|
|
56
56
|
"@types/earcut": "^2.1.1",
|
|
57
|
-
"@types/eslint": "^8.44.
|
|
57
|
+
"@types/eslint": "^8.44.2",
|
|
58
58
|
"@types/gl": "^6.0.2",
|
|
59
59
|
"@types/glob": "^8.1.0",
|
|
60
60
|
"@types/jest": "^29.5.3",
|
|
@@ -62,24 +62,24 @@
|
|
|
62
62
|
"@types/minimist": "^1.2.2",
|
|
63
63
|
"@types/murmurhash-js": "^1.0.4",
|
|
64
64
|
"@types/nise": "^1.4.1",
|
|
65
|
-
"@types/node": "^20.4.
|
|
65
|
+
"@types/node": "^20.4.8",
|
|
66
66
|
"@types/offscreencanvas": "^2019.7.0",
|
|
67
67
|
"@types/pixelmatch": "^5.2.4",
|
|
68
68
|
"@types/pngjs": "^6.0.1",
|
|
69
|
-
"@types/react": "^18.2.
|
|
69
|
+
"@types/react": "^18.2.18",
|
|
70
70
|
"@types/react-dom": "^18.2.7",
|
|
71
71
|
"@types/request": "^2.48.8",
|
|
72
72
|
"@types/shuffle-seed": "^1.1.0",
|
|
73
73
|
"@types/window-or-global": "^1.0.4",
|
|
74
|
-
"@typescript-eslint/eslint-plugin": "^6.2.
|
|
75
|
-
"@typescript-eslint/parser": "^6.2.
|
|
74
|
+
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
|
75
|
+
"@typescript-eslint/parser": "^6.2.1",
|
|
76
76
|
"address": "^1.2.2",
|
|
77
77
|
"benchmark": "^2.1.4",
|
|
78
78
|
"canvas": "^2.11.2",
|
|
79
79
|
"cssnano": "^6.0.1",
|
|
80
80
|
"d3": "^7.8.5",
|
|
81
81
|
"d3-queue": "^3.0.7",
|
|
82
|
-
"devtools-protocol": "^0.0.
|
|
82
|
+
"devtools-protocol": "^0.0.1179426",
|
|
83
83
|
"diff": "^5.1.0",
|
|
84
84
|
"dts-bundle-generator": "^8.0.1",
|
|
85
85
|
"eslint": "^8.46.0",
|
|
@@ -111,10 +111,10 @@
|
|
|
111
111
|
"postcss-cli": "^10.1.0",
|
|
112
112
|
"postcss-inline-svg": "^6.0.0",
|
|
113
113
|
"pretty-bytes": "^6.1.1",
|
|
114
|
-
"puppeteer": "^
|
|
114
|
+
"puppeteer": "^21.0.1",
|
|
115
115
|
"react": "^18.2.0",
|
|
116
116
|
"react-dom": "^18.2.0",
|
|
117
|
-
"rollup": "^3.27.
|
|
117
|
+
"rollup": "^3.27.2",
|
|
118
118
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
|
119
119
|
"rw": "^1.3.3",
|
|
120
120
|
"semver": "^7.5.4",
|
|
@@ -621,10 +621,11 @@ register('StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48', StructArrayLayout2i2ui3ul3u
|
|
|
621
621
|
* [0]: Int16[8]
|
|
622
622
|
* [16]: Uint16[15]
|
|
623
623
|
* [48]: Uint32[1]
|
|
624
|
-
* [52]: Float32[
|
|
624
|
+
* [52]: Float32[2]
|
|
625
|
+
* [60]: Uint16[2]
|
|
625
626
|
*
|
|
626
627
|
*/
|
|
627
|
-
class
|
|
628
|
+
class StructArrayLayout8i15ui1ul2f2ui64 extends StructArray {
|
|
628
629
|
uint8: Uint8Array;
|
|
629
630
|
int16: Int16Array;
|
|
630
631
|
uint16: Uint16Array;
|
|
@@ -646,8 +647,8 @@ class StructArrayLayout8i15ui1ul4f68 extends StructArray {
|
|
|
646
647
|
}
|
|
647
648
|
|
|
648
649
|
public emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number, v21: number, v22: number, v23: number, v24: number, v25: number, v26: number, v27: number) {
|
|
649
|
-
const o2 = i *
|
|
650
|
-
const o4 = i *
|
|
650
|
+
const o2 = i * 32;
|
|
651
|
+
const o4 = i * 16;
|
|
651
652
|
this.int16[o2 + 0] = v0;
|
|
652
653
|
this.int16[o2 + 1] = v1;
|
|
653
654
|
this.int16[o2 + 2] = v2;
|
|
@@ -674,14 +675,14 @@ class StructArrayLayout8i15ui1ul4f68 extends StructArray {
|
|
|
674
675
|
this.uint32[o4 + 12] = v23;
|
|
675
676
|
this.float32[o4 + 13] = v24;
|
|
676
677
|
this.float32[o4 + 14] = v25;
|
|
677
|
-
this.
|
|
678
|
-
this.
|
|
678
|
+
this.uint16[o2 + 30] = v26;
|
|
679
|
+
this.uint16[o2 + 31] = v27;
|
|
679
680
|
return i;
|
|
680
681
|
}
|
|
681
682
|
}
|
|
682
683
|
|
|
683
|
-
|
|
684
|
-
register('
|
|
684
|
+
StructArrayLayout8i15ui1ul2f2ui64.prototype.bytesPerElement = 64;
|
|
685
|
+
register('StructArrayLayout8i15ui1ul2f2ui64', StructArrayLayout8i15ui1ul2f2ui64);
|
|
685
686
|
|
|
686
687
|
/**
|
|
687
688
|
* @internal
|
|
@@ -714,6 +715,43 @@ class StructArrayLayout1f4 extends StructArray {
|
|
|
714
715
|
StructArrayLayout1f4.prototype.bytesPerElement = 4;
|
|
715
716
|
register('StructArrayLayout1f4', StructArrayLayout1f4);
|
|
716
717
|
|
|
718
|
+
/**
|
|
719
|
+
* @internal
|
|
720
|
+
* Implementation of the StructArray layout:
|
|
721
|
+
* [0]: Uint16[1]
|
|
722
|
+
* [4]: Float32[2]
|
|
723
|
+
*
|
|
724
|
+
*/
|
|
725
|
+
class StructArrayLayout1ui2f12 extends StructArray {
|
|
726
|
+
uint8: Uint8Array;
|
|
727
|
+
uint16: Uint16Array;
|
|
728
|
+
float32: Float32Array;
|
|
729
|
+
|
|
730
|
+
_refreshViews() {
|
|
731
|
+
this.uint8 = new Uint8Array(this.arrayBuffer);
|
|
732
|
+
this.uint16 = new Uint16Array(this.arrayBuffer);
|
|
733
|
+
this.float32 = new Float32Array(this.arrayBuffer);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
public emplaceBack(v0: number, v1: number, v2: number) {
|
|
737
|
+
const i = this.length;
|
|
738
|
+
this.resize(i + 1);
|
|
739
|
+
return this.emplace(i, v0, v1, v2);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
public emplace(i: number, v0: number, v1: number, v2: number) {
|
|
743
|
+
const o2 = i * 6;
|
|
744
|
+
const o4 = i * 3;
|
|
745
|
+
this.uint16[o2 + 0] = v0;
|
|
746
|
+
this.float32[o4 + 1] = v1;
|
|
747
|
+
this.float32[o4 + 2] = v2;
|
|
748
|
+
return i;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
StructArrayLayout1ui2f12.prototype.bytesPerElement = 12;
|
|
753
|
+
register('StructArrayLayout1ui2f12', StructArrayLayout1ui2f12);
|
|
754
|
+
|
|
717
755
|
/**
|
|
718
756
|
* @internal
|
|
719
757
|
* Implementation of the StructArray layout:
|
|
@@ -951,17 +989,17 @@ class SymbolInstanceStruct extends Struct {
|
|
|
951
989
|
get crossTileID() { return this._structArray.uint32[this._pos4 + 12]; }
|
|
952
990
|
set crossTileID(x: number) { this._structArray.uint32[this._pos4 + 12] = x; }
|
|
953
991
|
get textBoxScale() { return this._structArray.float32[this._pos4 + 13]; }
|
|
954
|
-
get
|
|
955
|
-
get
|
|
956
|
-
get
|
|
992
|
+
get collisionCircleDiameter() { return this._structArray.float32[this._pos4 + 14]; }
|
|
993
|
+
get textAnchorOffsetStartIndex() { return this._structArray.uint16[this._pos2 + 30]; }
|
|
994
|
+
get textAnchorOffsetEndIndex() { return this._structArray.uint16[this._pos2 + 31]; }
|
|
957
995
|
}
|
|
958
996
|
|
|
959
|
-
SymbolInstanceStruct.prototype.size =
|
|
997
|
+
SymbolInstanceStruct.prototype.size = 64;
|
|
960
998
|
|
|
961
999
|
export type SymbolInstance = SymbolInstanceStruct;
|
|
962
1000
|
|
|
963
1001
|
/** @internal */
|
|
964
|
-
export class SymbolInstanceArray extends
|
|
1002
|
+
export class SymbolInstanceArray extends StructArrayLayout8i15ui1ul2f2ui64 {
|
|
965
1003
|
/**
|
|
966
1004
|
* Return the SymbolInstanceStruct at the given location in the array.
|
|
967
1005
|
* @param index The index of the element.
|
|
@@ -989,6 +1027,31 @@ export class SymbolLineVertexArray extends StructArrayLayout3i6 {
|
|
|
989
1027
|
|
|
990
1028
|
register('SymbolLineVertexArray', SymbolLineVertexArray);
|
|
991
1029
|
|
|
1030
|
+
/** @internal */
|
|
1031
|
+
class TextAnchorOffsetStruct extends Struct {
|
|
1032
|
+
_structArray: TextAnchorOffsetArray;
|
|
1033
|
+
get textAnchor() { return this._structArray.uint16[this._pos2 + 0]; }
|
|
1034
|
+
get textOffset0() { return this._structArray.float32[this._pos4 + 1]; }
|
|
1035
|
+
get textOffset1() { return this._structArray.float32[this._pos4 + 2]; }
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
TextAnchorOffsetStruct.prototype.size = 12;
|
|
1039
|
+
|
|
1040
|
+
export type TextAnchorOffset = TextAnchorOffsetStruct;
|
|
1041
|
+
|
|
1042
|
+
/** @internal */
|
|
1043
|
+
export class TextAnchorOffsetArray extends StructArrayLayout1ui2f12 {
|
|
1044
|
+
/**
|
|
1045
|
+
* Return the TextAnchorOffsetStruct at the given location in the array.
|
|
1046
|
+
* @param index The index of the element.
|
|
1047
|
+
*/
|
|
1048
|
+
get(index: number): TextAnchorOffsetStruct {
|
|
1049
|
+
return new TextAnchorOffsetStruct(this, index);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
register('TextAnchorOffsetArray', TextAnchorOffsetArray);
|
|
1054
|
+
|
|
992
1055
|
/** @internal */
|
|
993
1056
|
class FeatureIndexStruct extends Struct {
|
|
994
1057
|
_structArray: FeatureIndexArray;
|
|
@@ -1051,8 +1114,9 @@ export {
|
|
|
1051
1114
|
StructArrayLayout2ub2f12,
|
|
1052
1115
|
StructArrayLayout3ui6,
|
|
1053
1116
|
StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48,
|
|
1054
|
-
|
|
1117
|
+
StructArrayLayout8i15ui1ul2f2ui64,
|
|
1055
1118
|
StructArrayLayout1f4,
|
|
1119
|
+
StructArrayLayout1ui2f12,
|
|
1056
1120
|
StructArrayLayout1ul2ui8,
|
|
1057
1121
|
StructArrayLayout2ui4,
|
|
1058
1122
|
StructArrayLayout1ui2,
|
|
@@ -100,8 +100,9 @@ export const symbolInstance = createLayout([
|
|
|
100
100
|
{type: 'Uint16', name: 'useRuntimeCollisionCircles'},
|
|
101
101
|
{type: 'Uint32', name: 'crossTileID'},
|
|
102
102
|
{type: 'Float32', name: 'textBoxScale'},
|
|
103
|
-
{type: 'Float32', components: 2, name: 'textOffset'},
|
|
104
103
|
{type: 'Float32', name: 'collisionCircleDiameter'},
|
|
104
|
+
{type: 'Uint16', name: 'textAnchorOffsetStartIndex'},
|
|
105
|
+
{type: 'Uint16', name: 'textAnchorOffsetEndIndex'}
|
|
105
106
|
]);
|
|
106
107
|
|
|
107
108
|
export const glyphOffset = createLayout([
|
|
@@ -113,3 +114,8 @@ export const lineVertex = createLayout([
|
|
|
113
114
|
{type: 'Int16', name: 'y'},
|
|
114
115
|
{type: 'Int16', name: 'tileUnitDistanceFromAnchor'}
|
|
115
116
|
]);
|
|
117
|
+
|
|
118
|
+
export const textAnchorOffset = createLayout([
|
|
119
|
+
{type: 'Uint16', name: 'textAnchor'},
|
|
120
|
+
{type: 'Float32', components: 2, name: 'textOffset'}
|
|
121
|
+
]);
|
|
@@ -13,7 +13,8 @@ import {SymbolLayoutArray,
|
|
|
13
13
|
PlacedSymbolArray,
|
|
14
14
|
SymbolInstanceArray,
|
|
15
15
|
GlyphOffsetArray,
|
|
16
|
-
SymbolLineVertexArray
|
|
16
|
+
SymbolLineVertexArray,
|
|
17
|
+
TextAnchorOffsetArray
|
|
17
18
|
} from '../array_types.g';
|
|
18
19
|
|
|
19
20
|
import Point from '@mapbox/point-geometry';
|
|
@@ -333,6 +334,7 @@ export class SymbolBucket implements Bucket {
|
|
|
333
334
|
lineVertexArray: SymbolLineVertexArray;
|
|
334
335
|
features: Array<SymbolFeature>;
|
|
335
336
|
symbolInstances: SymbolInstanceArray;
|
|
337
|
+
textAnchorOffsets: TextAnchorOffsetArray;
|
|
336
338
|
collisionArrays: Array<CollisionArrays>;
|
|
337
339
|
sortKeyRanges: Array<SortKeyRange>;
|
|
338
340
|
pixelRatio: number;
|
|
@@ -412,6 +414,7 @@ export class SymbolBucket implements Bucket {
|
|
|
412
414
|
this.glyphOffsetArray = new GlyphOffsetArray();
|
|
413
415
|
this.lineVertexArray = new SymbolLineVertexArray();
|
|
414
416
|
this.symbolInstances = new SymbolInstanceArray();
|
|
417
|
+
this.textAnchorOffsets = new TextAnchorOffsetArray();
|
|
415
418
|
}
|
|
416
419
|
|
|
417
420
|
calculateGlyphDependencies(text: string, stack: {[_: number]: boolean}, textAlongLine: boolean, allowVerticalPlacement: boolean, doesAllowVerticalWritingMode: boolean) {
|
|
@@ -13,7 +13,6 @@ import {addDynamicAttributes} from '../data/bucket/symbol_bucket';
|
|
|
13
13
|
|
|
14
14
|
import {getAnchorAlignment, WritingMode} from '../symbol/shaping';
|
|
15
15
|
import ONE_EM from '../symbol/one_em';
|
|
16
|
-
import {evaluateVariableOffset, TextAnchor} from '../symbol/symbol_layout';
|
|
17
16
|
|
|
18
17
|
import {
|
|
19
18
|
SymbolIconUniformsType,
|
|
@@ -37,6 +36,7 @@ import type {SymbolLayerSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
|
37
36
|
import type {Transform} from '../geo/transform';
|
|
38
37
|
import type {ColorMode} from '../gl/color_mode';
|
|
39
38
|
import type {Program} from './program';
|
|
39
|
+
import type {TextAnchor} from '../style/style_layer/variable_text_anchor';
|
|
40
40
|
|
|
41
41
|
type SymbolTileRenderState = {
|
|
42
42
|
segments: SegmentVector;
|
|
@@ -65,11 +65,11 @@ export function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: S
|
|
|
65
65
|
// Disable the stencil test so that labels aren't clipped to tile boundaries.
|
|
66
66
|
const stencilMode = StencilMode.disabled;
|
|
67
67
|
const colorMode = painter.colorModeForRenderPass();
|
|
68
|
-
const
|
|
68
|
+
const hasVariablePlacement = layer._unevaluatedLayout.hasValue('text-variable-anchor') || layer._unevaluatedLayout.hasValue('text-variable-anchor-offset');
|
|
69
69
|
|
|
70
70
|
//Compute variable-offsets before painting since icons and text data positioning
|
|
71
71
|
//depend on each other in this case.
|
|
72
|
-
if (
|
|
72
|
+
if (hasVariablePlacement) {
|
|
73
73
|
updateVariableAnchors(coords, painter, layer, sourceCache,
|
|
74
74
|
layer.layout.get('text-rotation-alignment'),
|
|
75
75
|
layer.layout.get('text-pitch-alignment'),
|
|
@@ -117,10 +117,9 @@ function calculateVariableRenderShift(
|
|
|
117
117
|
const {horizontalAlign, verticalAlign} = getAnchorAlignment(anchor);
|
|
118
118
|
const shiftX = -(horizontalAlign - 0.5) * width;
|
|
119
119
|
const shiftY = -(verticalAlign - 0.5) * height;
|
|
120
|
-
const variableOffset = evaluateVariableOffset(anchor, textOffset);
|
|
121
120
|
return new Point(
|
|
122
|
-
(shiftX / textBoxScale +
|
|
123
|
-
(shiftY / textBoxScale +
|
|
121
|
+
(shiftX / textBoxScale + textOffset[0]) * renderTextSize,
|
|
122
|
+
(shiftY / textBoxScale + textOffset[1]) * renderTextSize
|
|
124
123
|
);
|
|
125
124
|
}
|
|
126
125
|
|
|
@@ -281,7 +280,7 @@ function drawLayerSymbols(
|
|
|
281
280
|
|
|
282
281
|
const depthMode = painter.depthModeForSublayer(0, DepthMode.ReadOnly);
|
|
283
282
|
|
|
284
|
-
const
|
|
283
|
+
const hasVariablePlacement = layer._unevaluatedLayout.hasValue('text-variable-anchor') || layer._unevaluatedLayout.hasValue('text-variable-anchor-offset');
|
|
285
284
|
|
|
286
285
|
const tileRenderState: Array<SymbolTileRenderState> = [];
|
|
287
286
|
|
|
@@ -332,7 +331,7 @@ function drawLayerSymbols(
|
|
|
332
331
|
const labelPlaneMatrix = symbolProjection.getLabelPlaneMatrix(coord.posMatrix, pitchWithMap, rotateWithMap, painter.transform, s);
|
|
333
332
|
const glCoordMatrix = symbolProjection.getGlCoordMatrix(coord.posMatrix, pitchWithMap, rotateWithMap, painter.transform, s);
|
|
334
333
|
|
|
335
|
-
const hasVariableAnchors =
|
|
334
|
+
const hasVariableAnchors = hasVariablePlacement && bucket.hasTextData();
|
|
336
335
|
const updateTextFitIcon = layer.layout.get('icon-text-fit') !== 'none' &&
|
|
337
336
|
hasVariableAnchors &&
|
|
338
337
|
bucket.hasIconData();
|
|
@@ -344,7 +343,7 @@ function drawLayerSymbols(
|
|
|
344
343
|
}
|
|
345
344
|
|
|
346
345
|
const matrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor),
|
|
347
|
-
uLabelPlaneMatrix = (alongLine || (isText &&
|
|
346
|
+
uLabelPlaneMatrix = (alongLine || (isText && hasVariablePlacement) || updateTextFitIcon) ? identityMat4 : labelPlaneMatrix,
|
|
348
347
|
uglCoordMatrix = painter.translatePosMatrix(glCoordMatrix, tile, translate, translateAnchor, true);
|
|
349
348
|
|
|
350
349
|
const hasHalo = isSDF && layer.paint.get(isText ? 'text-halo-width' : 'icon-halo-width').constantOr(1) !== 0;
|
package/src/style/properties.ts
CHANGED
|
@@ -317,6 +317,10 @@ export class Layout<Props> {
|
|
|
317
317
|
this._values = (Object.create(properties.defaultPropertyValues) as any);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
+
hasValue<S extends keyof Props>(name: S) {
|
|
321
|
+
return this._values[name].value !== undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
320
324
|
getValue<S extends keyof Props>(name: S) {
|
|
321
325
|
return clone(this._values[name].value);
|
|
322
326
|
}
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
export type CircleLayoutProps = {
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
export type FillLayoutProps = {
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
export type LineLayoutProps = {
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
import {
|
|
@@ -58,6 +53,7 @@ export type SymbolLayoutProps = {
|
|
|
58
53
|
"text-justify": DataDrivenProperty<"auto" | "left" | "center" | "right">,
|
|
59
54
|
"text-radial-offset": DataDrivenProperty<number>,
|
|
60
55
|
"text-variable-anchor": DataConstantProperty<Array<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">>,
|
|
56
|
+
"text-variable-anchor-offset": DataDrivenProperty<VariableAnchorOffsetCollection>,
|
|
61
57
|
"text-anchor": DataDrivenProperty<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
|
|
62
58
|
"text-max-angle": DataConstantProperty<number>,
|
|
63
59
|
"text-writing-mode": DataConstantProperty<Array<"horizontal" | "vertical">>,
|
|
@@ -104,6 +100,7 @@ export type SymbolLayoutPropsPossiblyEvaluated = {
|
|
|
104
100
|
"text-justify": PossiblyEvaluatedPropertyValue<"auto" | "left" | "center" | "right">,
|
|
105
101
|
"text-radial-offset": PossiblyEvaluatedPropertyValue<number>,
|
|
106
102
|
"text-variable-anchor": Array<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
|
|
103
|
+
"text-variable-anchor-offset": PossiblyEvaluatedPropertyValue<VariableAnchorOffsetCollection>,
|
|
107
104
|
"text-anchor": PossiblyEvaluatedPropertyValue<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
|
|
108
105
|
"text-max-angle": number,
|
|
109
106
|
"text-writing-mode": Array<"horizontal" | "vertical">,
|
|
@@ -151,6 +148,7 @@ const getLayout = () => layout = layout || new Properties({
|
|
|
151
148
|
"text-justify": new DataDrivenProperty(styleSpec["layout_symbol"]["text-justify"] as any as StylePropertySpecification),
|
|
152
149
|
"text-radial-offset": new DataDrivenProperty(styleSpec["layout_symbol"]["text-radial-offset"] as any as StylePropertySpecification),
|
|
153
150
|
"text-variable-anchor": new DataConstantProperty(styleSpec["layout_symbol"]["text-variable-anchor"] as any as StylePropertySpecification),
|
|
151
|
+
"text-variable-anchor-offset": new DataDrivenProperty(styleSpec["layout_symbol"]["text-variable-anchor-offset"] as any as StylePropertySpecification),
|
|
154
152
|
"text-anchor": new DataDrivenProperty(styleSpec["layout_symbol"]["text-anchor"] as any as StylePropertySpecification),
|
|
155
153
|
"text-max-angle": new DataConstantProperty(styleSpec["layout_symbol"]["text-max-angle"] as any as StylePropertySpecification),
|
|
156
154
|
"text-writing-mode": new DataConstantProperty(styleSpec["layout_symbol"]["text-writing-mode"] as any as StylePropertySpecification),
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {EvaluationParameters} from '../evaluation_parameters';
|
|
2
|
+
import {ZoomHistory} from '../zoom_history';
|
|
3
|
+
import {SymbolStyleLayer} from './symbol_style_layer';
|
|
4
|
+
import {INVALID_TEXT_OFFSET, evaluateVariableOffset, getTextVariableAnchorOffset} from './variable_text_anchor';
|
|
5
|
+
|
|
6
|
+
describe('evaluateVariableOffset', () => {
|
|
7
|
+
test('fromRadialOffset', () => {
|
|
8
|
+
// Radial offset mode is invoked by using INVALID_TEXT_OFFSET as the Y value
|
|
9
|
+
const srcOffset = [10, INVALID_TEXT_OFFSET] as [number, number];
|
|
10
|
+
|
|
11
|
+
expect(evaluateVariableOffset('center', srcOffset)).toEqual([0, 0]);
|
|
12
|
+
|
|
13
|
+
// Top/bottom offsets are shifted by the default baseline (7)
|
|
14
|
+
expect(evaluateVariableOffset('top', srcOffset)).toEqual([0, 3]);
|
|
15
|
+
expect(evaluateVariableOffset('bottom', srcOffset)).toEqual([0, -3]);
|
|
16
|
+
expect(evaluateVariableOffset('left', srcOffset)).toEqual([10, 0]);
|
|
17
|
+
expect(evaluateVariableOffset('right', srcOffset)).toEqual([-10, 0]);
|
|
18
|
+
|
|
19
|
+
const hypotenuse = 10 / Math.SQRT2;
|
|
20
|
+
expect(evaluateVariableOffset('top-left', srcOffset)).toEqual([expect.closeTo(hypotenuse), expect.closeTo(hypotenuse - 7)]);
|
|
21
|
+
expect(evaluateVariableOffset('top-right', srcOffset)).toEqual([expect.closeTo(-hypotenuse), expect.closeTo(hypotenuse - 7)]);
|
|
22
|
+
expect(evaluateVariableOffset('bottom-left', srcOffset)).toEqual([expect.closeTo(hypotenuse), expect.closeTo(-hypotenuse + 7)]);
|
|
23
|
+
expect(evaluateVariableOffset('bottom-right', srcOffset)).toEqual([expect.closeTo(-hypotenuse), expect.closeTo(-hypotenuse + 7)]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('fromTextOffset', () => {
|
|
27
|
+
const srcOffset = [10, -10] as [number, number];
|
|
28
|
+
|
|
29
|
+
expect(evaluateVariableOffset('center', srcOffset)).toEqual([0, 0]);
|
|
30
|
+
|
|
31
|
+
// Top/bottom offsets are shifted by the default baseline (7)
|
|
32
|
+
expect(evaluateVariableOffset('top', srcOffset)).toEqual([0, 3]);
|
|
33
|
+
expect(evaluateVariableOffset('bottom', srcOffset)).toEqual([0, -3]);
|
|
34
|
+
expect(evaluateVariableOffset('left', srcOffset)).toEqual([10, 0]);
|
|
35
|
+
expect(evaluateVariableOffset('right', srcOffset)).toEqual([-10, 0]);
|
|
36
|
+
expect(evaluateVariableOffset('top-left', srcOffset)).toEqual([10, 3]);
|
|
37
|
+
expect(evaluateVariableOffset('top-right', srcOffset)).toEqual([-10, 3]);
|
|
38
|
+
expect(evaluateVariableOffset('bottom-left', srcOffset)).toEqual([10, -3]);
|
|
39
|
+
expect(evaluateVariableOffset('bottom-right', srcOffset)).toEqual([-10, -3]);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function createSymbolLayer(layerProperties) {
|
|
44
|
+
const layer = new SymbolStyleLayer(layerProperties);
|
|
45
|
+
layer.recalculate({zoom: 0, zoomHistory: {} as ZoomHistory} as EvaluationParameters, []);
|
|
46
|
+
return layer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe('getTextVariableAnchorOffset', () => {
|
|
50
|
+
test('defaults - no props set', () => {
|
|
51
|
+
const props = {};
|
|
52
|
+
const layer = createSymbolLayer(props);
|
|
53
|
+
|
|
54
|
+
expect(getTextVariableAnchorOffset(layer, null, null)).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('text-variable-anchor-offset set', () => {
|
|
58
|
+
const props = {layout: {'text-variable-anchor-offset': ['top', [1, 1], 'bottom', [2, 2]]}};
|
|
59
|
+
const layer = createSymbolLayer(props);
|
|
60
|
+
|
|
61
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
62
|
+
expect(offset).toBeDefined();
|
|
63
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
64
|
+
expect(offset.toString()).toBe('["top",[24,17],"bottom",[48,55]]');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('text-variable-anchor set', () => {
|
|
68
|
+
const props = {layout: {'text-variable-anchor': ['top']}};
|
|
69
|
+
const layer = createSymbolLayer(props);
|
|
70
|
+
|
|
71
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
72
|
+
expect(offset).toBeDefined();
|
|
73
|
+
// Default offset (0, 0) converted to EMs, accounting for baseline shift on Y axis
|
|
74
|
+
expect(offset.toString()).toBe('["top",[0,-7]]');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('text-variable-anchor and text-offset set', () => {
|
|
78
|
+
const props = {layout: {'text-variable-anchor': ['top'], 'text-offset': [1, 1]}};
|
|
79
|
+
const layer = createSymbolLayer(props);
|
|
80
|
+
|
|
81
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
82
|
+
expect(offset).toBeDefined();
|
|
83
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
84
|
+
expect(offset.toString()).toBe('["top",[0,17]]');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('text-variable-anchor and text-radial-offset set', () => {
|
|
88
|
+
const props = {layout: {'text-variable-anchor': ['top'], 'text-radial-offset': 2}};
|
|
89
|
+
const layer = createSymbolLayer(props);
|
|
90
|
+
|
|
91
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
92
|
+
expect(offset).toBeDefined();
|
|
93
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
94
|
+
expect(offset.toString()).toBe('["top",[0,41]]');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('text-variable-anchor, text-offset, and text-radial-offset set', () => {
|
|
98
|
+
const props = {layout: {'text-variable-anchor': ['top'], 'text-offset': [1, 1], 'text-radial-offset': 2}};
|
|
99
|
+
const layer = createSymbolLayer(props);
|
|
100
|
+
|
|
101
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
102
|
+
expect(offset).toBeDefined();
|
|
103
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
104
|
+
expect(offset.toString()).toBe('["top",[0,41]]');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('text-variable-anchor and text-variable-anchor-offset set', () => {
|
|
108
|
+
const props = {layout: {'text-variable-anchor-offset': ['top', [1, 1]], 'text-variable-anchor': ['bottom']}};
|
|
109
|
+
const layer = createSymbolLayer(props);
|
|
110
|
+
|
|
111
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
112
|
+
expect(offset).toBeDefined();
|
|
113
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
114
|
+
expect(offset.toString()).toBe('["top",[24,17]]');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|