@mapwhit/tilerenderer 0.52.1 → 1.0.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 (142) hide show
  1. package/README.md +7 -0
  2. package/build/min/package.json +1 -1
  3. package/build/min/src/shaders/_prelude.fragment.glsl.js +2 -2
  4. package/build/min/src/shaders/_prelude.vertex.glsl.js +2 -2
  5. package/build/min/src/shaders/background.fragment.glsl.js +2 -2
  6. package/build/min/src/shaders/background.vertex.glsl.js +1 -1
  7. package/build/min/src/shaders/background_pattern.fragment.glsl.js +2 -2
  8. package/build/min/src/shaders/background_pattern.vertex.glsl.js +1 -1
  9. package/build/min/src/shaders/circle.fragment.glsl.js +2 -2
  10. package/build/min/src/shaders/circle.vertex.glsl.js +2 -2
  11. package/build/min/src/shaders/clipping_mask.fragment.glsl.js +1 -1
  12. package/build/min/src/shaders/clipping_mask.vertex.glsl.js +1 -1
  13. package/build/min/src/shaders/collision_box.fragment.glsl.js +1 -1
  14. package/build/min/src/shaders/collision_box.vertex.glsl.js +1 -1
  15. package/build/min/src/shaders/collision_circle.fragment.glsl.js +1 -1
  16. package/build/min/src/shaders/collision_circle.vertex.glsl.js +1 -1
  17. package/build/min/src/shaders/debug.fragment.glsl.js +1 -1
  18. package/build/min/src/shaders/debug.vertex.glsl.js +1 -1
  19. package/build/min/src/shaders/fill.fragment.glsl.js +2 -2
  20. package/build/min/src/shaders/fill.vertex.glsl.js +2 -2
  21. package/build/min/src/shaders/fill_extrusion.fragment.glsl.js +2 -2
  22. package/build/min/src/shaders/fill_extrusion.vertex.glsl.js +2 -2
  23. package/build/min/src/shaders/fill_extrusion_pattern.fragment.glsl.js +2 -2
  24. package/build/min/src/shaders/fill_extrusion_pattern.vertex.glsl.js +2 -2
  25. package/build/min/src/shaders/fill_outline.fragment.glsl.js +2 -2
  26. package/build/min/src/shaders/fill_outline.vertex.glsl.js +2 -2
  27. package/build/min/src/shaders/fill_outline_pattern.fragment.glsl.js +2 -2
  28. package/build/min/src/shaders/fill_outline_pattern.vertex.glsl.js +2 -2
  29. package/build/min/src/shaders/fill_pattern.fragment.glsl.js +2 -2
  30. package/build/min/src/shaders/fill_pattern.vertex.glsl.js +2 -2
  31. package/build/min/src/shaders/heatmap.fragment.glsl.js +2 -2
  32. package/build/min/src/shaders/heatmap.vertex.glsl.js +2 -2
  33. package/build/min/src/shaders/heatmap_texture.fragment.glsl.js +2 -2
  34. package/build/min/src/shaders/heatmap_texture.vertex.glsl.js +1 -1
  35. package/build/min/src/shaders/hillshade.fragment.glsl.js +2 -2
  36. package/build/min/src/shaders/hillshade.vertex.glsl.js +1 -1
  37. package/build/min/src/shaders/hillshade_prepare.fragment.glsl.js +2 -2
  38. package/build/min/src/shaders/hillshade_prepare.vertex.glsl.js +1 -1
  39. package/build/min/src/shaders/line.fragment.glsl.js +2 -2
  40. package/build/min/src/shaders/line.vertex.glsl.js +2 -2
  41. package/build/min/src/shaders/line_gradient.fragment.glsl.js +2 -2
  42. package/build/min/src/shaders/line_gradient.vertex.glsl.js +2 -2
  43. package/build/min/src/shaders/line_pattern.fragment.glsl.js +2 -2
  44. package/build/min/src/shaders/line_pattern.vertex.glsl.js +2 -2
  45. package/build/min/src/shaders/line_sdf.fragment.glsl.js +2 -2
  46. package/build/min/src/shaders/line_sdf.vertex.glsl.js +2 -2
  47. package/build/min/src/shaders/raster.fragment.glsl.js +2 -2
  48. package/build/min/src/shaders/raster.vertex.glsl.js +1 -1
  49. package/build/min/src/shaders/symbol_icon.fragment.glsl.js +2 -2
  50. package/build/min/src/shaders/symbol_icon.vertex.glsl.js +2 -2
  51. package/build/min/src/shaders/symbol_sdf.fragment.glsl.js +2 -2
  52. package/build/min/src/shaders/symbol_sdf.vertex.glsl.js +2 -2
  53. package/package.json +3 -3
  54. package/src/data/array_types.js +1 -36
  55. package/src/data/bucket/circle_bucket.js +0 -3
  56. package/src/data/bucket/fill_bucket.js +0 -3
  57. package/src/data/bucket/fill_extrusion_bucket.js +0 -3
  58. package/src/data/bucket/heatmap_bucket.js +0 -4
  59. package/src/data/bucket/line_bucket.js +1 -4
  60. package/src/data/bucket/pattern_bucket_features.js +2 -2
  61. package/src/data/bucket/symbol_bucket.js +87 -126
  62. package/src/data/bucket.js +26 -21
  63. package/src/data/dem_data.js +0 -3
  64. package/src/data/feature_index.js +3 -8
  65. package/src/data/program_configuration.js +3 -12
  66. package/src/data/segment.js +0 -4
  67. package/src/render/draw_background.js +3 -3
  68. package/src/render/draw_circle.js +4 -4
  69. package/src/render/draw_fill.js +8 -8
  70. package/src/render/draw_fill_extrusion.js +8 -8
  71. package/src/render/draw_heatmap.js +4 -4
  72. package/src/render/draw_line.js +6 -6
  73. package/src/render/draw_raster.js +6 -6
  74. package/src/render/draw_symbol.js +16 -16
  75. package/src/render/glyph_atlas.js +0 -3
  76. package/src/render/glyph_manager.js +1 -2
  77. package/src/render/image_atlas.js +0 -4
  78. package/src/render/image_manager.js +33 -19
  79. package/src/render/painter.js +13 -14
  80. package/src/render/program/circle_program.js +4 -4
  81. package/src/render/program/fill_extrusion_program.js +1 -1
  82. package/src/render/program/heatmap_program.js +1 -1
  83. package/src/render/program/hillshade_program.js +6 -6
  84. package/src/render/program/line_program.js +3 -3
  85. package/src/render/program/raster_program.js +6 -6
  86. package/src/source/geojson_source.js +15 -24
  87. package/src/source/geojson_worker_source.js +40 -68
  88. package/src/source/geojson_wrapper.js +9 -1
  89. package/src/source/image_source.js +6 -16
  90. package/src/source/query_features.js +4 -5
  91. package/src/source/raster_dem_tile_source.js +45 -64
  92. package/src/source/raster_tile_source.js +1 -6
  93. package/src/source/resources/glyphs.js +2 -2
  94. package/src/source/resources/index.js +3 -9
  95. package/src/source/rtl_text_plugin.js +58 -31
  96. package/src/source/source.js +11 -13
  97. package/src/source/source_cache.js +135 -151
  98. package/src/source/source_state.js +101 -12
  99. package/src/source/tile.js +32 -46
  100. package/src/source/tile_bounds.js +26 -26
  101. package/src/source/tile_id.js +2 -5
  102. package/src/source/vector_tile_source.js +14 -14
  103. package/src/source/vector_tile_worker_source.js +19 -23
  104. package/src/source/worker_tile.js +120 -117
  105. package/src/style/create_style_layer.js +1 -1
  106. package/src/style/pauseable_placement.js +4 -5
  107. package/src/style/properties.js +1 -8
  108. package/src/style/query_utils.js +3 -3
  109. package/src/style/style.js +263 -195
  110. package/src/style/style_layer/circle_style_layer.js +13 -11
  111. package/src/style/style_layer/fill_extrusion_style_layer.js +42 -27
  112. package/src/style/style_layer/fill_style_layer.js +5 -5
  113. package/src/style/style_layer/heatmap_style_layer.js +1 -1
  114. package/src/style/style_layer/hillshade_style_layer.js +1 -1
  115. package/src/style/style_layer/line_style_layer.js +23 -19
  116. package/src/style/style_layer/symbol_style_layer.js +13 -13
  117. package/src/style/style_layer.js +48 -30
  118. package/src/style/style_layer_index.js +16 -41
  119. package/src/symbol/anchor.js +0 -4
  120. package/src/symbol/cross_tile_symbol_index.js +2 -5
  121. package/src/symbol/opacity_state.js +0 -4
  122. package/src/symbol/placement.js +3 -3
  123. package/src/symbol/quads.js +4 -4
  124. package/src/symbol/symbol_layout.js +7 -7
  125. package/src/symbol/transform_text.js +1 -1
  126. package/src/ui/map.js +49 -11
  127. package/src/util/group_layers.js +41 -0
  128. package/src/util/image.js +0 -5
  129. package/src/util/key.js +21 -0
  130. package/src/util/object.js +8 -53
  131. package/src/worker.js +1 -4
  132. package/src/source/resources/images.js +0 -68
  133. package/src/source/worker.js +0 -110
  134. package/src/source/worker_source.js +0 -14
  135. package/src/style-spec/deref.js +0 -51
  136. package/src/style-spec/group_by_layout.js +0 -46
  137. package/src/util/actor.js +0 -108
  138. package/src/util/dispatcher.js +0 -65
  139. package/src/util/global_worker_pool.js +0 -15
  140. package/src/util/transfer_registry.js +0 -168
  141. package/src/util/web_worker_transfer.js +0 -43
  142. package/src/util/worker_pool.js +0 -41
@@ -21,7 +21,7 @@ class CircleStyleLayer extends StyleLayer {
21
21
  return (
22
22
  getMaximumPaintValue('circle-radius', this, circleBucket) +
23
23
  getMaximumPaintValue('circle-stroke-width', this, circleBucket) +
24
- translateDistance(this.paint.get('circle-translate'))
24
+ translateDistance(this._paint.get('circle-translate'))
25
25
  );
26
26
  }
27
27
 
@@ -37,24 +37,29 @@ class CircleStyleLayer extends StyleLayer {
37
37
  ) {
38
38
  const translatedPolygon = translate(
39
39
  queryGeometry,
40
- this.paint.get('circle-translate'),
41
- this.paint.get('circle-translate-anchor'),
40
+ this._paint.get('circle-translate'),
41
+ this._paint.get('circle-translate-anchor'),
42
42
  transform.angle,
43
43
  pixelsToTileUnits
44
44
  );
45
- const radius = this.paint.get('circle-radius').evaluate(feature, featureState);
46
- const stroke = this.paint.get('circle-stroke-width').evaluate(feature, featureState);
45
+ const radius = this._paint.get('circle-radius').evaluate(feature, featureState);
46
+ const stroke = this._paint.get('circle-stroke-width').evaluate(feature, featureState);
47
47
  const size = radius + stroke;
48
48
 
49
49
  // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile
50
50
  // // Otherwise, compare geometry in the plane of the viewport
51
51
  // // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance
52
52
  // // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance
53
- const alignWithMap = this.paint.get('circle-pitch-alignment') === 'map';
53
+ const pitchScale = this._paint.get('circle-pitch-scale');
54
+ const pitchAlignment = this._paint.get('circle-pitch-alignment');
55
+ const alignWithMap = pitchAlignment === 'map';
56
+ const alignWithViewport = pitchAlignment === 'viewport';
54
57
  const transformedPolygon = alignWithMap
55
58
  ? translatedPolygon
56
59
  : projectQueryGeometry(translatedPolygon, pixelPosMatrix);
57
60
  const transformedSize = alignWithMap ? size * pixelsToTileUnits : size;
61
+ const adjustViewportToMap = pitchScale === 'viewport' && alignWithMap;
62
+ const adjustMapToViewport = pitchScale === 'map' && alignWithViewport;
58
63
 
59
64
  for (const ring of geometry) {
60
65
  for (const point of ring) {
@@ -62,12 +67,9 @@ class CircleStyleLayer extends StyleLayer {
62
67
 
63
68
  let adjustedSize = transformedSize;
64
69
  const projectedCenter = vec4.transformMat4([], [point.x, point.y, 0, 1], pixelPosMatrix);
65
- if (this.paint.get('circle-pitch-scale') === 'viewport' && this.paint.get('circle-pitch-alignment') === 'map') {
70
+ if (adjustViewportToMap) {
66
71
  adjustedSize *= projectedCenter[3] / transform.cameraToCenterDistance;
67
- } else if (
68
- this.paint.get('circle-pitch-scale') === 'map' &&
69
- this.paint.get('circle-pitch-alignment') === 'viewport'
70
- ) {
72
+ } else if (adjustMapToViewport) {
71
73
  adjustedSize *= transform.cameraToCenterDistance / projectedCenter[3];
72
74
  }
73
75
 
@@ -17,7 +17,7 @@ class FillExtrusionStyleLayer extends StyleLayer {
17
17
  }
18
18
 
19
19
  queryRadius() {
20
- return translateDistance(this.paint.get('fill-extrusion-translate'));
20
+ return translateDistance(this._paint.get('fill-extrusion-translate'));
21
21
  }
22
22
 
23
23
  is3D() {
@@ -36,13 +36,13 @@ class FillExtrusionStyleLayer extends StyleLayer {
36
36
  ) {
37
37
  const translatedPolygon = translate(
38
38
  queryGeometry,
39
- this.paint.get('fill-extrusion-translate'),
40
- this.paint.get('fill-extrusion-translate-anchor'),
39
+ this._paint.get('fill-extrusion-translate'),
40
+ this._paint.get('fill-extrusion-translate-anchor'),
41
41
  transform.angle,
42
42
  pixelsToTileUnits
43
43
  );
44
- const height = this.paint.get('fill-extrusion-height').evaluate(feature, featureState);
45
- const base = this.paint.get('fill-extrusion-base').evaluate(feature, featureState);
44
+ const height = this._paint.get('fill-extrusion-height').evaluate(feature, featureState);
45
+ const base = this._paint.get('fill-extrusion-base').evaluate(feature, featureState);
46
46
 
47
47
  const projectedQueryGeometry = projectQueryGeometry(translatedPolygon, pixelPosMatrix, transform, 0);
48
48
 
@@ -66,27 +66,42 @@ function getIntersectionDistance(projectedQueryGeometry, projectedFace) {
66
66
  // triangle of the face, using only the xy plane. It doesn't matter if the
67
67
  // point is outside the first triangle because all the triangles in the face
68
68
  // are in the same plane.
69
- const a = projectedFace[0];
70
- const b = projectedFace[1];
71
- const c = projectedFace[3];
72
- const p = projectedQueryGeometry[0];
73
-
74
- const ab = b.sub(a);
75
- const ac = c.sub(a);
76
- const ap = p.sub(a);
77
-
78
- const dotABAB = dot(ab, ab);
79
- const dotABAC = dot(ab, ac);
80
- const dotACAC = dot(ac, ac);
81
- const dotAPAB = dot(ap, ab);
82
- const dotAPAC = dot(ap, ac);
83
- const denom = dotABAB * dotACAC - dotABAC * dotABAC;
84
- const v = (dotACAC * dotAPAB - dotABAC * dotAPAC) / denom;
85
- const w = (dotABAB * dotAPAC - dotABAC * dotAPAB) / denom;
86
- const u = 1 - v - w;
87
-
88
- // Use the barycentric weighting along with the original triangle z coordinates to get the point of intersection.
89
- return a.z * u + b.z * v + c.z * w;
69
+ //
70
+ // Check whether points are coincident and use other points if they are.
71
+ let i = 0;
72
+ const a = projectedFace[i++];
73
+ let b;
74
+ while (!b || a.equals(b)) {
75
+ b = projectedFace[i++];
76
+ if (!b) return Number.POSITIVE_INFINITY;
77
+ }
78
+
79
+ // Loop until point `c` is not colinear with points `a` and `b`.
80
+ for (; i < projectedFace.length; i++) {
81
+ const c = projectedFace[i];
82
+ const p = projectedQueryGeometry[0];
83
+
84
+ const ab = b.sub(a);
85
+ const ac = c.sub(a);
86
+ const ap = p.sub(a);
87
+
88
+ const dotABAB = dot(ab, ab);
89
+ const dotABAC = dot(ab, ac);
90
+ const dotACAC = dot(ac, ac);
91
+ const dotAPAB = dot(ap, ab);
92
+ const dotAPAC = dot(ap, ac);
93
+ const denom = dotABAB * dotACAC - dotABAC * dotABAC;
94
+
95
+ const v = (dotACAC * dotAPAB - dotABAC * dotAPAC) / denom;
96
+ const w = (dotABAB * dotAPAC - dotABAC * dotAPAB) / denom;
97
+ const u = 1 - v - w;
98
+
99
+ // Use the barycentric weighting along with the original triangle z coordinates to get the point of intersection.
100
+ const distance = a.z * u + b.z * v + c.z * w;
101
+
102
+ if (Number.isFinite(distance)) return distance;
103
+ }
104
+ return Number.POSITIVE_INFINITY;
90
105
  }
91
106
  // The counts as closest is less clear when the query is a box. This
92
107
  // returns the distance to the nearest point on the face, whether it is
@@ -191,4 +206,4 @@ function projectQueryGeometry(queryGeometry, pixelPosMatrix, transform, z) {
191
206
  return projectedQueryGeometry;
192
207
  }
193
208
 
194
- module.exports = FillExtrusionStyleLayer;
209
+ module.exports = { FillExtrusionStyleLayer, getIntersectionDistance };
@@ -13,9 +13,9 @@ class FillStyleLayer extends StyleLayer {
13
13
  recalculate(parameters) {
14
14
  super.recalculate(parameters);
15
15
 
16
- const outlineColor = this.paint._values['fill-outline-color'];
16
+ const outlineColor = this._paint._values['fill-outline-color'];
17
17
  if (outlineColor.value.kind === 'constant' && outlineColor.value.value === undefined) {
18
- this.paint._values['fill-outline-color'] = this.paint._values['fill-color'];
18
+ this._paint._values['fill-outline-color'] = this._paint._values['fill-color'];
19
19
  }
20
20
  }
21
21
 
@@ -24,14 +24,14 @@ class FillStyleLayer extends StyleLayer {
24
24
  }
25
25
 
26
26
  queryRadius() {
27
- return translateDistance(this.paint.get('fill-translate'));
27
+ return translateDistance(this._paint.get('fill-translate'));
28
28
  }
29
29
 
30
30
  queryIntersectsFeature(queryGeometry, feature, featureState, geometry, zoom, transform, pixelsToTileUnits) {
31
31
  const translatedPolygon = translate(
32
32
  queryGeometry,
33
- this.paint.get('fill-translate'),
34
- this.paint.get('fill-translate-anchor'),
33
+ this._paint.get('fill-translate'),
34
+ this._paint.get('fill-translate-anchor'),
35
35
  transform.angle,
36
36
  pixelsToTileUnits
37
37
  );
@@ -44,7 +44,7 @@ class HeatmapStyleLayer extends StyleLayer {
44
44
  }
45
45
 
46
46
  hasOffscreenPass() {
47
- return this.paint.get('heatmap-opacity') !== 0 && this.visibility !== 'none';
47
+ return this._paint.get('heatmap-opacity') !== 0 && this.visibility !== 'none';
48
48
  }
49
49
  }
50
50
 
@@ -8,7 +8,7 @@ class HillshadeStyleLayer extends StyleLayer {
8
8
  }
9
9
 
10
10
  hasOffscreenPass() {
11
- return this.paint.get('hillshade-exaggeration') !== 0 && this.visibility !== 'none';
11
+ return this._paint.get('hillshade-exaggeration') !== 0 && this.visibility !== 'none';
12
12
  }
13
13
  }
14
14
 
@@ -49,7 +49,7 @@ class LineStyleLayer extends StyleLayer {
49
49
  recalculate(parameters) {
50
50
  super.recalculate(parameters);
51
51
 
52
- this.paint._values['line-floorwidth'] = lineFloorwidthProperty.possiblyEvaluate(
52
+ this._paint._values['line-floorwidth'] = lineFloorwidthProperty.possiblyEvaluate(
53
53
  this._transitioningPaint._values['line-width'].value,
54
54
  parameters
55
55
  );
@@ -66,24 +66,24 @@ class LineStyleLayer extends StyleLayer {
66
66
  getMaximumPaintValue('line-gap-width', this, lineBucket)
67
67
  );
68
68
  const offset = getMaximumPaintValue('line-offset', this, lineBucket);
69
- return width / 2 + Math.abs(offset) + translateDistance(this.paint.get('line-translate'));
69
+ return width / 2 + Math.abs(offset) + translateDistance(this._paint.get('line-translate'));
70
70
  }
71
71
 
72
72
  queryIntersectsFeature(queryGeometry, feature, featureState, geometry, zoom, transform, pixelsToTileUnits) {
73
73
  const translatedPolygon = translate(
74
74
  queryGeometry,
75
- this.paint.get('line-translate'),
76
- this.paint.get('line-translate-anchor'),
75
+ this._paint.get('line-translate'),
76
+ this._paint.get('line-translate-anchor'),
77
77
  transform.angle,
78
78
  pixelsToTileUnits
79
79
  );
80
80
  const halfWidth =
81
81
  (pixelsToTileUnits / 2) *
82
82
  getLineWidth(
83
- this.paint.get('line-width').evaluate(feature, featureState),
84
- this.paint.get('line-gap-width').evaluate(feature, featureState)
83
+ this._paint.get('line-width').evaluate(feature, featureState),
84
+ this._paint.get('line-gap-width').evaluate(feature, featureState)
85
85
  );
86
- const lineOffset = this.paint.get('line-offset').evaluate(feature, featureState);
86
+ const lineOffset = this._paint.get('line-offset').evaluate(feature, featureState);
87
87
  if (lineOffset) {
88
88
  geometry = offsetLine(geometry, lineOffset * pixelsToTileUnits);
89
89
  }
@@ -105,25 +105,29 @@ function getLineWidth(lineWidth, lineGapWidth) {
105
105
  }
106
106
 
107
107
  function offsetLine(rings, offset) {
108
- const newRings = [];
109
- const zero = new Point(0, 0);
108
+ const newRings = new Array(rings.length);
110
109
  for (let k = 0; k < rings.length; k++) {
111
110
  const ring = rings[k];
112
- const newRing = [];
113
- for (let i = 0; i < ring.length; i++) {
114
- const a = ring[i - 1];
115
- const b = ring[i];
111
+ const newRing = new Array(ring.length);
112
+ newRings[k] = newRing;
113
+
114
+ let b = ring[0];
115
+ let aToB = new Point(0, 0);
116
+ for (let i = 0; i < ring.length - 1; i++) {
116
117
  const c = ring[i + 1];
117
- const aToB = i === 0 ? zero : b.sub(a)._unit()._perp();
118
- const bToC = i === ring.length - 1 ? zero : c.sub(b)._unit()._perp();
118
+ const bToC = c.sub(b)._unit()._perp();
119
119
  const extrude = aToB._add(bToC)._unit();
120
-
121
120
  const cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y;
122
- extrude._mult(1 / cosHalfAngle);
121
+ if (cosHalfAngle !== 0) {
122
+ extrude._div(cosHalfAngle);
123
+ }
124
+ newRing[i] = extrude._mult(offset)._add(b);
123
125
 
124
- newRing.push(extrude._mult(offset)._add(b));
126
+ b = c;
127
+ aToB = bToC;
125
128
  }
126
- newRings.push(newRing);
129
+
130
+ newRing[ring.length - 1] = aToB._unit()._mult(offset)._add(b);
127
131
  }
128
132
  return newRings;
129
133
  }
@@ -14,33 +14,33 @@ class SymbolStyleLayer extends StyleLayer {
14
14
  recalculate(parameters) {
15
15
  super.recalculate(parameters);
16
16
 
17
- if (this.layout.get('icon-rotation-alignment') === 'auto') {
18
- if (this.layout.get('symbol-placement') !== 'point') {
19
- this.layout._values['icon-rotation-alignment'] = 'map';
17
+ if (this._layout.get('icon-rotation-alignment') === 'auto') {
18
+ if (this._layout.get('symbol-placement') !== 'point') {
19
+ this._layout._values['icon-rotation-alignment'] = 'map';
20
20
  } else {
21
- this.layout._values['icon-rotation-alignment'] = 'viewport';
21
+ this._layout._values['icon-rotation-alignment'] = 'viewport';
22
22
  }
23
23
  }
24
24
 
25
- if (this.layout.get('text-rotation-alignment') === 'auto') {
26
- if (this.layout.get('symbol-placement') !== 'point') {
27
- this.layout._values['text-rotation-alignment'] = 'map';
25
+ if (this._layout.get('text-rotation-alignment') === 'auto') {
26
+ if (this._layout.get('symbol-placement') !== 'point') {
27
+ this._layout._values['text-rotation-alignment'] = 'map';
28
28
  } else {
29
- this.layout._values['text-rotation-alignment'] = 'viewport';
29
+ this._layout._values['text-rotation-alignment'] = 'viewport';
30
30
  }
31
31
  }
32
32
 
33
33
  // If unspecified, `*-pitch-alignment` inherits `*-rotation-alignment`
34
- if (this.layout.get('text-pitch-alignment') === 'auto') {
35
- this.layout._values['text-pitch-alignment'] = this.layout.get('text-rotation-alignment');
34
+ if (this._layout.get('text-pitch-alignment') === 'auto') {
35
+ this._layout._values['text-pitch-alignment'] = this._layout.get('text-rotation-alignment');
36
36
  }
37
- if (this.layout.get('icon-pitch-alignment') === 'auto') {
38
- this.layout._values['icon-pitch-alignment'] = this.layout.get('icon-rotation-alignment');
37
+ if (this._layout.get('icon-pitch-alignment') === 'auto') {
38
+ this._layout._values['icon-pitch-alignment'] = this._layout.get('icon-rotation-alignment');
39
39
  }
40
40
  }
41
41
 
42
42
  getValueAndResolveTokens(name, feature) {
43
- const value = this.layout.get(name).evaluate(feature, {});
43
+ const value = this._layout.get(name).evaluate(feature, {});
44
44
  const unevaluated = this._unevaluatedLayout._values[name];
45
45
  if (!unevaluated.isDataDriven() && !isExpression(unevaluated.value)) {
46
46
  return resolveTokens(feature.properties, value);
@@ -4,10 +4,20 @@ const { Evented } = require('@mapwhit/events');
4
4
  const { Layout, Transitionable, PossiblyEvaluatedPropertyValue } = require('./properties');
5
5
  const { supportsPropertyExpression } = require('@mapwhit/style-expressions');
6
6
  const featureFilter = require('../style-spec/feature_filter');
7
+ const createKey = require('../util/key');
7
8
 
9
+ const keyProperties = ['type', 'minzoom', 'maxzoom', 'filter', 'layout'];
8
10
  const TRANSITION_SUFFIX = '-transition';
9
11
 
12
+ /**
13
+ * Representing a style layer in the map.
14
+ * Properties:
15
+ * `this.paint` - paint properties of the layer as defined in the map style
16
+ * `this._paint` - internal representation of paint properties necessary to calculate expressions
17
+ */
10
18
  class StyleLayer extends Evented {
19
+ #key;
20
+
11
21
  constructor(layer, properties) {
12
22
  super();
13
23
 
@@ -22,7 +32,7 @@ class StyleLayer extends Evented {
22
32
 
23
33
  if (layer.type !== 'background') {
24
34
  this.source = layer.source;
25
- this.sourceLayer = layer['source-layer'];
35
+ this['source-layer'] = this.sourceLayer = layer['source-layer'];
26
36
  this.filter = layer.filter;
27
37
  this._featureFilter = featureFilter(layer.filter);
28
38
  }
@@ -46,10 +56,26 @@ class StyleLayer extends Evented {
46
56
  }
47
57
 
48
58
  setFilter(filter) {
59
+ this.#key = undefined;
49
60
  this.filter = filter;
50
61
  this._featureFilter = featureFilter(filter);
51
62
  }
52
63
 
64
+ _setZoomRange(minzoom, maxzoom) {
65
+ if (this.minzoom === minzoom && this.maxzoom === maxzoom) {
66
+ return;
67
+ }
68
+ if (minzoom != null) {
69
+ this.#key = undefined;
70
+ this.minzoom = minzoom;
71
+ }
72
+ if (maxzoom != null) {
73
+ this.#key = undefined;
74
+ this.maxzoom = maxzoom;
75
+ }
76
+ return true;
77
+ }
78
+
53
79
  getCrossfadeParameters() {
54
80
  return this._crossfadeParameters;
55
81
  }
@@ -111,6 +137,8 @@ class StyleLayer extends Evented {
111
137
  }
112
138
 
113
139
  setLayoutProperty(name, value) {
140
+ this.#key = undefined;
141
+ this.layout[name] = value;
114
142
  if (name === 'visibility') {
115
143
  this.visibility = value === 'none' ? value : 'visible';
116
144
  return;
@@ -127,6 +155,7 @@ class StyleLayer extends Evented {
127
155
  }
128
156
 
129
157
  setPaintProperty(name, value) {
158
+ this.paint[name] = value;
130
159
  if (name.endsWith(TRANSITION_SUFFIX)) {
131
160
  this._transitionablePaint.setTransition(name.slice(0, -TRANSITION_SUFFIX.length), value || undefined);
132
161
  return false;
@@ -141,9 +170,19 @@ class StyleLayer extends Evented {
141
170
  this._transitionablePaint.setValue(name, value);
142
171
  const isDataDriven = this._transitionablePaint._values[name].value.isDataDriven();
143
172
  this._handleSpecialPaintPropertyUpdate(name);
173
+ if (isDataDriven !== wasDataDriven || (wasDataDriven && isDataDriven)) {
174
+ // reset transitioning in progress
175
+ this._untransitioned(name);
176
+ }
144
177
  return isDataDriven || wasDataDriven || newCrossFadedValue;
145
178
  }
146
179
 
180
+ _untransitioned(name) {
181
+ if (this._transitioningPaint) {
182
+ this._transitioningPaint._values[name] = this._transitionablePaint._values[name].untransitioned();
183
+ }
184
+ }
185
+
147
186
  _handleSpecialPaintPropertyUpdate() {
148
187
  // No-op; can be overridden by derived classes.
149
188
  }
@@ -167,38 +206,17 @@ class StyleLayer extends Evented {
167
206
  this._crossfadeParameters = parameters.getCrossfadeParameters();
168
207
  }
169
208
  if (this._unevaluatedLayout) {
170
- this.layout = this._unevaluatedLayout.possiblyEvaluate(parameters);
209
+ this._layout = this._unevaluatedLayout.possiblyEvaluate(parameters);
171
210
  }
172
211
 
173
- this.paint = this._transitioningPaint.possiblyEvaluate(parameters);
212
+ this._paint = this._transitioningPaint.possiblyEvaluate(parameters);
174
213
  }
175
214
 
176
- serialize() {
177
- const output = {
178
- id: this.id,
179
- type: this.type,
180
- source: this.source,
181
- 'source-layer': this.sourceLayer,
182
- metadata: this.metadata,
183
- minzoom: this.minzoom,
184
- maxzoom: this.maxzoom,
185
- filter: this.filter,
186
- layout: this._unevaluatedLayout?.serialize(),
187
- paint: this._transitionablePaint?.serialize()
188
- };
189
-
190
- if (this.visibility === 'none') {
191
- output.layout = output.layout || {};
192
- output.layout.visibility = 'none';
215
+ get key() {
216
+ if (!this.#key) {
217
+ this.#key = createKey(keyProperties, this);
193
218
  }
194
-
195
- return filterObject(output, (value, key) => {
196
- return (
197
- value !== undefined &&
198
- !(key === 'layout' && !Object.keys(value).length) &&
199
- !(key === 'paint' && !Object.keys(value).length)
200
- );
201
- });
219
+ return this.#key;
202
220
  }
203
221
 
204
222
  is3D() {
@@ -218,8 +236,8 @@ class StyleLayer extends Evented {
218
236
  }
219
237
 
220
238
  isStateDependent() {
221
- for (const property in this.paint._values) {
222
- const value = this.paint.get(property);
239
+ for (const property in this._paint._values) {
240
+ const value = this._paint.get(property);
223
241
  if (
224
242
  !(value instanceof PossiblyEvaluatedPropertyValue) ||
225
243
  !supportsPropertyExpression(value.property.specification)
@@ -1,54 +1,29 @@
1
- const createStyleLayer = require('./create_style_layer');
2
-
3
- const { values } = require('../util/object');
4
- const featureFilter = require('../style-spec/feature_filter');
5
- const groupByLayout = require('../style-spec/group_by_layout');
1
+ const groupBySource = require('../util/group_layers');
6
2
 
7
3
  class StyleLayerIndex {
8
- #layerConfigs = {};
9
- #layers = {};
4
+ #layers = new Map();
5
+ #fbs = {};
10
6
 
11
- constructor(layerConfigs) {
12
- if (layerConfigs) {
13
- this.update(layerConfigs);
7
+ constructor(layers) {
8
+ if (layers) {
9
+ this.replace(layers);
14
10
  }
15
11
  }
16
12
 
17
- replace(layerConfigs) {
18
- this.#layerConfigs = {};
19
- this.#layers = {};
20
- this.update(layerConfigs);
13
+ replace(layers) {
14
+ this.#layers = layers;
15
+ this.#fbs = null;
21
16
  }
22
17
 
23
- update(layerConfigs, removedIds = []) {
24
- for (const layerConfig of layerConfigs) {
25
- this.#layerConfigs[layerConfig.id] = layerConfig;
26
-
27
- const layer = (this.#layers[layerConfig.id] = createStyleLayer(layerConfig));
28
- layer._featureFilter = featureFilter(layer.filter);
29
- }
30
- for (const id of removedIds) {
31
- delete this.#layerConfigs[id];
32
- delete this.#layers[id];
33
- }
34
-
35
- this.familiesBySource = {};
36
-
37
- const groups = groupByLayout(values(this.#layerConfigs));
38
-
39
- for (const layerConfigs of groups) {
40
- const layers = layerConfigs.map(layerConfig => this.#layers[layerConfig.id]);
41
-
42
- const layer = layers[0];
43
- if (layer.visibility === 'none') {
44
- continue;
45
- }
18
+ update() {
19
+ this.#fbs = null;
20
+ }
46
21
 
47
- const { source = '', sourceLayer = '_geojsonTileLayer' } = layer;
48
- const sourceGroup = (this.familiesBySource[source] ??= {});
49
- const sourceLayerFamilies = (sourceGroup[sourceLayer] ??= []);
50
- sourceLayerFamilies.push(layers);
22
+ get familiesBySource() {
23
+ if (!this.#fbs) {
24
+ this.#fbs = groupBySource(this.#layers.values());
51
25
  }
26
+ return this.#fbs;
52
27
  }
53
28
  }
54
29
 
@@ -1,7 +1,5 @@
1
1
  const { default: Point } = require('@mapbox/point-geometry');
2
2
 
3
- const { register } = require('../util/transfer_registry');
4
-
5
3
  class Anchor extends Point {
6
4
  constructor(x, y, angle, segment) {
7
5
  super(x, y);
@@ -16,6 +14,4 @@ class Anchor extends Point {
16
14
  }
17
15
  }
18
16
 
19
- register('Anchor', Anchor);
20
-
21
17
  module.exports = Anchor;
@@ -260,12 +260,9 @@ class CrossTileSymbolIndex {
260
260
  }
261
261
 
262
262
  pruneUnusedLayers(usedLayers) {
263
- const usedLayerMap = {};
264
- usedLayers.forEach(usedLayer => {
265
- usedLayerMap[usedLayer] = true;
266
- });
263
+ const usedLayersSet = new Set(usedLayers);
267
264
  for (const layerId in this.layerIndexes) {
268
- if (!usedLayerMap[layerId]) {
265
+ if (!usedLayersSet.has(layerId)) {
269
266
  delete this.layerIndexes[layerId];
270
267
  }
271
268
  }
@@ -1,5 +1,3 @@
1
- const { register } = require('../util/transfer_registry');
2
-
3
1
  class OpacityState {
4
2
  constructor() {
5
3
  this.opacity = 0;
@@ -16,6 +14,4 @@ class OpacityState {
16
14
  }
17
15
  }
18
16
 
19
- register('OpacityState', OpacityState);
20
-
21
17
  module.exports = OpacityState;
@@ -100,7 +100,7 @@ class Placement {
100
100
 
101
101
  const collisionBoxArray = tile.collisionBoxArray;
102
102
 
103
- const layout = symbolBucket.layers[0].layout;
103
+ const layout = symbolBucket.layers[0]._layout;
104
104
 
105
105
  const scale = 2 ** (this.transform.zoom - tile.tileID.overscaledZ);
106
106
  const textPixelRatio = tile.tileSize / EXTENT;
@@ -159,7 +159,7 @@ class Placement {
159
159
  seenCrossTileIDs,
160
160
  collisionBoxArray
161
161
  ) {
162
- const layout = bucket.layers[0].layout;
162
+ const layout = bucket.layers[0]._layout;
163
163
 
164
164
  const partiallyEvaluatedTextSize = symbolSize.evaluateSizeForZoom(
165
165
  bucket.textSizeData,
@@ -411,7 +411,7 @@ class Placement {
411
411
  if (bucket.hasCollisionBoxData()) bucket.collisionBox.collisionVertexArray.clear();
412
412
  if (bucket.hasCollisionCircleData()) bucket.collisionCircle.collisionVertexArray.clear();
413
413
 
414
- const layout = bucket.layers[0].layout;
414
+ const layout = bucket.layers[0]._layout;
415
415
  const duplicateOpacityState = new JointOpacityState(null, 0, false, false, true);
416
416
  const textAllowOverlap = layout.get('text-allow-overlap');
417
417
  const iconAllowOverlap = layout.get('icon-allow-overlap');
@@ -22,7 +22,7 @@ const { GLYPH_PBF_BORDER } = require('../style/parse_glyph_pbf');
22
22
  */
23
23
  function getIconQuads(anchor, shapedIcon, layer, alongLine, shapedText, feature) {
24
24
  const image = shapedIcon.image;
25
- const layout = layer.layout;
25
+ const layout = layer._layout;
26
26
 
27
27
  // If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual
28
28
  // pixels. The quad needs to be padded to account for this, otherwise they'll look slightly clipped
@@ -71,7 +71,7 @@ function getIconQuads(anchor, shapedIcon, layer, alongLine, shapedText, feature)
71
71
  bl = new Point(left, bottom);
72
72
  }
73
73
 
74
- const angle = (layer.layout.get('icon-rotate').evaluate(feature, {}) * Math.PI) / 180;
74
+ const angle = (layer._layout.get('icon-rotate').evaluate(feature, {}) * Math.PI) / 180;
75
75
 
76
76
  if (angle) {
77
77
  const sin = Math.sin(angle);
@@ -94,8 +94,8 @@ function getIconQuads(anchor, shapedIcon, layer, alongLine, shapedText, feature)
94
94
  */
95
95
  function getGlyphQuads(anchor, shaping, layer, alongLine, feature, positions) {
96
96
  const oneEm = 24;
97
- const textRotate = (layer.layout.get('text-rotate').evaluate(feature, {}) * Math.PI) / 180;
98
- const textOffset = layer.layout
97
+ const textRotate = (layer._layout.get('text-rotate').evaluate(feature, {}) * Math.PI) / 180;
98
+ const textOffset = layer._layout
99
99
  .get('text-offset')
100
100
  .evaluate(feature, {})
101
101
  .map(t => t * oneEm);