@mapwhit/tilerenderer 1.2.2 → 1.4.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 (40) hide show
  1. package/build/min/package.json +1 -1
  2. package/package.json +1 -1
  3. package/src/data/array_types.js +115 -64
  4. package/src/data/bucket/circle_bucket.js +42 -5
  5. package/src/data/bucket/fill_bucket.js +31 -13
  6. package/src/data/bucket/fill_extrusion_bucket.js +8 -6
  7. package/src/data/bucket/line_bucket.js +38 -14
  8. package/src/data/bucket/symbol_attributes.js +13 -5
  9. package/src/data/bucket/symbol_bucket.js +87 -33
  10. package/src/data/bucket/symbol_collision_buffers.js +1 -1
  11. package/src/data/bucket.js +3 -1
  12. package/src/data/feature_index.js +24 -11
  13. package/src/data/segment.js +15 -7
  14. package/src/render/draw_circle.js +45 -4
  15. package/src/render/draw_symbol.js +190 -22
  16. package/src/render/painter.js +1 -1
  17. package/src/source/geojson_source.js +118 -21
  18. package/src/source/geojson_source_diff.js +148 -0
  19. package/src/source/geojson_tiler.js +89 -0
  20. package/src/source/source.js +16 -5
  21. package/src/source/source_cache.js +6 -6
  22. package/src/source/source_state.js +4 -2
  23. package/src/source/tile.js +5 -3
  24. package/src/source/vector_tile_source.js +2 -0
  25. package/src/source/worker_tile.js +4 -2
  26. package/src/style/pauseable_placement.js +39 -7
  27. package/src/style/style.js +86 -34
  28. package/src/style/style_layer/circle_style_layer_properties.js +8 -1
  29. package/src/style/style_layer/fill_style_layer_properties.js +8 -1
  30. package/src/style/style_layer/line_style_layer_properties.js +4 -0
  31. package/src/style/style_layer/symbol_style_layer_properties.js +17 -2
  32. package/src/style-spec/reference/v8.json +161 -4
  33. package/src/symbol/one_em.js +4 -0
  34. package/src/symbol/placement.js +406 -173
  35. package/src/symbol/projection.js +3 -3
  36. package/src/symbol/quads.js +1 -6
  37. package/src/symbol/shaping.js +16 -27
  38. package/src/symbol/symbol_layout.js +243 -81
  39. package/src/util/vectortile_to_geojson.js +3 -4
  40. package/src/source/geojson_worker_source.js +0 -97
@@ -324,8 +324,8 @@ function placeGlyphsAlongLine(
324
324
  aspectRatio
325
325
  ) {
326
326
  const fontScale = fontSize / 24;
327
- const lineOffsetX = symbol.lineOffsetX * fontSize;
328
- const lineOffsetY = symbol.lineOffsetY * fontSize;
327
+ const lineOffsetX = symbol.lineOffsetX * fontScale;
328
+ const lineOffsetY = symbol.lineOffsetY * fontScale;
329
329
 
330
330
  let placedGlyphs;
331
331
  if (symbol.numGlyphs > 1) {
@@ -580,7 +580,7 @@ const hiddenGlyphAttributes = new Float32Array([
580
580
 
581
581
  // Hide them by moving them offscreen. We still need to add them to the buffer
582
582
  // because the dynamic buffer is paired with a static buffer that doesn't get updated.
583
- function hideGlyphs(num, dynamicLayoutVertexArray) {
583
+ export function hideGlyphs(num, dynamicLayoutVertexArray) {
584
584
  for (let i = 0; i < num; i++) {
585
585
  const offset = dynamicLayoutVertexArray.length;
586
586
  dynamicLayoutVertexArray.resize(offset + 4);
@@ -91,13 +91,8 @@ export function getIconQuads(anchor, shapedIcon, layer, alongLine, shapedText, f
91
91
  * Create the quads used for rendering a text label.
92
92
  * @private
93
93
  */
94
- export function getGlyphQuads(anchor, shaping, layer, alongLine, feature, positions) {
95
- const oneEm = 24;
94
+ export function getGlyphQuads(anchor, shaping, textOffset, layer, alongLine, feature, positions) {
96
95
  const textRotate = (layer._layout.get('text-rotate').evaluate(feature, {}) * Math.PI) / 180;
97
- const textOffset = layer._layout
98
- .get('text-offset')
99
- .evaluate(feature, {})
100
- .map(t => t * oneEm);
101
96
 
102
97
  const positionedGlyphs = shaping.positionedGlyphs;
103
98
  const quads = [];
@@ -1,6 +1,7 @@
1
1
  import { plugin as rtlTextPlugin } from '../source/rtl_text_plugin.js';
2
2
  import { charAllowsIdeographicBreaking, charHasUprightVerticalOrientation } from '../util/script_detection.js';
3
3
  import verticalizePunctuation from '../util/verticalize_punctuation.js';
4
+ import ONE_EM from './one_em.js';
4
5
 
5
6
  export const WritingMode = {
6
7
  horizontal: 1,
@@ -102,7 +103,6 @@ export function shapeText(
102
103
  textJustify,
103
104
  spacing,
104
105
  translate,
105
- verticalHeight,
106
106
  writingMode
107
107
  ) {
108
108
  const logicalInput = TaggedString.fromFeature(text, defaultFontStack);
@@ -110,17 +110,6 @@ export function shapeText(
110
110
  logicalInput.verticalizePunctuation();
111
111
  }
112
112
 
113
- const positionedGlyphs = [];
114
- const shaping = {
115
- positionedGlyphs,
116
- text: logicalInput,
117
- top: translate[1],
118
- bottom: translate[1],
119
- left: translate[0],
120
- right: translate[0],
121
- writingMode
122
- };
123
-
124
113
  let lines;
125
114
 
126
115
  const { processBidirectionalText, processStyledBidirectionalText } = rtlTextPlugin;
@@ -160,13 +149,23 @@ export function shapeText(
160
149
  lines = breakLines(logicalInput, determineLineBreaks(logicalInput, spacing, maxWidth, glyphs));
161
150
  }
162
151
 
163
- shapeLines(shaping, glyphs, lines, lineHeight, textAnchor, textJustify, writingMode, spacing, verticalHeight);
152
+ const positionedGlyphs = [];
153
+ const shaping = {
154
+ positionedGlyphs,
155
+ text: logicalInput.toString(),
156
+ top: translate[1],
157
+ bottom: translate[1],
158
+ left: translate[0],
159
+ right: translate[0],
160
+ writingMode,
161
+ lineCount: lines.length
162
+ };
164
163
 
164
+ shapeLines(shaping, glyphs, lines, lineHeight, textAnchor, textJustify, writingMode, spacing);
165
165
  if (!positionedGlyphs.length) {
166
166
  return false;
167
167
  }
168
168
 
169
- shaping.text = shaping.text.toString();
170
169
  return shaping;
171
170
  }
172
171
 
@@ -323,7 +322,7 @@ function determineLineBreaks(logicalInput, spacing, maxWidth, glyphMap) {
323
322
  return leastBadBreaks(evaluateBreak(logicalInput.length(), currentX, targetWidth, potentialLineBreaks, 0, true));
324
323
  }
325
324
 
326
- function getAnchorAlignment(anchor) {
325
+ export function getAnchorAlignment(anchor) {
327
326
  let horizontalAlign = 0.5;
328
327
  let verticalAlign = 0.5;
329
328
 
@@ -356,17 +355,7 @@ function getAnchorAlignment(anchor) {
356
355
  return { horizontalAlign, verticalAlign };
357
356
  }
358
357
 
359
- function shapeLines(
360
- shaping,
361
- glyphMap,
362
- lines,
363
- lineHeight,
364
- textAnchor,
365
- textJustify,
366
- writingMode,
367
- spacing,
368
- verticalHeight
369
- ) {
358
+ function shapeLines(shaping, glyphMap, lines, lineHeight, textAnchor, textJustify, writingMode, spacing) {
370
359
  // the y offset *should* be part of the font metadata
371
360
  const yOffset = -17;
372
361
 
@@ -422,7 +411,7 @@ function shapeLines(
422
411
  scale: section.scale,
423
412
  fontStack: section.fontStack
424
413
  });
425
- x += verticalHeight * section.scale + spacing;
414
+ x += ONE_EM * section.scale + spacing;
426
415
  }
427
416
  }
428
417
 
@@ -9,6 +9,7 @@ import warn from '../util/warn.js';
9
9
  import Anchor from './anchor.js';
10
10
  import CollisionFeature from './collision_feature.js';
11
11
  import { getAnchors, getCenterAnchor } from './get_anchors.js';
12
+ import ONE_EM from './one_em.js';
12
13
  import { getGlyphQuads, getIconQuads } from './quads.js';
13
14
  import { shapeIcon, shapeText, WritingMode } from './shaping.js';
14
15
 
@@ -27,6 +28,56 @@ import { shapeIcon, shapeText, WritingMode } from './shaping.js';
27
28
  // (1) and (2) are stored in `bucket.layers[0]._layout`. The remainder are below.
28
29
  //
29
30
 
31
+ // The radial offset is to the edge of the text box
32
+ // In the horizontal direction, the edge of the text box is where glyphs start
33
+ // But in the vertical direction, the glyphs appear to "start" at the baseline
34
+ // We don't actually load baseline data, but we assume an offset of ONE_EM - 17
35
+ // (see "yOffset" in shaping.js)
36
+ const baselineOffset = 7;
37
+
38
+ export function evaluateRadialOffset(anchor, radialOffset) {
39
+ let x = 0;
40
+ let y = 0;
41
+ // solve for r where r^2 + r^2 = radialOffset^2
42
+ const hypotenuse = radialOffset / Math.sqrt(2);
43
+
44
+ switch (anchor) {
45
+ case 'top-right':
46
+ case 'top-left':
47
+ y = hypotenuse - baselineOffset;
48
+ break;
49
+ case 'bottom-right':
50
+ case 'bottom-left':
51
+ y = -hypotenuse + baselineOffset;
52
+ break;
53
+ case 'bottom':
54
+ y = -radialOffset + baselineOffset;
55
+ break;
56
+ case 'top':
57
+ y = radialOffset - baselineOffset;
58
+ break;
59
+ }
60
+
61
+ switch (anchor) {
62
+ case 'top-right':
63
+ case 'bottom-right':
64
+ x = -hypotenuse;
65
+ break;
66
+ case 'top-left':
67
+ case 'bottom-left':
68
+ x = hypotenuse;
69
+ break;
70
+ case 'left':
71
+ x = radialOffset;
72
+ break;
73
+ case 'right':
74
+ x = -radialOffset;
75
+ break;
76
+ }
77
+
78
+ return [x, y];
79
+ }
80
+
30
81
  export function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap, imagePositions, showCollisionBoxes) {
31
82
  bucket.createArrays();
32
83
 
@@ -64,8 +115,7 @@ export function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap,
64
115
  );
65
116
  sizes.textMaxSize = unevaluatedLayoutValues['text-size'].possiblyEvaluate(new EvaluationParameters(18));
66
117
 
67
- const oneEm = 24;
68
- const lineHeight = layout.get('text-line-height') * oneEm;
118
+ const lineHeight = layout.get('text-line-height') * ONE_EM;
69
119
  const textAlongLine = layout.get('text-rotation-alignment') === 'map' && layout.get('symbol-placement') !== 'point';
70
120
  const keepUpright = layout.get('text-keep-upright');
71
121
 
@@ -73,36 +123,82 @@ export function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap,
73
123
  const fontstack = layout.get('text-font').evaluate(feature, {}).join(',');
74
124
  const glyphPositionMap = glyphPositions;
75
125
 
76
- const shapedTextOrientations = {};
126
+ const shapedTextOrientations = {
127
+ horizontal: {},
128
+ vertical: undefined
129
+ };
77
130
  const text = feature.text;
131
+ let textOffset = [0, 0];
78
132
  if (text) {
79
133
  const unformattedText = text.toString();
80
- const textOffset = layout
81
- .get('text-offset')
82
- .evaluate(feature, {})
83
- .map(t => t * oneEm);
84
- const spacing = layout.get('text-letter-spacing').evaluate(feature, {}) * oneEm;
134
+ const spacing = layout.get('text-letter-spacing').evaluate(feature, {}) * ONE_EM;
85
135
  const spacingIfAllowed = allowsLetterSpacing(unformattedText) ? spacing : 0;
136
+
86
137
  const textAnchor = layout.get('text-anchor').evaluate(feature, {});
87
- const textJustify = layout.get('text-justify').evaluate(feature, {});
138
+ const variableTextAnchor = layout.get('text-variable-anchor');
139
+ const radialOffset = layout.get('text-radial-offset').evaluate(feature, {});
140
+
141
+ if (!variableTextAnchor) {
142
+ // Layers with variable anchors use the `text-radial-offset` property and the [x, y] offset vector
143
+ // is calculated at placement time instead of layout time
144
+ if (radialOffset) {
145
+ // The style spec says don't use `text-offset` and `text-radial-offset` together
146
+ // but doesn't actually specify what happens if you use both. We go with the radial offset.
147
+ textOffset = evaluateRadialOffset(textAnchor, radialOffset * ONE_EM);
148
+ } else {
149
+ textOffset = layout
150
+ .get('text-offset')
151
+ .evaluate(feature, {})
152
+ .map(t => t * ONE_EM);
153
+ }
154
+ }
155
+
156
+ let textJustify = textAlongLine ? 'center' : layout.get('text-justify').evaluate(feature, {});
157
+
88
158
  const maxWidth =
89
- layout.get('symbol-placement') === 'point' ? layout.get('text-max-width').evaluate(feature, {}) * oneEm : 0;
90
-
91
- shapedTextOrientations.horizontal = shapeText(
92
- text,
93
- glyphMap,
94
- fontstack,
95
- maxWidth,
96
- lineHeight,
97
- textAnchor,
98
- textJustify,
99
- spacingIfAllowed,
100
- textOffset,
101
- oneEm,
102
- WritingMode.horizontal
103
- );
104
- if (allowsVerticalWritingMode(unformattedText) && textAlongLine && keepUpright) {
105
- shapedTextOrientations.vertical = shapeText(
159
+ layout.get('symbol-placement') === 'point' ? layout.get('text-max-width').evaluate(feature, {}) * ONE_EM : 0;
160
+
161
+ // If this layer uses text-variable-anchor, generate shapings for all justification possibilities.
162
+ if (!textAlongLine && variableTextAnchor) {
163
+ const justifications =
164
+ textJustify === 'auto' ? variableTextAnchor.map(a => getAnchorJustification(a)) : [textJustify];
165
+
166
+ let singleLine = false;
167
+ for (let i = 0; i < justifications.length; i++) {
168
+ const justification = justifications[i];
169
+ if (shapedTextOrientations.horizontal[justification]) {
170
+ continue;
171
+ }
172
+ if (singleLine) {
173
+ // If the shaping for the first justification was only a single line, we
174
+ // can re-use it for the other justifications
175
+ shapedTextOrientations.horizontal[justification] = shapedTextOrientations.horizontal[0];
176
+ } else {
177
+ // If using text-variable-anchor for the layer, we use a center anchor for all shapings and apply
178
+ // the offsets for the anchor in the placement step.
179
+ const shaping = shapeText(
180
+ text,
181
+ glyphMap,
182
+ fontstack,
183
+ maxWidth,
184
+ lineHeight,
185
+ 'center',
186
+ justification,
187
+ spacingIfAllowed,
188
+ textOffset,
189
+ WritingMode.horizontal
190
+ );
191
+ if (shaping) {
192
+ shapedTextOrientations.horizontal[justification] = shaping;
193
+ singleLine = shaping.lineCount === 1;
194
+ }
195
+ }
196
+ }
197
+ } else {
198
+ if (textJustify === 'auto') {
199
+ textJustify = getAnchorJustification(textAnchor);
200
+ }
201
+ const shaping = shapeText(
106
202
  text,
107
203
  glyphMap,
108
204
  fontstack,
@@ -112,9 +208,26 @@ export function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap,
112
208
  textJustify,
113
209
  spacingIfAllowed,
114
210
  textOffset,
115
- oneEm,
116
- WritingMode.vertical
211
+ WritingMode.horizontal
117
212
  );
213
+ if (shaping) {
214
+ shapedTextOrientations.horizontal[textJustify] = shaping;
215
+ }
216
+
217
+ if (allowsVerticalWritingMode(unformattedText) && textAlongLine && keepUpright) {
218
+ shapedTextOrientations.vertical = shapeText(
219
+ text,
220
+ glyphMap,
221
+ fontstack,
222
+ maxWidth,
223
+ lineHeight,
224
+ textAnchor,
225
+ textJustify,
226
+ spacingIfAllowed,
227
+ textOffset,
228
+ WritingMode.vertical
229
+ );
230
+ }
118
231
  }
119
232
  }
120
233
 
@@ -140,8 +253,8 @@ export function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap,
140
253
  }
141
254
  }
142
255
 
143
- if (shapedTextOrientations.horizontal || shapedIcon) {
144
- addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPositionMap, sizes);
256
+ if (Object.keys(shapedTextOrientations.horizontal).length || shapedIcon) {
257
+ addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPositionMap, sizes, textOffset);
145
258
  }
146
259
  }
147
260
 
@@ -150,6 +263,21 @@ export function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap,
150
263
  }
151
264
  }
152
265
 
266
+ // Choose the justification that matches the direction of the TextAnchor
267
+ export function getAnchorJustification(anchor) {
268
+ switch (anchor) {
269
+ case 'right':
270
+ case 'top-right':
271
+ case 'bottom-right':
272
+ return 'right';
273
+ case 'left':
274
+ case 'top-left':
275
+ case 'bottom-left':
276
+ return 'left';
277
+ }
278
+ return 'center';
279
+ }
280
+
153
281
  /**
154
282
  * Given a feature and its shaped text and icon data, add a 'symbol
155
283
  * instance' for each _possible_ placement of the symbol feature.
@@ -157,7 +285,7 @@ export function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap,
157
285
  * show or hide based on collisions with symbols in other layers.)
158
286
  * @private
159
287
  */
160
- function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPositionMap, sizes) {
288
+ function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPositionMap, sizes, textOffset) {
161
289
  const layoutTextSize = sizes.layoutTextSize.evaluate(feature, {});
162
290
  const layoutIconSize = sizes.layoutIconSize.evaluate(feature, {});
163
291
 
@@ -171,9 +299,8 @@ function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPo
171
299
  }
172
300
 
173
301
  const layout = bucket.layers[0]._layout;
174
- const textOffset = layout.get('text-offset').evaluate(feature, {});
175
302
  const iconOffset = layout.get('icon-offset').evaluate(feature, {});
176
-
303
+ const defaultHorizontalShaping = getDefaultHorizontalShaping(shapedTextOrientations.horizontal);
177
304
  const glyphSize = 24;
178
305
  const fontScale = layoutTextSize / glyphSize;
179
306
  const textBoxScale = bucket.tilePixelRatio * fontScale;
@@ -227,7 +354,7 @@ function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPo
227
354
  line,
228
355
  symbolMinDistance,
229
356
  textMaxAngle,
230
- shapedTextOrientations.vertical || shapedTextOrientations.horizontal,
357
+ shapedTextOrientations.vertical || defaultHorizontalShaping,
231
358
  shapedIcon,
232
359
  glyphSize,
233
360
  textMaxBoxScale,
@@ -235,7 +362,7 @@ function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPo
235
362
  EXTENT
236
363
  );
237
364
  for (const anchor of anchors) {
238
- const shapedText = shapedTextOrientations.horizontal;
365
+ const shapedText = defaultHorizontalShaping;
239
366
  if (!shapedText || !anchorIsTooClose(bucket, shapedText.text, textRepeatDistance, anchor)) {
240
367
  addSymbolAtAnchor(line, anchor);
241
368
  }
@@ -249,7 +376,7 @@ function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, glyphPo
249
376
  const anchor = getCenterAnchor(
250
377
  line,
251
378
  textMaxAngle,
252
- shapedTextOrientations.vertical || shapedTextOrientations.horizontal,
379
+ shapedTextOrientations.vertical || defaultHorizontalShaping,
253
380
  shapedIcon,
254
381
  glyphSize,
255
382
  textMaxBoxScale
@@ -289,11 +416,12 @@ function addTextVertices(
289
416
  textOffset,
290
417
  lineArray,
291
418
  writingMode,
419
+ placementTypes,
292
420
  placedTextSymbolIndices,
293
421
  glyphPositionMap,
294
422
  sizes
295
423
  ) {
296
- const glyphQuads = getGlyphQuads(anchor, shapedText, layer, textAlongLine, feature, glyphPositionMap);
424
+ const glyphQuads = getGlyphQuads(anchor, shapedText, textOffset, layer, textAlongLine, feature, glyphPositionMap);
297
425
 
298
426
  const sizeData = bucket.textSizeData;
299
427
  let textSizeData = null;
@@ -322,11 +450,22 @@ function addTextVertices(
322
450
 
323
451
  // The placedSymbolArray is used at render time in drawTileSymbols
324
452
  // These indices allow access to the array at collision detection time
325
- placedTextSymbolIndices.push(bucket.text.placedSymbolArray.length - 1);
453
+ for (const placementType of placementTypes) {
454
+ placedTextSymbolIndices[placementType] = bucket.text.placedSymbolArray.length - 1;
455
+ }
326
456
 
327
457
  return glyphQuads.length * 4;
328
458
  }
329
459
 
460
+ function getDefaultHorizontalShaping(horizontalShaping) {
461
+ // We don't care which shaping we get because this is used for collision purposes
462
+ // and all the justifications have the same collision box
463
+ for (const justification in horizontalShaping) {
464
+ return horizontalShaping[justification];
465
+ }
466
+ return null;
467
+ }
468
+
330
469
  /**
331
470
  * Add a single label & icon placement.
332
471
  *
@@ -361,61 +500,76 @@ function addSymbol(
361
500
  let iconCollisionFeature;
362
501
 
363
502
  let numIconVertices = 0;
364
- let numGlyphVertices = 0;
503
+ let numHorizontalGlyphVertices = 0;
365
504
  let numVerticalGlyphVertices = 0;
366
- const key = murmur3(shapedTextOrientations.horizontal ? shapedTextOrientations.horizontal.text : '');
367
- const placedTextSymbolIndices = [];
368
- if (shapedTextOrientations.horizontal) {
369
- // As a collision approximation, we can use either the vertical or the horizontal version of the feature
370
- // We're counting on the two versions having similar dimensions
371
- const textRotate = layer._layout.get('text-rotate').evaluate(feature, {});
372
- textCollisionFeature = new CollisionFeature(
373
- collisionBoxArray,
374
- line,
375
- anchor,
376
- featureIndex,
377
- sourceLayerIndex,
378
- bucketIndex,
379
- shapedTextOrientations.horizontal,
380
- textBoxScale,
381
- textPadding,
382
- textAlongLine,
383
- bucket.overscaling,
384
- textRotate
385
- );
386
- numGlyphVertices += addTextVertices(
505
+ const placedTextSymbolIndices = {};
506
+ let key = murmur3('');
507
+ const radialTextOffset = (layer._layout.get('text-radial-offset').evaluate(feature, {}) || 0) * ONE_EM;
508
+
509
+ for (const justification in shapedTextOrientations.horizontal) {
510
+ const shaping = shapedTextOrientations.horizontal[justification];
511
+
512
+ if (!textCollisionFeature) {
513
+ key = murmur3(shaping.text);
514
+ const textRotate = layer._layout.get('text-rotate').evaluate(feature, {});
515
+ // As a collision approximation, we can use either the vertical or any of the horizontal versions of the feature
516
+ // We're counting on all versions having similar dimensions
517
+ textCollisionFeature = new CollisionFeature(
518
+ collisionBoxArray,
519
+ line,
520
+ anchor,
521
+ featureIndex,
522
+ sourceLayerIndex,
523
+ bucketIndex,
524
+ shaping,
525
+ textBoxScale,
526
+ textPadding,
527
+ textAlongLine,
528
+ bucket.overscaling,
529
+ textRotate
530
+ );
531
+ }
532
+
533
+ const singleLine = shaping.lineCount === 1;
534
+ numHorizontalGlyphVertices += addTextVertices(
387
535
  bucket,
388
536
  anchor,
389
- shapedTextOrientations.horizontal,
537
+ shaping,
390
538
  layer,
391
539
  textAlongLine,
392
540
  feature,
393
541
  textOffset,
394
542
  lineArray,
395
543
  shapedTextOrientations.vertical ? WritingMode.horizontal : WritingMode.horizontalOnly,
544
+ singleLine ? Object.keys(shapedTextOrientations.horizontal) : [justification],
396
545
  placedTextSymbolIndices,
397
546
  glyphPositionMap,
398
547
  sizes
399
548
  );
400
549
 
401
- if (shapedTextOrientations.vertical) {
402
- numVerticalGlyphVertices += addTextVertices(
403
- bucket,
404
- anchor,
405
- shapedTextOrientations.vertical,
406
- layer,
407
- textAlongLine,
408
- feature,
409
- textOffset,
410
- lineArray,
411
- WritingMode.vertical,
412
- placedTextSymbolIndices,
413
- glyphPositionMap,
414
- sizes
415
- );
550
+ if (singleLine) {
551
+ break;
416
552
  }
417
553
  }
418
554
 
555
+ if (shapedTextOrientations.vertical) {
556
+ numVerticalGlyphVertices += addTextVertices(
557
+ bucket,
558
+ anchor,
559
+ shapedTextOrientations.vertical,
560
+ layer,
561
+ textAlongLine,
562
+ feature,
563
+ textOffset,
564
+ lineArray,
565
+ WritingMode.vertical,
566
+ ['vertical'],
567
+ placedTextSymbolIndices,
568
+ glyphPositionMap,
569
+ sizes
570
+ );
571
+ }
572
+
419
573
  const textBoxStartIndex = textCollisionFeature ? textCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length;
420
574
  const textBoxEndIndex = textCollisionFeature ? textCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length;
421
575
 
@@ -425,7 +579,7 @@ function addSymbol(
425
579
  shapedIcon,
426
580
  layer,
427
581
  iconAlongLine,
428
- shapedTextOrientations.horizontal,
582
+ getDefaultHorizontalShaping(shapedTextOrientations.horizontal),
429
583
  feature
430
584
  );
431
585
  const iconRotate = layer._layout.get('icon-rotate').evaluate(feature, {});
@@ -479,21 +633,29 @@ function addSymbol(
479
633
  warn.once('Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907');
480
634
  }
481
635
 
636
+ if (feature.sortKey !== undefined) {
637
+ bucket.addToSortKeyRanges(bucket.symbolInstances.length, feature.sortKey);
638
+ }
639
+
482
640
  bucket.symbolInstances.emplaceBack(
483
641
  anchor.x,
484
642
  anchor.y,
485
- placedTextSymbolIndices.length > 0 ? placedTextSymbolIndices[0] : -1,
486
- placedTextSymbolIndices.length > 1 ? placedTextSymbolIndices[1] : -1,
643
+ placedTextSymbolIndices.right >= 0 ? placedTextSymbolIndices.right : -1,
644
+ placedTextSymbolIndices.center >= 0 ? placedTextSymbolIndices.center : -1,
645
+ placedTextSymbolIndices.left >= 0 ? placedTextSymbolIndices.left : -1,
646
+ placedTextSymbolIndices.vertical || -1,
487
647
  key,
488
648
  textBoxStartIndex,
489
649
  textBoxEndIndex,
490
650
  iconBoxStartIndex,
491
651
  iconBoxEndIndex,
492
652
  featureIndex,
493
- numGlyphVertices,
653
+ numHorizontalGlyphVertices,
494
654
  numVerticalGlyphVertices,
495
655
  numIconVertices,
496
- 0
656
+ 0,
657
+ textBoxScale,
658
+ radialTextOffset
497
659
  );
498
660
  }
499
661
 
@@ -6,16 +6,15 @@ export default class GeoJSONFeature {
6
6
  #geometry;
7
7
  #xyz;
8
8
 
9
- constructor(vectorTileFeature, z, x, y) {
9
+ constructor(vectorTileFeature, z, x, y, id) {
10
10
  this.type = 'Feature';
11
11
 
12
12
  this.#vectorTileFeature = vectorTileFeature;
13
13
  this.#xyz = { z, x, y };
14
14
 
15
15
  this.properties = vectorTileFeature.properties;
16
-
17
- if (vectorTileFeature.id != null) {
18
- this.id = vectorTileFeature.id;
16
+ if (id !== undefined) {
17
+ this.id = id;
19
18
  }
20
19
  }
21
20