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.
Files changed (88) hide show
  1. package/build/generate-struct-arrays.ts +6 -3
  2. package/build/generate-style-code.ts +7 -8
  3. package/dist/maplibre-gl-csp-worker.js +1 -1
  4. package/dist/maplibre-gl-csp-worker.js.map +1 -1
  5. package/dist/maplibre-gl-csp.js +1 -1
  6. package/dist/maplibre-gl-csp.js.map +1 -1
  7. package/dist/maplibre-gl-dev.js +565 -243
  8. package/dist/maplibre-gl-dev.js.map +1 -1
  9. package/dist/maplibre-gl.d.ts +111 -57
  10. package/dist/maplibre-gl.js +4 -4
  11. package/dist/maplibre-gl.js.map +1 -1
  12. package/package.json +20 -20
  13. package/src/data/array_types.g.ts +110 -24
  14. package/src/data/bucket/circle_bucket.ts +1 -0
  15. package/src/data/bucket/line_bucket.ts +1 -0
  16. package/src/data/bucket/symbol_attributes.ts +7 -1
  17. package/src/data/bucket/symbol_bucket.ts +5 -1
  18. package/src/data/feature_index.ts +1 -0
  19. package/src/data/program_configuration.ts +1 -0
  20. package/src/data/segment.ts +2 -0
  21. package/src/geo/transform.ts +1 -0
  22. package/src/gl/context.ts +1 -0
  23. package/src/gl/framebuffer.ts +1 -0
  24. package/src/gl/index_buffer.ts +1 -0
  25. package/src/gl/render_pool.ts +2 -1
  26. package/src/gl/vertex_buffer.ts +1 -0
  27. package/src/render/draw_symbol.ts +8 -9
  28. package/src/render/image_atlas.ts +1 -0
  29. package/src/render/line_atlas.ts +1 -0
  30. package/src/render/painter.ts +1 -0
  31. package/src/render/program.ts +1 -0
  32. package/src/render/render_to_texture.ts +31 -14
  33. package/src/render/terrain.ts +3 -0
  34. package/src/render/texture.ts +1 -0
  35. package/src/render/uniform_binding.ts +2 -0
  36. package/src/render/vertex_array_object.ts +1 -0
  37. package/src/shaders/symbol_sdf.fragment.glsl +9 -3
  38. package/src/shaders/symbol_sdf.fragment.glsl.g.ts +1 -1
  39. package/src/source/canvas_source.ts +1 -3
  40. package/src/source/geojson_source.ts +1 -3
  41. package/src/source/image_source.ts +2 -4
  42. package/src/source/source_cache.ts +1 -0
  43. package/src/source/source_state.ts +1 -0
  44. package/src/source/terrain_source_cache.ts +1 -0
  45. package/src/source/tile.ts +1 -0
  46. package/src/source/tile_cache.ts +1 -1
  47. package/src/source/tile_id.ts +1 -0
  48. package/src/source/vector_tile_worker_source.test.ts +79 -0
  49. package/src/source/vector_tile_worker_source.ts +26 -1
  50. package/src/source/worker_source.ts +1 -0
  51. package/src/style/evaluation_parameters.ts +1 -0
  52. package/src/style/properties.ts +18 -0
  53. package/src/style/style.ts +1 -0
  54. package/src/style/style_glyph.ts +1 -0
  55. package/src/style/style_layer/background_style_layer_properties.g.ts +1 -6
  56. package/src/style/style_layer/circle_style_layer_properties.g.ts +1 -6
  57. package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +1 -6
  58. package/src/style/style_layer/fill_style_layer_properties.g.ts +1 -6
  59. package/src/style/style_layer/heatmap_style_layer_properties.g.ts +1 -6
  60. package/src/style/style_layer/hillshade_style_layer_properties.g.ts +1 -6
  61. package/src/style/style_layer/line_style_layer_properties.g.ts +1 -6
  62. package/src/style/style_layer/raster_style_layer_properties.g.ts +1 -6
  63. package/src/style/style_layer/symbol_style_layer_properties.g.ts +4 -6
  64. package/src/style/style_layer/variable_text_anchor.test.ts +117 -0
  65. package/src/style/style_layer/variable_text_anchor.ts +163 -0
  66. package/src/symbol/collision_index.ts +1 -0
  67. package/src/symbol/grid_index.ts +1 -0
  68. package/src/symbol/placement.ts +52 -40
  69. package/src/symbol/symbol_layout.ts +42 -116
  70. package/src/ui/camera.ts +8 -8
  71. package/src/ui/handler/box_zoom.ts +1 -3
  72. package/src/ui/handler/click_zoom.ts +1 -3
  73. package/src/ui/handler/keyboard.ts +1 -3
  74. package/src/ui/handler/scroll_zoom.ts +1 -3
  75. package/src/ui/handler/shim/dblclick_zoom.ts +1 -3
  76. package/src/ui/handler/shim/drag_pan.ts +1 -3
  77. package/src/ui/handler/shim/drag_rotate.ts +1 -3
  78. package/src/ui/handler/shim/two_fingers_touch.ts +1 -3
  79. package/src/ui/handler/transform-provider.ts +1 -0
  80. package/src/ui/handler/two_fingers_touch.ts +1 -3
  81. package/src/ui/map.ts +7 -6
  82. package/src/util/ajax.test.ts +33 -0
  83. package/src/util/ajax.ts +5 -0
  84. package/src/util/image.ts +1 -0
  85. package/src/util/image_request.ts +2 -2
  86. package/src/util/performance.ts +1 -2
  87. package/src/util/struct_array.ts +5 -1
  88. package/src/util/test/mock_fetch.ts +51 -0
@@ -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, evaluateVariableOffset} from './symbol_layout';
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 + offset[0] * textBoxScale,
153
- shiftY + offset[1] * textBoxScale
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
- anchor: TextAnchor,
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 textOffset = [symbolInstance.textOffset0, symbolInstance.textOffset1] as [number, number];
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
- if (!layout.get('text-variable-anchor')) {
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
- let anchors = layout.get('text-variable-anchor');
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
- const placementAttempts = (textOverlapMode !== 'never') ? anchors.length * 2 : anchors.length;
589
- for (let i = 0; i < placementAttempts; ++i) {
590
- const anchor = anchors[i % anchors.length];
591
- const overlapMode = (i >= anchors.length) ? textOverlapMode : 'never';
592
- const result = this.attemptAnchorPlacement(
593
- anchor, collisionTextBox, width, height,
594
- textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, posMatrix,
595
- collisionGroup, overlapMode, symbolInstance, bucket, orientation, variableIconBox, getElevation);
596
-
597
- if (result) {
598
- placedBox = result.placedGlyphBoxes;
599
- if (placedBox && placedBox.box && placedBox.box.length) {
600
- placeText = true;
601
- shift = result.shift;
602
- break;
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 layout = bucket.layers[0].layout;
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 variablePlacement = layout.get('text-variable-anchor');
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 (variablePlacement) {
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 layout = args.bucket.layers[0].layout;
179
- const unevaluatedLayoutValues = args.bucket.layers[0]._unevaluatedLayout._values;
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 variableTextAnchor = layout.get('text-variable-anchor');
140
+ const variableAnchorOffset = getTextVariableAnchorOffset(layer, feature, args.canonical);
230
141
 
231
- if (!variableTextAnchor) {
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]) as [number, number];
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 && variableTextAnchor) {
265
- const justifications = textJustify === 'auto' ?
266
- variableTextAnchor.map(a => getAnchorJustification(a)) :
267
- [textJustify];
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 (let i = 0; i < justifications.length; i++) {
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
- textOffset0,
793
- textOffset1,
794
- collisionCircleDiameter);
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
- * @hidden
266
+ * @internal
267
267
  * holds the geographical coordinate of the target
268
268
  */
269
269
  _elevationCenter: LngLat;
270
270
  /**
271
- * @hidden
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
- * @hidden
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
- * @hidden
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
- * @hidden
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
  */
@@ -25,9 +25,7 @@ export class BoxZoomHandler implements Handler {
25
25
  _box: HTMLElement;
26
26
  _clickTolerance: number;
27
27
 
28
- /**
29
- * @hidden
30
- */
28
+ /** @internal */
31
29
  constructor(map: Map, options: {
32
30
  clickTolerance: number;
33
31
  }) {
@@ -13,9 +13,7 @@ export class ClickZoomHandler implements Handler {
13
13
  _enabled: boolean;
14
14
  _active: boolean;
15
15
 
16
- /**
17
- * @hidden
18
- */
16
+ /** @internal */
19
17
  constructor(map: Map) {
20
18
  this._tr = new TransformProvider(map);
21
19
  this.reset();
@@ -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.
@@ -27,9 +27,7 @@ abstract class TwoFingersTouchHandler implements Handler {
27
27
  _startVector: Point;
28
28
  _aroundCenter: boolean;
29
29
 
30
- /**
31
- * @hidden
32
- */
30
+ /** @internal */
33
31
  constructor() {
34
32
  this.reset();
35
33
  }
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
- * @hidden
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
- * @hidden
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
  */