maplibre-gl 3.2.1 → 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 +6 -3
- 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 +565 -243
- package/dist/maplibre-gl-dev.js.map +1 -1
- package/dist/maplibre-gl.d.ts +111 -57
- package/dist/maplibre-gl.js +4 -4
- package/dist/maplibre-gl.js.map +1 -1
- package/package.json +20 -20
- package/src/data/array_types.g.ts +110 -24
- package/src/data/bucket/circle_bucket.ts +1 -0
- package/src/data/bucket/line_bucket.ts +1 -0
- package/src/data/bucket/symbol_attributes.ts +7 -1
- package/src/data/bucket/symbol_bucket.ts +5 -1
- package/src/data/feature_index.ts +1 -0
- package/src/data/program_configuration.ts +1 -0
- package/src/data/segment.ts +2 -0
- package/src/geo/transform.ts +1 -0
- package/src/gl/context.ts +1 -0
- package/src/gl/framebuffer.ts +1 -0
- package/src/gl/index_buffer.ts +1 -0
- package/src/gl/render_pool.ts +2 -1
- package/src/gl/vertex_buffer.ts +1 -0
- package/src/render/draw_symbol.ts +8 -9
- package/src/render/image_atlas.ts +1 -0
- package/src/render/line_atlas.ts +1 -0
- package/src/render/painter.ts +1 -0
- package/src/render/program.ts +1 -0
- package/src/render/render_to_texture.ts +31 -14
- package/src/render/terrain.ts +3 -0
- package/src/render/texture.ts +1 -0
- package/src/render/uniform_binding.ts +2 -0
- package/src/render/vertex_array_object.ts +1 -0
- package/src/shaders/symbol_sdf.fragment.glsl +9 -3
- package/src/shaders/symbol_sdf.fragment.glsl.g.ts +1 -1
- package/src/source/canvas_source.ts +1 -3
- package/src/source/geojson_source.ts +1 -3
- package/src/source/image_source.ts +2 -4
- package/src/source/source_cache.ts +1 -0
- package/src/source/source_state.ts +1 -0
- package/src/source/terrain_source_cache.ts +1 -0
- package/src/source/tile.ts +1 -0
- package/src/source/tile_cache.ts +1 -1
- package/src/source/tile_id.ts +1 -0
- package/src/source/vector_tile_worker_source.test.ts +79 -0
- package/src/source/vector_tile_worker_source.ts +26 -1
- package/src/source/worker_source.ts +1 -0
- package/src/style/evaluation_parameters.ts +1 -0
- package/src/style/properties.ts +18 -0
- package/src/style/style.ts +1 -0
- package/src/style/style_glyph.ts +1 -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/collision_index.ts +1 -0
- package/src/symbol/grid_index.ts +1 -0
- package/src/symbol/placement.ts +52 -40
- package/src/symbol/symbol_layout.ts +42 -116
- package/src/ui/camera.ts +8 -8
- package/src/ui/handler/box_zoom.ts +1 -3
- package/src/ui/handler/click_zoom.ts +1 -3
- package/src/ui/handler/keyboard.ts +1 -3
- package/src/ui/handler/scroll_zoom.ts +1 -3
- package/src/ui/handler/shim/dblclick_zoom.ts +1 -3
- package/src/ui/handler/shim/drag_pan.ts +1 -3
- package/src/ui/handler/shim/drag_rotate.ts +1 -3
- package/src/ui/handler/shim/two_fingers_touch.ts +1 -3
- package/src/ui/handler/transform-provider.ts +1 -0
- package/src/ui/handler/two_fingers_touch.ts +1 -3
- package/src/ui/map.ts +7 -6
- package/src/util/ajax.test.ts +33 -0
- package/src/util/ajax.ts +5 -0
- package/src/util/image.ts +1 -0
- package/src/util/image_request.ts +2 -2
- package/src/util/performance.ts +1 -2
- package/src/util/struct_array.ts +5 -1
- package/src/util/test/mock_fetch.ts +51 -0
package/src/symbol/placement.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type {FeatureKey} from './collision_index';
|
|
|
3
3
|
import {EXTENT} from '../data/extent';
|
|
4
4
|
import * as symbolSize from './symbol_size';
|
|
5
5
|
import * as projection from './projection';
|
|
6
|
-
import {getAnchorJustification
|
|
6
|
+
import {getAnchorJustification} from './symbol_layout';
|
|
7
7
|
import {getAnchorAlignment, WritingMode} from './shaping';
|
|
8
8
|
import {mat4} from 'gl-matrix';
|
|
9
9
|
import {pixelsToTileUnits} from '../source/pixels_to_tile_units';
|
|
@@ -17,12 +17,12 @@ import {getOverlapMode, OverlapMode} from '../style/style_layer/overlap_mode';
|
|
|
17
17
|
import type {Tile} from '../source/tile';
|
|
18
18
|
import {SymbolBucket, CollisionArrays, SingleCollisionBox} from '../data/bucket/symbol_bucket';
|
|
19
19
|
|
|
20
|
-
import type {CollisionBoxArray, CollisionVertexArray, SymbolInstance} from '../data/array_types.g';
|
|
20
|
+
import type {CollisionBoxArray, CollisionVertexArray, SymbolInstance, TextAnchorOffset} from '../data/array_types.g';
|
|
21
21
|
import type {FeatureIndex} from '../data/feature_index';
|
|
22
22
|
import type {OverscaledTileID} from '../source/tile_id';
|
|
23
|
-
import type {TextAnchor} from './symbol_layout';
|
|
24
23
|
import {Terrain} from '../render/terrain';
|
|
25
24
|
import {warnOnce} from '../util/util';
|
|
25
|
+
import {TextAnchor, TextAnchorEnum} from '../style/style_layer/variable_text_anchor';
|
|
26
26
|
|
|
27
27
|
class OpacityState {
|
|
28
28
|
opacity: number;
|
|
@@ -147,10 +147,9 @@ function calculateVariableLayoutShift(
|
|
|
147
147
|
const {horizontalAlign, verticalAlign} = getAnchorAlignment(anchor);
|
|
148
148
|
const shiftX = -(horizontalAlign - 0.5) * width;
|
|
149
149
|
const shiftY = -(verticalAlign - 0.5) * height;
|
|
150
|
-
const offset = evaluateVariableOffset(anchor, textOffset);
|
|
151
150
|
return new Point(
|
|
152
|
-
shiftX +
|
|
153
|
-
shiftY +
|
|
151
|
+
shiftX + textOffset[0] * textBoxScale,
|
|
152
|
+
shiftY + textOffset[1] * textBoxScale
|
|
154
153
|
);
|
|
155
154
|
}
|
|
156
155
|
|
|
@@ -339,7 +338,7 @@ export class Placement {
|
|
|
339
338
|
}
|
|
340
339
|
|
|
341
340
|
attemptAnchorPlacement(
|
|
342
|
-
|
|
341
|
+
textAnchorOffset: TextAnchorOffset,
|
|
343
342
|
textBox: SingleCollisionBox,
|
|
344
343
|
width: number,
|
|
345
344
|
height: number,
|
|
@@ -363,7 +362,8 @@ export class Placement {
|
|
|
363
362
|
};
|
|
364
363
|
} {
|
|
365
364
|
|
|
366
|
-
const
|
|
365
|
+
const anchor = TextAnchorEnum[textAnchorOffset.textAnchor] as TextAnchor;
|
|
366
|
+
const textOffset = [textAnchorOffset.textOffset0, textAnchorOffset.textOffset1] as [number, number];
|
|
367
367
|
const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
|
|
368
368
|
|
|
369
369
|
const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(
|
|
@@ -528,7 +528,11 @@ export class Placement {
|
|
|
528
528
|
}
|
|
529
529
|
};
|
|
530
530
|
|
|
531
|
-
|
|
531
|
+
const textAnchorOffsetStart = symbolInstance.textAnchorOffsetStartIndex;
|
|
532
|
+
const textAnchorOffsetEnd = symbolInstance.textAnchorOffsetEndIndex;
|
|
533
|
+
|
|
534
|
+
// If start+end indices match, text-variable-anchor is not in play.
|
|
535
|
+
if (textAnchorOffsetEnd === textAnchorOffsetStart) {
|
|
532
536
|
const placeBox = (collisionTextBox, orientation) => {
|
|
533
537
|
const placedFeature = this.collisionIndex.placeCollisionBox(
|
|
534
538
|
collisionTextBox,
|
|
@@ -561,47 +565,54 @@ export class Placement {
|
|
|
561
565
|
updatePreviousOrientationIfNotPlaced(placed && placed.box && placed.box.length);
|
|
562
566
|
|
|
563
567
|
} else {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
// If this symbol was in the last placement, shift the previously used
|
|
567
|
-
// anchor to the front of the anchor list, only if the previous anchor
|
|
568
|
-
// is still in the anchor list
|
|
569
|
-
if (this.prevPlacement && this.prevPlacement.variableOffsets[symbolInstance.crossTileID]) {
|
|
570
|
-
const prevOffsets = this.prevPlacement.variableOffsets[symbolInstance.crossTileID];
|
|
571
|
-
if (anchors.indexOf(prevOffsets.anchor) > 0) {
|
|
572
|
-
anchors = anchors.filter(anchor => anchor !== prevOffsets.anchor);
|
|
573
|
-
anchors.unshift(prevOffsets.anchor);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
568
|
+
// If this symbol was in the last placement, prefer placement using same anchor, if it's still available
|
|
569
|
+
let prevAnchor = TextAnchorEnum[this.prevPlacement?.variableOffsets[symbolInstance.crossTileID]?.anchor];
|
|
576
570
|
|
|
577
571
|
const placeBoxForVariableAnchors = (collisionTextBox, collisionIconBox, orientation) => {
|
|
578
572
|
const width = collisionTextBox.x2 - collisionTextBox.x1;
|
|
579
573
|
const height = collisionTextBox.y2 - collisionTextBox.y1;
|
|
580
574
|
const textBoxScale = symbolInstance.textBoxScale;
|
|
581
|
-
|
|
582
575
|
const variableIconBox = hasIconTextFit && (iconOverlapMode === 'never') ? collisionIconBox : null;
|
|
583
576
|
|
|
584
577
|
let placedBox: {
|
|
585
578
|
box: Array<number>;
|
|
586
579
|
offscreen: boolean;
|
|
587
580
|
} = {box: [], offscreen: false};
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
if (
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
581
|
+
let placementPasses = (textOverlapMode === 'never') ? 1 : 2;
|
|
582
|
+
let overlapMode: OverlapMode = 'never';
|
|
583
|
+
|
|
584
|
+
if (prevAnchor) {
|
|
585
|
+
placementPasses++;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
for (let pass = 0; pass < placementPasses; pass++) {
|
|
589
|
+
for (let i = textAnchorOffsetStart; i < textAnchorOffsetEnd; i++) {
|
|
590
|
+
const textAnchorOffset = bucket.textAnchorOffsets.get(i);
|
|
591
|
+
|
|
592
|
+
if (prevAnchor && textAnchorOffset.textAnchor !== prevAnchor) {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const result = this.attemptAnchorPlacement(
|
|
597
|
+
textAnchorOffset, collisionTextBox, width, height,
|
|
598
|
+
textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, posMatrix,
|
|
599
|
+
collisionGroup, overlapMode, symbolInstance, bucket, orientation, variableIconBox, getElevation);
|
|
600
|
+
|
|
601
|
+
if (result) {
|
|
602
|
+
placedBox = result.placedGlyphBoxes;
|
|
603
|
+
if (placedBox && placedBox.box && placedBox.box.length) {
|
|
604
|
+
placeText = true;
|
|
605
|
+
shift = result.shift;
|
|
606
|
+
return placedBox;
|
|
607
|
+
}
|
|
603
608
|
}
|
|
604
609
|
}
|
|
610
|
+
|
|
611
|
+
if (prevAnchor) {
|
|
612
|
+
prevAnchor = null;
|
|
613
|
+
} else {
|
|
614
|
+
overlapMode = textOverlapMode;
|
|
615
|
+
}
|
|
605
616
|
}
|
|
606
617
|
|
|
607
618
|
return placedBox;
|
|
@@ -952,11 +963,12 @@ export class Placement {
|
|
|
952
963
|
if (bucket.hasIconCollisionBoxData()) bucket.iconCollisionBox.collisionVertexArray.clear();
|
|
953
964
|
if (bucket.hasTextCollisionBoxData()) bucket.textCollisionBox.collisionVertexArray.clear();
|
|
954
965
|
|
|
955
|
-
const
|
|
966
|
+
const layer = bucket.layers[0];
|
|
967
|
+
const layout = layer.layout;
|
|
956
968
|
const duplicateOpacityState = new JointOpacityState(null, 0, false, false, true);
|
|
957
969
|
const textAllowOverlap = layout.get('text-allow-overlap');
|
|
958
970
|
const iconAllowOverlap = layout.get('icon-allow-overlap');
|
|
959
|
-
const
|
|
971
|
+
const hasVariablePlacement = layer._unevaluatedLayout.hasValue('text-variable-anchor') || layer._unevaluatedLayout.hasValue('text-variable-anchor-offset');
|
|
960
972
|
const rotateWithMap = layout.get('text-rotation-alignment') === 'map';
|
|
961
973
|
const pitchWithMap = layout.get('text-pitch-alignment') === 'map';
|
|
962
974
|
const hasIconTextFit = layout.get('icon-text-fit') !== 'none';
|
|
@@ -1074,7 +1086,7 @@ export class Placement {
|
|
|
1074
1086
|
let shift = new Point(0, 0);
|
|
1075
1087
|
if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
|
|
1076
1088
|
let used = true;
|
|
1077
|
-
if (
|
|
1089
|
+
if (hasVariablePlacement) {
|
|
1078
1090
|
const variableOffset = this.variableOffsets[crossTileID];
|
|
1079
1091
|
if (variableOffset) {
|
|
1080
1092
|
// This will show either the currently placed position or the last
|
|
@@ -19,7 +19,7 @@ import {SIZE_PACK_FACTOR, MAX_PACKED_SIZE, MAX_GLYPH_ICON_SIZE} from './symbol_s
|
|
|
19
19
|
import ONE_EM from './one_em';
|
|
20
20
|
import type {CanonicalTileID} from '../source/tile_id';
|
|
21
21
|
import type {Shaping, PositionedIcon, TextJustify} from './shaping';
|
|
22
|
-
import type {CollisionBoxArray} from '../data/array_types.g';
|
|
22
|
+
import type {CollisionBoxArray, TextAnchorOffsetArray} from '../data/array_types.g';
|
|
23
23
|
import type {SymbolFeature} from '../data/bucket/symbol_bucket';
|
|
24
24
|
import type {StyleImage} from '../style/style_image';
|
|
25
25
|
import type {StyleGlyph} from '../style/style_glyph';
|
|
@@ -31,6 +31,8 @@ import type {PossiblyEvaluatedPropertyValue} from '../style/properties';
|
|
|
31
31
|
import Point from '@mapbox/point-geometry';
|
|
32
32
|
import murmur3 from 'murmurhash-js';
|
|
33
33
|
import {getIconPadding, SymbolPadding} from '../style/style_layer/symbol_style_layer';
|
|
34
|
+
import {VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
35
|
+
import {getTextVariableAnchorOffset, evaluateVariableOffset, INVALID_TEXT_OFFSET, TextAnchor, TextAnchorEnum} from '../style/style_layer/variable_text_anchor';
|
|
34
36
|
|
|
35
37
|
// The symbol layout process needs `text-size` evaluated at up to five different zoom levels, and
|
|
36
38
|
// `icon-size` at up to three:
|
|
@@ -59,98 +61,6 @@ type ShapedTextOrientations = {
|
|
|
59
61
|
horizontal: Record<TextJustify, Shaping>;
|
|
60
62
|
};
|
|
61
63
|
|
|
62
|
-
export type TextAnchor = 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
63
|
-
|
|
64
|
-
// The radial offset is to the edge of the text box
|
|
65
|
-
// In the horizontal direction, the edge of the text box is where glyphs start
|
|
66
|
-
// But in the vertical direction, the glyphs appear to "start" at the baseline
|
|
67
|
-
// We don't actually load baseline data, but we assume an offset of ONE_EM - 17
|
|
68
|
-
// (see "yOffset" in shaping.js)
|
|
69
|
-
const baselineOffset = 7;
|
|
70
|
-
const INVALID_TEXT_OFFSET = Number.POSITIVE_INFINITY;
|
|
71
|
-
|
|
72
|
-
export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, number]) {
|
|
73
|
-
|
|
74
|
-
function fromRadialOffset(anchor: TextAnchor, radialOffset: number) {
|
|
75
|
-
let x = 0, y = 0;
|
|
76
|
-
if (radialOffset < 0) radialOffset = 0; // Ignore negative offset.
|
|
77
|
-
// solve for r where r^2 + r^2 = radialOffset^2
|
|
78
|
-
const hypotenuse = radialOffset / Math.sqrt(2);
|
|
79
|
-
switch (anchor) {
|
|
80
|
-
case 'top-right':
|
|
81
|
-
case 'top-left':
|
|
82
|
-
y = hypotenuse - baselineOffset;
|
|
83
|
-
break;
|
|
84
|
-
case 'bottom-right':
|
|
85
|
-
case 'bottom-left':
|
|
86
|
-
y = -hypotenuse + baselineOffset;
|
|
87
|
-
break;
|
|
88
|
-
case 'bottom':
|
|
89
|
-
y = -radialOffset + baselineOffset;
|
|
90
|
-
break;
|
|
91
|
-
case 'top':
|
|
92
|
-
y = radialOffset - baselineOffset;
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
switch (anchor) {
|
|
97
|
-
case 'top-right':
|
|
98
|
-
case 'bottom-right':
|
|
99
|
-
x = -hypotenuse;
|
|
100
|
-
break;
|
|
101
|
-
case 'top-left':
|
|
102
|
-
case 'bottom-left':
|
|
103
|
-
x = hypotenuse;
|
|
104
|
-
break;
|
|
105
|
-
case 'left':
|
|
106
|
-
x = radialOffset;
|
|
107
|
-
break;
|
|
108
|
-
case 'right':
|
|
109
|
-
x = -radialOffset;
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return [x, y];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function fromTextOffset(anchor: TextAnchor, offsetX: number, offsetY: number) {
|
|
117
|
-
let x = 0, y = 0;
|
|
118
|
-
// Use absolute offset values.
|
|
119
|
-
offsetX = Math.abs(offsetX);
|
|
120
|
-
offsetY = Math.abs(offsetY);
|
|
121
|
-
|
|
122
|
-
switch (anchor) {
|
|
123
|
-
case 'top-right':
|
|
124
|
-
case 'top-left':
|
|
125
|
-
case 'top':
|
|
126
|
-
y = offsetY - baselineOffset;
|
|
127
|
-
break;
|
|
128
|
-
case 'bottom-right':
|
|
129
|
-
case 'bottom-left':
|
|
130
|
-
case 'bottom':
|
|
131
|
-
y = -offsetY + baselineOffset;
|
|
132
|
-
break;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
switch (anchor) {
|
|
136
|
-
case 'top-right':
|
|
137
|
-
case 'bottom-right':
|
|
138
|
-
case 'right':
|
|
139
|
-
x = -offsetX;
|
|
140
|
-
break;
|
|
141
|
-
case 'top-left':
|
|
142
|
-
case 'bottom-left':
|
|
143
|
-
case 'left':
|
|
144
|
-
x = offsetX;
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return [x, y];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return (offset[1] !== INVALID_TEXT_OFFSET) ? fromTextOffset(anchor, offset[0], offset[1]) : fromRadialOffset(anchor, offset[0]);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
64
|
export function performSymbolLayout(args: {
|
|
155
65
|
bucket: SymbolBucket;
|
|
156
66
|
glyphMap: {
|
|
@@ -175,8 +85,9 @@ export function performSymbolLayout(args: {
|
|
|
175
85
|
args.bucket.compareText = {};
|
|
176
86
|
args.bucket.iconsNeedLinear = false;
|
|
177
87
|
|
|
178
|
-
const
|
|
179
|
-
const
|
|
88
|
+
const layer = args.bucket.layers[0];
|
|
89
|
+
const layout = layer.layout;
|
|
90
|
+
const unevaluatedLayoutValues = layer._unevaluatedLayout._values;
|
|
180
91
|
|
|
181
92
|
const sizes: Sizes = {
|
|
182
93
|
// Filled in below, if *SizeData.kind is 'composite'
|
|
@@ -226,16 +137,16 @@ export function performSymbolLayout(args: {
|
|
|
226
137
|
const spacingIfAllowed = allowsLetterSpacing(unformattedText) ? spacing : 0;
|
|
227
138
|
|
|
228
139
|
const textAnchor = layout.get('text-anchor').evaluate(feature, {}, args.canonical);
|
|
229
|
-
const
|
|
140
|
+
const variableAnchorOffset = getTextVariableAnchorOffset(layer, feature, args.canonical);
|
|
230
141
|
|
|
231
|
-
if (!
|
|
142
|
+
if (!variableAnchorOffset) {
|
|
232
143
|
const radialOffset = layout.get('text-radial-offset').evaluate(feature, {}, args.canonical);
|
|
233
144
|
// Layers with variable anchors use the `text-radial-offset` property and the [x, y] offset vector
|
|
234
145
|
// is calculated at placement time instead of layout time
|
|
235
146
|
if (radialOffset) {
|
|
236
147
|
// The style spec says don't use `text-offset` and `text-radial-offset` together
|
|
237
148
|
// but doesn't actually specify what happens if you use both. We go with the radial offset.
|
|
238
|
-
textOffset = evaluateVariableOffset(textAnchor, [radialOffset * ONE_EM, INVALID_TEXT_OFFSET])
|
|
149
|
+
textOffset = evaluateVariableOffset(textAnchor, [radialOffset * ONE_EM, INVALID_TEXT_OFFSET]);
|
|
239
150
|
} else {
|
|
240
151
|
textOffset = (layout.get('text-offset').evaluate(feature, {}, args.canonical).map(t => t * ONE_EM) as [number, number]);
|
|
241
152
|
}
|
|
@@ -261,14 +172,19 @@ export function performSymbolLayout(args: {
|
|
|
261
172
|
};
|
|
262
173
|
|
|
263
174
|
// If this layer uses text-variable-anchor, generate shapings for all justification possibilities.
|
|
264
|
-
if (!textAlongLine &&
|
|
265
|
-
const justifications =
|
|
266
|
-
|
|
267
|
-
|
|
175
|
+
if (!textAlongLine && variableAnchorOffset) {
|
|
176
|
+
const justifications = new Set<TextJustify>();
|
|
177
|
+
|
|
178
|
+
if (textJustify === 'auto') {
|
|
179
|
+
for (let i = 0; i < variableAnchorOffset.values.length; i += 2) {
|
|
180
|
+
justifications.add(getAnchorJustification(variableAnchorOffset.values[i] as TextAnchor));
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
justifications.add(textJustify);
|
|
184
|
+
}
|
|
268
185
|
|
|
269
186
|
let singleLine = false;
|
|
270
|
-
for (
|
|
271
|
-
const justification: TextJustify = justifications[i];
|
|
187
|
+
for (const justification of justifications) {
|
|
272
188
|
if (shapedTextOrientations.horizontal[justification]) continue;
|
|
273
189
|
if (singleLine) {
|
|
274
190
|
// If the shaping for the first justification was only a single line, we
|
|
@@ -486,6 +402,22 @@ function addFeature(bucket: SymbolBucket,
|
|
|
486
402
|
}
|
|
487
403
|
}
|
|
488
404
|
|
|
405
|
+
function addTextVariableAnchorOffsets(textAnchorOffsets: TextAnchorOffsetArray, variableAnchorOffset: VariableAnchorOffsetCollection): [number, number] {
|
|
406
|
+
const startIndex = textAnchorOffsets.length;
|
|
407
|
+
const values = variableAnchorOffset?.values;
|
|
408
|
+
|
|
409
|
+
if (values?.length > 0) {
|
|
410
|
+
for (let i = 0; i < values.length; i += 2) {
|
|
411
|
+
const anchor = TextAnchorEnum[values[i] as TextAnchor];
|
|
412
|
+
const offset = values[i + 1] as [number, number];
|
|
413
|
+
|
|
414
|
+
textAnchorOffsets.emplaceBack(anchor, offset[0], offset[1]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return [startIndex, textAnchorOffsets.length];
|
|
419
|
+
}
|
|
420
|
+
|
|
489
421
|
function addTextVertices(bucket: SymbolBucket,
|
|
490
422
|
anchor: Point,
|
|
491
423
|
shapedText: Shaping,
|
|
@@ -602,15 +534,6 @@ function addSymbol(bucket: SymbolBucket,
|
|
|
602
534
|
const placedTextSymbolIndices: {[k: string]: number} = {};
|
|
603
535
|
let key = murmur3('');
|
|
604
536
|
|
|
605
|
-
let textOffset0 = 0;
|
|
606
|
-
let textOffset1 = 0;
|
|
607
|
-
if (layer._unevaluatedLayout.getValue('text-radial-offset') === undefined) {
|
|
608
|
-
[textOffset0, textOffset1] = (layer.layout.get('text-offset').evaluate(feature, {}, canonical).map(t => t * ONE_EM) as [number, number]);
|
|
609
|
-
} else {
|
|
610
|
-
textOffset0 = layer.layout.get('text-radial-offset').evaluate(feature, {}, canonical) * ONE_EM;
|
|
611
|
-
textOffset1 = INVALID_TEXT_OFFSET;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
537
|
if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) {
|
|
615
538
|
const textRotation = layer.layout.get('text-rotate').evaluate(feature, {}, canonical);
|
|
616
539
|
const verticalTextRotation = textRotation + 90.0;
|
|
@@ -763,6 +686,9 @@ function addSymbol(bucket: SymbolBucket,
|
|
|
763
686
|
bucket.addToSortKeyRanges(bucket.symbolInstances.length, feature.sortKey as number);
|
|
764
687
|
}
|
|
765
688
|
|
|
689
|
+
const variableAnchorOffset = getTextVariableAnchorOffset(layer, feature, canonical);
|
|
690
|
+
const [textAnchorOffsetStartIndex, textAnchorOffsetEndIndex] = addTextVariableAnchorOffsets(bucket.textAnchorOffsets, variableAnchorOffset);
|
|
691
|
+
|
|
766
692
|
bucket.symbolInstances.emplaceBack(
|
|
767
693
|
anchor.x,
|
|
768
694
|
anchor.y,
|
|
@@ -789,9 +715,9 @@ function addSymbol(bucket: SymbolBucket,
|
|
|
789
715
|
useRuntimeCollisionCircles,
|
|
790
716
|
0,
|
|
791
717
|
textBoxScale,
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
718
|
+
collisionCircleDiameter,
|
|
719
|
+
textAnchorOffsetStartIndex,
|
|
720
|
+
textAnchorOffsetEndIndex);
|
|
795
721
|
}
|
|
796
722
|
|
|
797
723
|
function anchorIsTooClose(bucket: SymbolBucket, text: string, repeatDistance: number, anchor: Point) {
|
package/src/ui/camera.ts
CHANGED
|
@@ -263,30 +263,30 @@ export abstract class Camera extends Evented {
|
|
|
263
263
|
_easeFrameId: TaskID;
|
|
264
264
|
|
|
265
265
|
/**
|
|
266
|
-
* @
|
|
266
|
+
* @internal
|
|
267
267
|
* holds the geographical coordinate of the target
|
|
268
268
|
*/
|
|
269
269
|
_elevationCenter: LngLat;
|
|
270
270
|
/**
|
|
271
|
-
* @
|
|
271
|
+
* @internal
|
|
272
272
|
* holds the targ altitude value, = center elevation of the target.
|
|
273
273
|
* This value may changes during flight, because new terrain-tiles loads during flight.
|
|
274
274
|
*/
|
|
275
275
|
_elevationTarget: number;
|
|
276
276
|
/**
|
|
277
|
-
* @
|
|
277
|
+
* @internal
|
|
278
278
|
* holds the start altitude value, = center elevation before animation begins
|
|
279
279
|
* this value will recalculated during flight in respect of changing _elevationTarget values,
|
|
280
280
|
* so the linear interpolation between start and target keeps smooth and without jumps.
|
|
281
281
|
*/
|
|
282
282
|
_elevationStart: number;
|
|
283
283
|
/**
|
|
284
|
-
* @
|
|
284
|
+
* @internal
|
|
285
285
|
* Saves the current state of the elevation freeze - this is used during map movement to prevent "rocky" camera movement.
|
|
286
286
|
*/
|
|
287
287
|
_elevationFreeze: boolean;
|
|
288
288
|
/**
|
|
289
|
-
* @
|
|
289
|
+
* @internal
|
|
290
290
|
* Used to track accumulated changes during continuous interaction
|
|
291
291
|
*/
|
|
292
292
|
_requestedCameraState?: Transform;
|
|
@@ -645,6 +645,7 @@ export abstract class Camera extends Evented {
|
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
/**
|
|
648
|
+
* @internal
|
|
648
649
|
* Calculate the center of these two points in the viewport and use
|
|
649
650
|
* the highest zoom level up to and including `Map#getMaxZoom()` that fits
|
|
650
651
|
* the points in the viewport at the specified bearing.
|
|
@@ -654,7 +655,6 @@ export abstract class Camera extends Evented {
|
|
|
654
655
|
* @param options - the camera options
|
|
655
656
|
* @returns If map is able to fit to provided bounds, returns `center`, `zoom`, and `bearing`.
|
|
656
657
|
* If map is unable to fit, method will warn and return undefined.
|
|
657
|
-
* @hidden
|
|
658
658
|
* @example
|
|
659
659
|
* ```ts
|
|
660
660
|
* let p0 = [-79, 43];
|
|
@@ -1085,11 +1085,11 @@ export abstract class Camera extends Evented {
|
|
|
1085
1085
|
}
|
|
1086
1086
|
|
|
1087
1087
|
/**
|
|
1088
|
+
* @internal
|
|
1088
1089
|
* Called when the camera is about to be manipulated.
|
|
1089
1090
|
* If `transformCameraUpdate` is specified, a copy of the current transform is created to track the accumulated changes.
|
|
1090
1091
|
* This underlying transform represents the "desired state" proposed by input handlers / animations / UI controls.
|
|
1091
1092
|
* It may differ from the state used for rendering (`this.transform`).
|
|
1092
|
-
* @hidden
|
|
1093
1093
|
* @returns Transform to apply changes to
|
|
1094
1094
|
*/
|
|
1095
1095
|
_getTransformForUpdate(): Transform {
|
|
@@ -1102,8 +1102,8 @@ export abstract class Camera extends Evented {
|
|
|
1102
1102
|
}
|
|
1103
1103
|
|
|
1104
1104
|
/**
|
|
1105
|
+
* @internal
|
|
1105
1106
|
* Called after the camera is done being manipulated.
|
|
1106
|
-
* @hidden
|
|
1107
1107
|
* @param tr - the requested camera end state
|
|
1108
1108
|
* Call `transformCameraUpdate` if present, and then apply the "approved" changes.
|
|
1109
1109
|
*/
|
|
@@ -33,9 +33,7 @@ export class KeyboardHandler implements Handler {
|
|
|
33
33
|
_pitchStep: number;
|
|
34
34
|
_rotationDisabled: boolean;
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* @hidden
|
|
38
|
-
*/
|
|
36
|
+
/** @internal */
|
|
39
37
|
constructor(map: Map) {
|
|
40
38
|
this._tr = new TransformProvider(map);
|
|
41
39
|
const stepOptions = defaultOptions;
|
|
@@ -62,9 +62,7 @@ export class ScrollZoomHandler implements Handler {
|
|
|
62
62
|
_defaultZoomRate: number;
|
|
63
63
|
_wheelZoomRate: number;
|
|
64
64
|
|
|
65
|
-
/**
|
|
66
|
-
* @hidden
|
|
67
|
-
*/
|
|
65
|
+
/** @internal */
|
|
68
66
|
constructor(map: Map, triggerRenderFrame: () => void) {
|
|
69
67
|
this._map = map;
|
|
70
68
|
this._tr = new TransformProvider(map);
|
|
@@ -12,9 +12,7 @@ export class DoubleClickZoomHandler {
|
|
|
12
12
|
_clickZoom: ClickZoomHandler;
|
|
13
13
|
_tapZoom: TapZoomHandler;
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
* @hidden
|
|
17
|
-
*/
|
|
15
|
+
/** @internal */
|
|
18
16
|
constructor(clickZoom: ClickZoomHandler, TapZoom: TapZoomHandler) {
|
|
19
17
|
this._clickZoom = clickZoom;
|
|
20
18
|
this._tapZoom = TapZoom;
|
|
@@ -41,9 +41,7 @@ export class DragPanHandler {
|
|
|
41
41
|
_touchPan: TouchPanHandler;
|
|
42
42
|
_inertiaOptions: DragPanOptions | boolean;
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
* @hidden
|
|
46
|
-
*/
|
|
44
|
+
/** @internal */
|
|
47
45
|
constructor(el: HTMLElement, mousePan: MousePanHandler, touchPan: TouchPanHandler) {
|
|
48
46
|
this._el = el;
|
|
49
47
|
this._mousePan = mousePan;
|
|
@@ -20,9 +20,7 @@ export class DragRotateHandler {
|
|
|
20
20
|
_mousePitch: MousePitchHandler;
|
|
21
21
|
_pitchWithRotate: boolean;
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
* @hidden
|
|
25
|
-
*/
|
|
23
|
+
/** @internal */
|
|
26
24
|
constructor(options: DragRotateHandlerOptions, mouseRotate: MouseRotateHandler, mousePitch: MousePitchHandler) {
|
|
27
25
|
this._pitchWithRotate = options.pitchWithRotate;
|
|
28
26
|
this._mouseRotate = mouseRotate;
|
|
@@ -19,9 +19,7 @@ export class TwoFingersTouchZoomRotateHandler {
|
|
|
19
19
|
_rotationDisabled: boolean;
|
|
20
20
|
_enabled: boolean;
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* @hidden
|
|
24
|
-
*/
|
|
22
|
+
/** @internal */
|
|
25
23
|
constructor(el: HTMLElement, touchZoom: TwoFingersTouchZoomHandler, touchRotate: TwoFingersTouchRotateHandler, tapDragZoom: TapDragZoomHandler) {
|
|
26
24
|
this._el = el;
|
|
27
25
|
this._touchZoom = touchZoom;
|
|
@@ -5,6 +5,7 @@ import Point from '@mapbox/point-geometry';
|
|
|
5
5
|
import {LngLat} from '../../geo/lng_lat';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
+
* @internal
|
|
8
9
|
* Shared utilities for the Handler classes to access the correct camera state.
|
|
9
10
|
* If Camera.transformCameraUpdate is specified, the "desired state" of camera may differ from the state used for rendering.
|
|
10
11
|
* The handlers need the "desired state" to track accumulated changes.
|
package/src/ui/map.ts
CHANGED
|
@@ -504,7 +504,7 @@ export class Map extends Camera {
|
|
|
504
504
|
_terrainDataCallback: (e: MapStyleDataEvent | MapSourceDataEvent) => void;
|
|
505
505
|
|
|
506
506
|
/**
|
|
507
|
-
* @
|
|
507
|
+
* @internal
|
|
508
508
|
* image queue throttling handle. To be used later when clean up
|
|
509
509
|
*/
|
|
510
510
|
_imageQueueHandle: number;
|
|
@@ -707,9 +707,9 @@ export class Map extends Camera {
|
|
|
707
707
|
}
|
|
708
708
|
|
|
709
709
|
/**
|
|
710
|
+
* @internal
|
|
710
711
|
* Returns a unique number for this map instance which is used for the MapLoadEvent
|
|
711
712
|
* to make sure we only fire one event per instantiated map object.
|
|
712
|
-
* @hidden
|
|
713
713
|
* @returns the uniq map ID
|
|
714
714
|
*/
|
|
715
715
|
_getMapId() {
|
|
@@ -868,9 +868,9 @@ export class Map extends Camera {
|
|
|
868
868
|
}
|
|
869
869
|
|
|
870
870
|
/**
|
|
871
|
+
* @internal
|
|
871
872
|
* Return the map's pixel ratio eventually scaled down to respect maxCanvasSize.
|
|
872
873
|
* Internally you should use this and not getPixelRatio().
|
|
873
|
-
* @hidden
|
|
874
874
|
*/
|
|
875
875
|
_getClampedPixelRatio(width: number, height: number): number {
|
|
876
876
|
const {0: maxCanvasWidth, 1: maxCanvasHeight} = this._maxCanvasSize;
|
|
@@ -3070,9 +3070,9 @@ export class Map extends Camera {
|
|
|
3070
3070
|
}
|
|
3071
3071
|
|
|
3072
3072
|
/**
|
|
3073
|
+
* @internal
|
|
3073
3074
|
* Update this map's style and sources, and re-render the map.
|
|
3074
3075
|
*
|
|
3075
|
-
* @hidden
|
|
3076
3076
|
* @param updateStyle - mark the map's style for reprocessing as
|
|
3077
3077
|
* well as its sources
|
|
3078
3078
|
* @returns `this`
|
|
@@ -3088,9 +3088,10 @@ export class Map extends Camera {
|
|
|
3088
3088
|
}
|
|
3089
3089
|
|
|
3090
3090
|
/**
|
|
3091
|
+
* @internal
|
|
3091
3092
|
* Request that the given callback be executed during the next render
|
|
3092
3093
|
* frame. Schedule a render frame if one is not already scheduled.
|
|
3093
|
-
*
|
|
3094
|
+
*
|
|
3094
3095
|
* @returns An id that can be used to cancel the callback
|
|
3095
3096
|
*/
|
|
3096
3097
|
_requestRenderFrame(callback: () => void): TaskID {
|
|
@@ -3103,6 +3104,7 @@ export class Map extends Camera {
|
|
|
3103
3104
|
}
|
|
3104
3105
|
|
|
3105
3106
|
/**
|
|
3107
|
+
* @internal
|
|
3106
3108
|
* Call when a (re-)render of the map is required:
|
|
3107
3109
|
* - The style has changed (`setPaintProperty()`, etc.)
|
|
3108
3110
|
* - Source data has changed (e.g. tiles have finished loading)
|
|
@@ -3110,7 +3112,6 @@ export class Map extends Camera {
|
|
|
3110
3112
|
* - A transition is in progress
|
|
3111
3113
|
*
|
|
3112
3114
|
* @param paintStartTimeStamp - The time when the animation frame began executing.
|
|
3113
|
-
* @hidden
|
|
3114
3115
|
*
|
|
3115
3116
|
* @returns `this`
|
|
3116
3117
|
*/
|