@mapwhit/tilerenderer 0.47.2 → 0.49.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 (43) hide show
  1. package/build/min/package.json +1 -1
  2. package/package.json +3 -2
  3. package/src/data/array_types.js +169 -0
  4. package/src/data/bucket/symbol_attributes.js +18 -0
  5. package/src/data/bucket/symbol_bucket.js +116 -73
  6. package/src/data/program_configuration.js +18 -10
  7. package/src/geo/transform.js +17 -7
  8. package/src/index.js +1 -1
  9. package/src/render/glyph_atlas.js +3 -6
  10. package/src/render/image_atlas.js +3 -6
  11. package/src/render/image_manager.js +41 -41
  12. package/src/source/rtl_text_plugin.js +1 -0
  13. package/src/source/source_cache.js +1 -1
  14. package/src/source/tile.js +6 -5
  15. package/src/source/worker.js +1 -10
  16. package/src/style/style.js +4 -19
  17. package/src/style/style_layer/symbol_style_layer_properties.js +7 -1
  18. package/src/style-spec/expression/compound_expression.js +30 -16
  19. package/src/style-spec/expression/definitions/assertion.js +52 -5
  20. package/src/style-spec/expression/definitions/coercion.js +13 -0
  21. package/src/style-spec/expression/definitions/comparison.js +193 -0
  22. package/src/style-spec/expression/definitions/formatted.js +123 -0
  23. package/src/style-spec/expression/definitions/index.js +11 -62
  24. package/src/style-spec/expression/definitions/interpolate.js +17 -7
  25. package/src/style-spec/expression/definitions/literal.js +5 -0
  26. package/src/style-spec/expression/parsing_context.js +6 -7
  27. package/src/style-spec/expression/types.js +12 -1
  28. package/src/style-spec/feature_filter/convert.js +197 -0
  29. package/src/style-spec/feature_filter/index.js +5 -2
  30. package/src/style-spec/function/convert.js +78 -100
  31. package/src/style-spec/reference/v8.json +160 -52
  32. package/src/symbol/collision_index.js +0 -1
  33. package/src/symbol/cross_tile_symbol_index.js +12 -7
  34. package/src/symbol/get_anchors.js +11 -22
  35. package/src/symbol/mergelines.js +4 -1
  36. package/src/symbol/placement.js +58 -54
  37. package/src/symbol/quads.js +7 -6
  38. package/src/symbol/shaping.js +185 -40
  39. package/src/symbol/symbol_layout.js +40 -37
  40. package/src/symbol/transform_text.js +12 -1
  41. package/src/ui/map.js +8 -25
  42. package/src/style-spec/expression/definitions/array.js +0 -82
  43. package/src/style-spec/expression/definitions/equals.js +0 -93
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.47.1"
2
+ "version": "0.48.0"
3
3
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mapwhit/tilerenderer",
3
3
  "description": "A WebGL interactive maps library",
4
- "version": "0.47.2",
4
+ "version": "0.49.0",
5
5
  "main": "src/index.js",
6
6
  "license": "BSD-3-Clause",
7
7
  "repository": {
@@ -11,7 +11,6 @@
11
11
  "dependencies": {
12
12
  "@mapbox/gl-matrix": "^0.0.1",
13
13
  "@mapbox/point-geometry": "^0.1.0",
14
- "@mapbox/shelf-pack": "^3.0.0",
15
14
  "@mapbox/tiny-sdf": "^1.1.0",
16
15
  "@mapbox/unitbezier": "^0.0.0",
17
16
  "@mapbox/vector-tile": "^1.3.1",
@@ -24,6 +23,8 @@
24
23
  "earcut": "^2.0.3",
25
24
  "geojson-vt": "^3.1.2",
26
25
  "grid-index": "^1.1.0",
26
+ "murmurhash-js": "^1.0.0",
27
+ "potpack": "^1.0.1",
27
28
  "quickselect": "^2.0.0",
28
29
  "supercluster": "^2.0.1",
29
30
  "tinyqueue": "^1.1.0"
@@ -513,6 +513,68 @@ class StructArrayLayout2i2ui3ul3ui2f2ub40 extends StructArray {
513
513
  StructArrayLayout2i2ui3ul3ui2f2ub40.prototype.bytesPerElement = 40;
514
514
  register('StructArrayLayout2i2ui3ul3ui2f2ub40', StructArrayLayout2i2ui3ul3ui2f2ub40);
515
515
 
516
+ /**
517
+ * Implementation of the StructArray layout:
518
+ * [0]: Int16[4]
519
+ * [8]: Uint16[9]
520
+ * [28]: Uint32[1]
521
+ *
522
+ * @private
523
+ */
524
+ class StructArrayLayout4i9ui1ul32 extends StructArray {
525
+ _refreshViews() {
526
+ this.uint8 = new Uint8Array(this.arrayBuffer);
527
+ this.int16 = new Int16Array(this.arrayBuffer);
528
+ this.uint16 = new Uint16Array(this.arrayBuffer);
529
+ this.uint32 = new Uint32Array(this.arrayBuffer);
530
+ }
531
+
532
+ emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) {
533
+ const i = this.length;
534
+ this.resize(i + 1);
535
+ const o2 = i * 16;
536
+ const o4 = i * 8;
537
+ this.int16[o2 + 0] = v0;
538
+ this.int16[o2 + 1] = v1;
539
+ this.int16[o2 + 2] = v2;
540
+ this.int16[o2 + 3] = v3;
541
+ this.uint16[o2 + 4] = v4;
542
+ this.uint16[o2 + 5] = v5;
543
+ this.uint16[o2 + 6] = v6;
544
+ this.uint16[o2 + 7] = v7;
545
+ this.uint16[o2 + 8] = v8;
546
+ this.uint16[o2 + 9] = v9;
547
+ this.uint16[o2 + 10] = v10;
548
+ this.uint16[o2 + 11] = v11;
549
+ this.uint16[o2 + 12] = v12;
550
+ this.uint32[o4 + 7] = v13;
551
+ return i;
552
+ }
553
+
554
+ emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) {
555
+ const o2 = i * 16;
556
+ const o4 = i * 8;
557
+ this.int16[o2 + 0] = v0;
558
+ this.int16[o2 + 1] = v1;
559
+ this.int16[o2 + 2] = v2;
560
+ this.int16[o2 + 3] = v3;
561
+ this.uint16[o2 + 4] = v4;
562
+ this.uint16[o2 + 5] = v5;
563
+ this.uint16[o2 + 6] = v6;
564
+ this.uint16[o2 + 7] = v7;
565
+ this.uint16[o2 + 8] = v8;
566
+ this.uint16[o2 + 9] = v9;
567
+ this.uint16[o2 + 10] = v10;
568
+ this.uint16[o2 + 11] = v11;
569
+ this.uint16[o2 + 12] = v12;
570
+ this.uint32[o4 + 7] = v13;
571
+ return i;
572
+ }
573
+ }
574
+
575
+ StructArrayLayout4i9ui1ul32.prototype.bytesPerElement = 32;
576
+ register('StructArrayLayout4i9ui1ul32', StructArrayLayout4i9ui1ul32);
577
+
516
578
  /**
517
579
  * Implementation of the StructArray layout:
518
580
  * [0]: Float32[1]
@@ -974,6 +1036,111 @@ class PlacedSymbolArray extends StructArrayLayout2i2ui3ul3ui2f2ub40 {
974
1036
 
975
1037
  register('PlacedSymbolArray', PlacedSymbolArray);
976
1038
 
1039
+ class SymbolInstanceStruct extends Struct {
1040
+ get anchorX() {
1041
+ return this._structArray.int16[this._pos2 + 0];
1042
+ }
1043
+ set anchorX(x) {
1044
+ this._structArray.int16[this._pos2 + 0] = x;
1045
+ }
1046
+ get anchorY() {
1047
+ return this._structArray.int16[this._pos2 + 1];
1048
+ }
1049
+ set anchorY(x) {
1050
+ this._structArray.int16[this._pos2 + 1] = x;
1051
+ }
1052
+ get horizontalPlacedTextSymbolIndex() {
1053
+ return this._structArray.int16[this._pos2 + 2];
1054
+ }
1055
+ set horizontalPlacedTextSymbolIndex(x) {
1056
+ this._structArray.int16[this._pos2 + 2] = x;
1057
+ }
1058
+ get verticalPlacedTextSymbolIndex() {
1059
+ return this._structArray.int16[this._pos2 + 3];
1060
+ }
1061
+ set verticalPlacedTextSymbolIndex(x) {
1062
+ this._structArray.int16[this._pos2 + 3] = x;
1063
+ }
1064
+ get key() {
1065
+ return this._structArray.uint16[this._pos2 + 4];
1066
+ }
1067
+ set key(x) {
1068
+ this._structArray.uint16[this._pos2 + 4] = x;
1069
+ }
1070
+ get textBoxStartIndex() {
1071
+ return this._structArray.uint16[this._pos2 + 5];
1072
+ }
1073
+ set textBoxStartIndex(x) {
1074
+ this._structArray.uint16[this._pos2 + 5] = x;
1075
+ }
1076
+ get textBoxEndIndex() {
1077
+ return this._structArray.uint16[this._pos2 + 6];
1078
+ }
1079
+ set textBoxEndIndex(x) {
1080
+ this._structArray.uint16[this._pos2 + 6] = x;
1081
+ }
1082
+ get iconBoxStartIndex() {
1083
+ return this._structArray.uint16[this._pos2 + 7];
1084
+ }
1085
+ set iconBoxStartIndex(x) {
1086
+ this._structArray.uint16[this._pos2 + 7] = x;
1087
+ }
1088
+ get iconBoxEndIndex() {
1089
+ return this._structArray.uint16[this._pos2 + 8];
1090
+ }
1091
+ set iconBoxEndIndex(x) {
1092
+ this._structArray.uint16[this._pos2 + 8] = x;
1093
+ }
1094
+ get featureIndex() {
1095
+ return this._structArray.uint16[this._pos2 + 9];
1096
+ }
1097
+ set featureIndex(x) {
1098
+ this._structArray.uint16[this._pos2 + 9] = x;
1099
+ }
1100
+ get numGlyphVertices() {
1101
+ return this._structArray.uint16[this._pos2 + 10];
1102
+ }
1103
+ set numGlyphVertices(x) {
1104
+ this._structArray.uint16[this._pos2 + 10] = x;
1105
+ }
1106
+ get numVerticalGlyphVertices() {
1107
+ return this._structArray.uint16[this._pos2 + 11];
1108
+ }
1109
+ set numVerticalGlyphVertices(x) {
1110
+ this._structArray.uint16[this._pos2 + 11] = x;
1111
+ }
1112
+ get numIconVertices() {
1113
+ return this._structArray.uint16[this._pos2 + 12];
1114
+ }
1115
+ set numIconVertices(x) {
1116
+ this._structArray.uint16[this._pos2 + 12] = x;
1117
+ }
1118
+ get crossTileID() {
1119
+ return this._structArray.uint32[this._pos4 + 7];
1120
+ }
1121
+ set crossTileID(x) {
1122
+ this._structArray.uint32[this._pos4 + 7] = x;
1123
+ }
1124
+ }
1125
+
1126
+ SymbolInstanceStruct.prototype.size = 32;
1127
+
1128
+ /**
1129
+ * @private
1130
+ */
1131
+ class SymbolInstanceArray extends StructArrayLayout4i9ui1ul32 {
1132
+ /**
1133
+ * Return the SymbolInstanceStruct at the given location in the array.
1134
+ * @param {number} index The index of the element.
1135
+ */
1136
+ get(index) {
1137
+ assert(!this.isTransferred);
1138
+ return new SymbolInstanceStruct(this, index);
1139
+ }
1140
+ }
1141
+
1142
+ register('SymbolInstanceArray', SymbolInstanceArray);
1143
+
977
1144
  class GlyphOffsetStruct extends Struct {
978
1145
  get offsetX() {
979
1146
  return this._structArray.float32[this._pos4 + 0];
@@ -1104,6 +1271,7 @@ module.exports = {
1104
1271
  StructArrayLayout2i2i2i12,
1105
1272
  StructArrayLayout2ub4,
1106
1273
  StructArrayLayout2i2ui3ul3ui2f2ub40,
1274
+ StructArrayLayout4i9ui1ul32,
1107
1275
  StructArrayLayout1f4,
1108
1276
  StructArrayLayout3i6,
1109
1277
  StructArrayLayout1ul2ui8,
@@ -1134,5 +1302,6 @@ module.exports = {
1134
1302
  FeatureIndexArray,
1135
1303
  GlyphOffsetArray,
1136
1304
  PlacedSymbolArray,
1305
+ SymbolInstanceArray,
1137
1306
  SymbolLineVertexArray
1138
1307
  };
@@ -73,6 +73,23 @@ const placement = createLayout([
73
73
  { type: 'Uint8', name: 'hidden' }
74
74
  ]);
75
75
 
76
+ const symbolInstance = createLayout([
77
+ { type: 'Int16', name: 'anchorX' },
78
+ { type: 'Int16', name: 'anchorY' },
79
+ { type: 'Int16', name: 'horizontalPlacedTextSymbolIndex' },
80
+ { type: 'Int16', name: 'verticalPlacedTextSymbolIndex' },
81
+ { type: 'Uint16', name: 'key' },
82
+ { type: 'Uint16', name: 'textBoxStartIndex' },
83
+ { type: 'Uint16', name: 'textBoxEndIndex' },
84
+ { type: 'Uint16', name: 'iconBoxStartIndex' },
85
+ { type: 'Uint16', name: 'iconBoxEndIndex' },
86
+ { type: 'Uint16', name: 'featureIndex' },
87
+ { type: 'Uint16', name: 'numGlyphVertices' },
88
+ { type: 'Uint16', name: 'numVerticalGlyphVertices' },
89
+ { type: 'Uint16', name: 'numIconVertices' },
90
+ { type: 'Uint32', name: 'crossTileID' }
91
+ ]);
92
+
76
93
  const glyphOffset = createLayout([{ type: 'Float32', name: 'offsetX' }]);
77
94
 
78
95
  const lineVertex = createLayout([
@@ -90,6 +107,7 @@ module.exports = {
90
107
  collisionBoxLayout,
91
108
  collisionCircleLayout,
92
109
  placement,
110
+ symbolInstance,
93
111
  glyphOffset,
94
112
  lineVertex
95
113
  };
@@ -14,6 +14,7 @@ const {
14
14
  CollisionCircleLayoutArray,
15
15
  CollisionVertexArray,
16
16
  PlacedSymbolArray,
17
+ SymbolInstanceArray,
17
18
  GlyphOffsetArray,
18
19
  SymbolLineVertexArray
19
20
  } = require('../array_types');
@@ -31,6 +32,7 @@ const { verticalizedCharacterMap } = require('../../util/verticalize_punctuation
31
32
  const { getSizeData } = require('../../symbol/symbol_size');
32
33
  const { register } = require('../../util/transfer_registry');
33
34
  const EvaluationParameters = require('../../style/evaluation_parameters');
35
+ const { Formatted } = require('../../style-spec/expression/definitions/formatted');
34
36
 
35
37
  // Opacity arrays are frequently updated but don't contain a lot of information, so we pack them
36
38
  // tight. Each Uint32 is actually four duplicate Uint8s for the four corners of a glyph
@@ -189,11 +191,13 @@ class SymbolBucket {
189
191
  this.iconSizeData = getSizeData(this.zoom, unevaluatedLayoutValues['icon-size']);
190
192
 
191
193
  const layout = this.layers[0].layout;
194
+ const zOrderByViewportY = layout.get('symbol-z-order') === 'viewport-y';
192
195
  this.sortFeaturesByY =
193
- layout.get('text-allow-overlap') ||
194
- layout.get('icon-allow-overlap') ||
195
- layout.get('text-ignore-placement') ||
196
- layout.get('icon-ignore-placement');
196
+ zOrderByViewportY &&
197
+ (layout.get('text-allow-overlap') ||
198
+ layout.get('icon-allow-overlap') ||
199
+ layout.get('text-ignore-placement') ||
200
+ layout.get('icon-ignore-placement'));
197
201
 
198
202
  this.sourceID = options.sourceID;
199
203
  }
@@ -219,6 +223,19 @@ class SymbolBucket {
219
223
 
220
224
  this.glyphOffsetArray = new GlyphOffsetArray();
221
225
  this.lineVertexArray = new SymbolLineVertexArray();
226
+ this.symbolInstances = new SymbolInstanceArray();
227
+ }
228
+
229
+ calculateGlyphDependencies(text, stack, textAlongLine, doesAllowVerticalWritingMode) {
230
+ for (let i = 0; i < text.length; i++) {
231
+ stack[text.charCodeAt(i)] = true;
232
+ if (textAlongLine && doesAllowVerticalWritingMode) {
233
+ const verticalChar = verticalizedCharacterMap[text.charAt(i)];
234
+ if (verticalChar) {
235
+ stack[verticalChar.charCodeAt(0)] = true;
236
+ }
237
+ }
238
+ }
222
239
  }
223
240
 
224
241
  populate(features, options) {
@@ -229,7 +246,7 @@ class SymbolBucket {
229
246
  const textField = layout.get('text-field');
230
247
  const iconImage = layout.get('icon-image');
231
248
  const hasText =
232
- (textField.value.kind !== 'constant' || textField.value.value.length > 0) &&
249
+ (textField.value.kind !== 'constant' || textField.value.value.toString().length > 0) &&
233
250
  (textFont.value.kind !== 'constant' || textFont.value.value.length > 0);
234
251
  const hasIcon = iconImage.value.kind !== 'constant' || (iconImage.value.value && iconImage.value.value.length > 0);
235
252
 
@@ -286,15 +303,16 @@ class SymbolBucket {
286
303
  const stack = (stacks[fontStack] = stacks[fontStack] || {});
287
304
  const textAlongLine =
288
305
  layout.get('text-rotation-alignment') === 'map' && layout.get('symbol-placement') !== 'point';
289
- const doesAllowVerticalWritingMode = allowsVerticalWritingMode(text);
290
- for (let i = 0; i < text.length; i++) {
291
- stack[text.charCodeAt(i)] = true;
292
- if (textAlongLine && doesAllowVerticalWritingMode) {
293
- const verticalChar = verticalizedCharacterMap[text.charAt(i)];
294
- if (verticalChar) {
295
- stack[verticalChar.charCodeAt(0)] = true;
296
- }
306
+ if (text instanceof Formatted) {
307
+ for (const section of text.sections) {
308
+ const doesAllowVerticalWritingMode = allowsVerticalWritingMode(text.toString());
309
+ const sectionFont = section.fontStack || fontStack;
310
+ const sectionStack = (stacks[sectionFont] = stacks[sectionFont] || {});
311
+ this.calculateGlyphDependencies(section.text, sectionStack, textAlongLine, doesAllowVerticalWritingMode);
297
312
  }
313
+ } else {
314
+ const doesAllowVerticalWritingMode = allowsVerticalWritingMode(text);
315
+ this.calculateGlyphDependencies(text, stack, textAlongLine, doesAllowVerticalWritingMode);
298
316
  }
299
317
  }
300
318
  }
@@ -441,15 +459,15 @@ class SymbolBucket {
441
459
  arrays.programConfigurations.populatePaintArrays(arrays.layoutVertexArray.length, feature, feature.index, {});
442
460
  }
443
461
 
444
- _addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, point, anchor, extrude) {
462
+ _addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, point, anchorX, anchorY, extrude) {
445
463
  collisionVertexArray.emplaceBack(0, 0);
446
464
  return layoutVertexArray.emplaceBack(
447
465
  // pos
448
466
  point.x,
449
467
  point.y,
450
468
  // a_anchor_pos
451
- anchor.x,
452
- anchor.y,
469
+ anchorX,
470
+ anchorY,
453
471
  // extrude
454
472
  Math.round(extrude.x),
455
473
  Math.round(extrude.y)
@@ -463,32 +481,39 @@ class SymbolBucket {
463
481
  const layoutVertexArray = arrays.layoutVertexArray;
464
482
  const collisionVertexArray = arrays.collisionVertexArray;
465
483
 
484
+ const anchorX = symbolInstance.anchorX;
485
+ const anchorY = symbolInstance.anchorY;
486
+
466
487
  this._addCollisionDebugVertex(
467
488
  layoutVertexArray,
468
489
  collisionVertexArray,
469
490
  boxAnchorPoint,
470
- symbolInstance.anchor,
491
+ anchorX,
492
+ anchorY,
471
493
  new Point(x1, y1)
472
494
  );
473
495
  this._addCollisionDebugVertex(
474
496
  layoutVertexArray,
475
497
  collisionVertexArray,
476
498
  boxAnchorPoint,
477
- symbolInstance.anchor,
499
+ anchorX,
500
+ anchorY,
478
501
  new Point(x2, y1)
479
502
  );
480
503
  this._addCollisionDebugVertex(
481
504
  layoutVertexArray,
482
505
  collisionVertexArray,
483
506
  boxAnchorPoint,
484
- symbolInstance.anchor,
507
+ anchorX,
508
+ anchorY,
485
509
  new Point(x2, y2)
486
510
  );
487
511
  this._addCollisionDebugVertex(
488
512
  layoutVertexArray,
489
513
  collisionVertexArray,
490
514
  boxAnchorPoint,
491
- symbolInstance.anchor,
515
+ anchorX,
516
+ anchorY,
492
517
  new Point(x1, y2)
493
518
  );
494
519
 
@@ -510,49 +535,41 @@ class SymbolBucket {
510
535
  }
511
536
  }
512
537
 
513
- generateCollisionDebugBuffers() {
514
- for (const symbolInstance of this.symbolInstances) {
515
- symbolInstance.textCollisionFeature = {
516
- boxStartIndex: symbolInstance.textBoxStartIndex,
517
- boxEndIndex: symbolInstance.textBoxEndIndex
518
- };
519
- symbolInstance.iconCollisionFeature = {
520
- boxStartIndex: symbolInstance.iconBoxStartIndex,
521
- boxEndIndex: symbolInstance.iconBoxEndIndex
522
- };
538
+ addDebugCollisionBoxes(startIndex, endIndex, symbolInstance) {
539
+ for (let b = startIndex; b < endIndex; b++) {
540
+ const box = this.collisionBoxArray.get(b);
541
+ const x1 = box.x1;
542
+ const y1 = box.y1;
543
+ const x2 = box.x2;
544
+ const y2 = box.y2;
545
+
546
+ // If the radius > 0, this collision box is actually a circle
547
+ // The data we add to the buffers is exactly the same, but we'll render with a different shader.
548
+ const isCircle = box.radius > 0;
549
+ this.addCollisionDebugVertices(
550
+ x1,
551
+ y1,
552
+ x2,
553
+ y2,
554
+ isCircle ? this.collisionCircle : this.collisionBox,
555
+ box.anchorPoint,
556
+ symbolInstance,
557
+ isCircle
558
+ );
559
+ }
560
+ }
523
561
 
524
- for (let i = 0; i < 2; i++) {
525
- const feature = symbolInstance[i === 0 ? 'textCollisionFeature' : 'iconCollisionFeature'];
526
- if (!feature) continue;
527
-
528
- for (let b = feature.boxStartIndex; b < feature.boxEndIndex; b++) {
529
- const box = this.collisionBoxArray.get(b);
530
- const x1 = box.x1;
531
- const y1 = box.y1;
532
- const x2 = box.x2;
533
- const y2 = box.y2;
534
-
535
- // If the radius > 0, this collision box is actually a circle
536
- // The data we add to the buffers is exactly the same, but we'll render with a different shader.
537
- const isCircle = box.radius > 0;
538
- this.addCollisionDebugVertices(
539
- x1,
540
- y1,
541
- x2,
542
- y2,
543
- isCircle ? this.collisionCircle : this.collisionBox,
544
- box.anchorPoint,
545
- symbolInstance,
546
- isCircle
547
- );
548
- }
549
- }
562
+ generateCollisionDebugBuffers() {
563
+ for (let i = 0; i < this.symbolInstances.length; i++) {
564
+ const symbolInstance = this.symbolInstances.get(i);
565
+ this.addDebugCollisionBoxes(symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance);
566
+ this.addDebugCollisionBoxes(symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance);
550
567
  }
551
568
  }
552
569
 
553
570
  // These flat arrays are meant to be quicker to iterate over than the source
554
571
  // CollisionBoxArray
555
- deserializeCollisionBoxes(collisionBoxArray, textStartIndex, textEndIndex, iconStartIndex, iconEndIndex) {
572
+ _deserializeCollisionBoxesForSymbol(collisionBoxArray, textStartIndex, textEndIndex, iconStartIndex, iconEndIndex) {
556
573
  const collisionArrays = {};
557
574
  for (let k = textStartIndex; k < textEndIndex; k++) {
558
575
  const box = collisionBoxArray.get(k);
@@ -600,6 +617,22 @@ class SymbolBucket {
600
617
  return collisionArrays;
601
618
  }
602
619
 
620
+ deserializeCollisionBoxes(collisionBoxArray) {
621
+ this.collisionArrays = [];
622
+ for (let i = 0; i < this.symbolInstances.length; i++) {
623
+ const symbolInstance = this.symbolInstances.get(i);
624
+ this.collisionArrays.push(
625
+ this._deserializeCollisionBoxesForSymbol(
626
+ collisionBoxArray,
627
+ symbolInstance.textBoxStartIndex,
628
+ symbolInstance.textBoxEndIndex,
629
+ symbolInstance.iconBoxStartIndex,
630
+ symbolInstance.iconBoxEndIndex
631
+ )
632
+ );
633
+ }
634
+ }
635
+
603
636
  hasTextData() {
604
637
  return this.text.segments.get().length > 0;
605
638
  }
@@ -616,6 +649,16 @@ class SymbolBucket {
616
649
  return this.collisionCircle.segments.get().length > 0;
617
650
  }
618
651
 
652
+ addIndicesForPlacedTextSymbol(placedTextSymbolIndex) {
653
+ const placedSymbol = this.text.placedSymbolArray.get(placedTextSymbolIndex);
654
+
655
+ const endIndex = placedSymbol.vertexStartIndex + placedSymbol.numGlyphs * 4;
656
+ for (let vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
657
+ this.text.indexArray.emplaceBack(vertexIndex, vertexIndex + 1, vertexIndex + 2);
658
+ this.text.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
659
+ }
660
+ }
661
+
619
662
  sortFeatures(angle) {
620
663
  if (!this.sortFeaturesByY) return;
621
664
 
@@ -639,12 +682,16 @@ class SymbolBucket {
639
682
  const sin = Math.sin(angle);
640
683
  const cos = Math.cos(angle);
641
684
 
685
+ const rotatedYs = [];
686
+ const featureIndexes = [];
687
+ for (let i = 0; i < this.symbolInstances.length; i++) {
688
+ const symbolInstance = this.symbolInstances.get(i);
689
+ rotatedYs.push(Math.round(sin * symbolInstance.anchorX + cos * symbolInstance.anchorY) | 0);
690
+ featureIndexes.push(symbolInstance.featureIndex);
691
+ }
692
+
642
693
  symbolInstanceIndexes.sort((aIndex, bIndex) => {
643
- const a = this.symbolInstances[aIndex];
644
- const b = this.symbolInstances[bIndex];
645
- const aRotated = (sin * a.anchor.x + cos * a.anchor.y) | 0;
646
- const bRotated = (sin * b.anchor.x + cos * b.anchor.y) | 0;
647
- return aRotated - bRotated || b.featureIndex - a.featureIndex;
694
+ return rotatedYs[aIndex] - rotatedYs[bIndex] || featureIndexes[bIndex] - featureIndexes[aIndex];
648
695
  });
649
696
 
650
697
  this.text.indexArray.clear();
@@ -653,17 +700,14 @@ class SymbolBucket {
653
700
  this.featureSortOrder = [];
654
701
 
655
702
  for (const i of symbolInstanceIndexes) {
656
- const symbolInstance = this.symbolInstances[i];
703
+ const symbolInstance = this.symbolInstances.get(i);
657
704
  this.featureSortOrder.push(symbolInstance.featureIndex);
658
705
 
659
- for (const placedTextSymbolIndex of symbolInstance.placedTextSymbolIndices) {
660
- const placedSymbol = this.text.placedSymbolArray.get(placedTextSymbolIndex);
661
-
662
- const endIndex = placedSymbol.vertexStartIndex + placedSymbol.numGlyphs * 4;
663
- for (let vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
664
- this.text.indexArray.emplaceBack(vertexIndex, vertexIndex + 1, vertexIndex + 2);
665
- this.text.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
666
- }
706
+ if (symbolInstance.horizontalPlacedTextSymbolIndex >= 0) {
707
+ this.addIndicesForPlacedTextSymbol(symbolInstance.horizontalPlacedTextSymbolIndex);
708
+ }
709
+ if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) {
710
+ this.addIndicesForPlacedTextSymbol(symbolInstance.verticalPlacedTextSymbolIndex);
667
711
  }
668
712
 
669
713
  const placedIcon = this.icon.placedSymbolArray.get(i);
@@ -680,8 +724,7 @@ class SymbolBucket {
680
724
  }
681
725
 
682
726
  register('SymbolBucket', SymbolBucket, {
683
- omit: ['layers', 'collisionBoxArray', 'features', 'compareText'],
684
- shallow: ['symbolInstances']
727
+ omit: ['layers', 'collisionBoxArray', 'features', 'compareText']
685
728
  });
686
729
 
687
730
  // this constant is based on the size of StructArray indexes used in a symbol
@@ -170,11 +170,15 @@ class SourceExpressionBinder {
170
170
 
171
171
  upload(context) {
172
172
  if (this.paintVertexArray?.arrayBuffer) {
173
- this.paintVertexBuffer = context.createVertexBuffer(
174
- this.paintVertexArray,
175
- this.paintVertexAttributes,
176
- this.expression.isStateDependent
177
- );
173
+ if (this.paintVertexBuffer?.buffer) {
174
+ this.paintVertexBuffer.updateData(this.paintVertexArray);
175
+ } else {
176
+ this.paintVertexBuffer = context.createVertexBuffer(
177
+ this.paintVertexArray,
178
+ this.paintVertexAttributes,
179
+ this.expression.isStateDependent
180
+ );
181
+ }
178
182
  }
179
183
  }
180
184
 
@@ -265,11 +269,15 @@ class CompositeExpressionBinder {
265
269
 
266
270
  upload(context) {
267
271
  if (this.paintVertexArray?.arrayBuffer) {
268
- this.paintVertexBuffer = context.createVertexBuffer(
269
- this.paintVertexArray,
270
- this.paintVertexAttributes,
271
- this.expression.isStateDependent
272
- );
272
+ if (this.paintVertexBuffer?.buffer) {
273
+ this.paintVertexBuffer.updateData(this.paintVertexArray);
274
+ } else {
275
+ this.paintVertexBuffer = context.createVertexBuffer(
276
+ this.paintVertexArray,
277
+ this.paintVertexAttributes,
278
+ this.expression.isStateDependent
279
+ );
280
+ }
273
281
  }
274
282
  }
275
283
 
@@ -17,12 +17,13 @@ const { vec4, mat4, mat2 } = require('@mapbox/gl-matrix');
17
17
  class Transform {
18
18
  constructor(minZoom, maxZoom, renderWorldCopies) {
19
19
  this.tileSize = 512; // constant
20
+ this.maxValidLatitude = 85.051129; // constant
20
21
 
21
22
  this._renderWorldCopies = renderWorldCopies === undefined ? true : renderWorldCopies;
22
23
  this._minZoom = minZoom || 0;
23
24
  this._maxZoom = maxZoom || 22;
24
25
 
25
- this.latRange = [-85.05113, 85.05113];
26
+ this.latRange = [-this.maxValidLatitude, this.maxValidLatitude];
26
27
 
27
28
  this.width = 0;
28
29
  this.height = 0;
@@ -176,13 +177,21 @@ class Transform {
176
177
  * @private
177
178
  */
178
179
  getVisibleUnwrappedCoordinates(tileID) {
179
- const ul = this.pointCoordinate(new Point(0, 0), 0);
180
- const ur = this.pointCoordinate(new Point(this.width, 0), 0);
181
- const w0 = Math.floor(ul.column);
182
- const w1 = Math.floor(ur.column);
183
180
  const result = [new UnwrappedTileID(0, tileID)];
184
181
  if (this._renderWorldCopies) {
185
- for (let w = w0; w <= w1; w++) {
182
+ const utl = this.pointCoordinate(new Point(0, 0), 0);
183
+ const utr = this.pointCoordinate(new Point(this.width, 0), 0);
184
+ const ubl = this.pointCoordinate(new Point(this.width, this.height), 0);
185
+ const ubr = this.pointCoordinate(new Point(0, this.height), 0);
186
+ const w0 = Math.floor(Math.min(utl.column, utr.column, ubl.column, ubr.column));
187
+ const w1 = Math.floor(Math.max(utl.column, utr.column, ubl.column, ubr.column));
188
+
189
+ // Add an extra copy of the world on each side to properly render ImageSources and CanvasSources.
190
+ // Both sources draw outside the tile boundaries of the tile that "contains them" so we need
191
+ // to add extra copies on both sides in case offscreen tiles need to draw into on-screen ones.
192
+ const extraWorldCopy = 1;
193
+
194
+ for (let w = w0 - extraWorldCopy; w <= w1 + extraWorldCopy; w++) {
186
195
  if (w === 0) continue;
187
196
  result.push(new UnwrappedTileID(w, tileID));
188
197
  }
@@ -262,7 +271,7 @@ class Transform {
262
271
  }
263
272
 
264
273
  /**
265
- * latitude to absolute x coord
274
+ * longitude to absolute x coord
266
275
  * @returns {number} pixel coordinate
267
276
  */
268
277
  lngX(lng) {
@@ -273,6 +282,7 @@ class Transform {
273
282
  * @returns {number} pixel coordinate
274
283
  */
275
284
  latY(lat) {
285
+ lat = clamp(lat, -this.maxValidLatitude, this.maxValidLatitude);
276
286
  const y = (180 / Math.PI) * Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360));
277
287
  return ((180 - y) * this.worldSize) / 360;
278
288
  }