@mapwhit/tilerenderer 0.52.0 → 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 +8 -5
  56. package/src/data/bucket/fill_bucket.js +8 -5
  57. package/src/data/bucket/fill_extrusion_bucket.js +8 -5
  58. package/src/data/bucket/heatmap_bucket.js +0 -4
  59. package/src/data/bucket/line_bucket.js +9 -6
  60. package/src/data/bucket/pattern_bucket_features.js +2 -2
  61. package/src/data/bucket/symbol_bucket.js +99 -129
  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 +24 -33
  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 +122 -119
  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 +286 -216
  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 +71 -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 +52 -15
  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
@@ -9,58 +9,69 @@ const Light = require('./light');
9
9
  const LineAtlas = require('../render/line_atlas');
10
10
  const { clone, deepEqual, filterObject, mapObject } = require('../util/object');
11
11
  const browser = require('../util/browser');
12
- const dispatcher = require('../util/dispatcher');
13
12
  const { getType: getSourceType, setType: setSourceType } = require('../source/source');
14
13
  const { queryRenderedFeatures, queryRenderedSymbols, querySourceFeatures } = require('../source/query_features');
15
14
  const SourceCache = require('../source/source_cache');
16
- const getWorkerPool = require('../util/global_worker_pool');
17
- const deref = require('../style-spec/deref');
18
- const { registerForPluginAvailability, evented: rtlTextPluginEvented } = require('../source/rtl_text_plugin');
15
+ const plugin = require('../source/rtl_text_plugin');
19
16
  const PauseablePlacement = require('./pauseable_placement');
20
17
  const ZoomHistory = require('./zoom_history');
21
18
  const CrossTileSymbolIndex = require('../symbol/cross_tile_symbol_index');
19
+ const StyleLayerIndex = require('../style/style_layer_index');
20
+ const { resources } = require('../source/resources');
21
+
22
+ const properties = [
23
+ 'version',
24
+ 'name',
25
+ 'metadata',
26
+ 'center',
27
+ 'zoom',
28
+ 'bearing',
29
+ 'pitch',
30
+ 'state',
31
+ 'sprite',
32
+ 'glyphs',
33
+ 'transition'
34
+ ];
22
35
 
23
36
  /**
24
37
  * @private
25
38
  */
26
39
  class Style extends Evented {
27
- // exposed to allow stubbing by unit tests
40
+ #resources = resources({
41
+ getImages: this.getImages.bind(this),
42
+ loadGlyphRange: this.loadGlyphRange.bind(this)
43
+ });
44
+ #layerIndex = new StyleLayerIndex();
28
45
 
29
46
  constructor(map, options = {}) {
30
47
  super();
31
48
 
32
49
  this.map = map;
33
- this.dispatcher = dispatcher(getWorkerPool(), this);
34
50
  this.imageManager = new ImageManager();
35
51
  this.glyphManager = new GlyphManager();
52
+
36
53
  this.lineAtlas = new LineAtlas(256, 512);
37
54
  this.crossTileSymbolIndex = new CrossTileSymbolIndex();
38
55
 
39
- this._layers = {};
40
- this._order = [];
41
- this.sourceCaches = {};
56
+ // insertion operations are done in the order of the layers in the style
57
+ this._layers = new Map();
58
+ this._sources = {};
42
59
  this.zoomHistory = new ZoomHistory();
43
60
  this._loaded = false;
44
61
  this._globalState = {};
45
62
 
63
+ this._updatedLayers = new Map();
64
+ this._removedLayers = new Map();
46
65
  this._resetUpdates();
47
66
 
48
- const self = this;
49
- this._rtlTextPluginCallback = Style.registerForPluginAvailability(args => {
50
- self.dispatcher
51
- .broadcast('loadRTLTextPlugin', args.pluginURL)
52
- .then(_ => args.completionCallback(), args.completionCallback);
53
- for (const id in self.sourceCaches) {
54
- self.sourceCaches[id].reload(); // Should be a no-op if the plugin loads before any tiles load
55
- }
56
- });
67
+ this._rtlTextPluginCallbackUnregister = plugin.registerForPluginAvailability(this._reloadSources.bind(this));
57
68
 
58
69
  this.on('data', event => {
59
70
  if (event.dataType !== 'source' || event.sourceDataType !== 'metadata') {
60
71
  return;
61
72
  }
62
73
 
63
- const sourceCache = this.sourceCaches[event.sourceId];
74
+ const sourceCache = this._sources[event.sourceId];
64
75
  if (!sourceCache) {
65
76
  return;
66
77
  }
@@ -70,8 +81,7 @@ class Style extends Evented {
70
81
  return;
71
82
  }
72
83
 
73
- for (const layerId in this._layers) {
74
- const layer = this._layers[layerId];
84
+ for (const layer of this._layers.values()) {
75
85
  if (layer.source === source.id) {
76
86
  this._validateLayer(layer);
77
87
  }
@@ -90,14 +100,7 @@ class Style extends Evented {
90
100
 
91
101
  this._globalState[name] = newValue;
92
102
 
93
- const sourceIdsToReload = this._findGlobalStateAffectedSources([name]);
94
-
95
- for (const id in this.sourceCaches) {
96
- if (sourceIdsToReload.has(id)) {
97
- this._reloadSource(id);
98
- this._changed = true;
99
- }
100
- }
103
+ this._applyGlobalStateChanges([name]);
101
104
  }
102
105
 
103
106
  getGlobalState() {
@@ -118,39 +121,43 @@ class Style extends Evented {
118
121
  }
119
122
  }
120
123
 
121
- const sourceIdsToReload = this._findGlobalStateAffectedSources(changedGlobalStateRefs);
122
-
123
- for (const id in this.sourceCaches) {
124
- if (sourceIdsToReload.has(id)) {
125
- this._reloadSource(id);
126
- this._changed = true;
127
- }
128
- }
124
+ this._applyGlobalStateChanges(changedGlobalStateRefs);
129
125
  }
130
126
 
131
127
  /**
132
- * Find all sources that are affected by the global state changes.
133
- * For example, if a layer filter uses global-state expression, this function will return the source id of that layer.
128
+ * * Find all sources that are affected by the global state changes and reload them.
129
+ * Find all paint properties that are affected by the global state changes and update them.
130
+ * For example, if a layer filter uses global-state expression, this function will find the source id of that layer.
134
131
  */
135
- _findGlobalStateAffectedSources(globalStateRefs) {
132
+ _applyGlobalStateChanges(globalStateRefs) {
136
133
  if (globalStateRefs.length === 0) {
137
- return new Set();
134
+ return;
138
135
  }
139
136
 
140
137
  const sourceIdsToReload = new Set();
141
138
 
142
- for (const layerId in this._layers) {
143
- const layer = this._layers[layerId];
139
+ for (const layer of this._layers.values()) {
144
140
  const layoutAffectingGlobalStateRefs = layer.getLayoutAffectingGlobalStateRefs();
141
+ const paintAffectingGlobalStateRefs = layer.getPaintAffectingGlobalStateRefs();
145
142
 
146
143
  for (const ref of globalStateRefs) {
147
144
  if (layoutAffectingGlobalStateRefs.has(ref)) {
148
145
  sourceIdsToReload.add(layer.source);
149
146
  }
147
+ if (paintAffectingGlobalStateRefs.has(ref)) {
148
+ for (const { name, value } of paintAffectingGlobalStateRefs.get(ref)) {
149
+ this._updatePaintProperty(layer, name, value);
150
+ }
151
+ }
150
152
  }
151
153
  }
152
154
 
153
- return sourceIdsToReload;
155
+ for (const id in this._sources) {
156
+ if (sourceIdsToReload.has(id)) {
157
+ this._reloadSource(id);
158
+ this._changed = true;
159
+ }
160
+ }
154
161
  }
155
162
 
156
163
  loadJSON(json) {
@@ -164,7 +171,9 @@ class Style extends Evented {
164
171
  _load(json) {
165
172
  this._loaded = true;
166
173
  this.stylesheet = json;
174
+ properties.forEach(prop => (this[prop] = json[prop]));
167
175
 
176
+ this.sources = {};
168
177
  for (const id in json.sources) {
169
178
  this.addSource(id, json.sources[id]);
170
179
  }
@@ -188,20 +197,22 @@ class Style extends Evented {
188
197
 
189
198
  this.glyphManager.setGlyphsLoader(json.glyphs);
190
199
 
191
- const layers = deref(this.stylesheet.layers);
192
-
193
- this._order = layers.map(layer => layer.id);
200
+ const layers = this.stylesheet.layers;
194
201
 
195
- this._layers = {};
202
+ this._layers.clear();
196
203
  for (let layer of layers) {
204
+ if (layer.ref) {
205
+ continue; // just ignore layers that reference other layers
206
+ }
197
207
  layer = createStyleLayer(layer);
198
208
  layer.setEventedParent(this, { layer: { id: layer.id } });
199
- this._layers[layer.id] = layer;
209
+ this._layers.set(layer.id, layer);
200
210
  }
201
211
 
202
- this.dispatcher.broadcast('setLayers', this._serializeLayers(this._order));
212
+ this.#layerIndex.replace(this._layers);
203
213
 
204
- this.light = new Light(this.stylesheet.light);
214
+ this.light = this.stylesheet.light;
215
+ this._light = new Light(this.light);
205
216
 
206
217
  this.setGlobalState(this.stylesheet.state ?? null);
207
218
 
@@ -210,7 +221,7 @@ class Style extends Evented {
210
221
  }
211
222
 
212
223
  _validateLayer(layer) {
213
- const sourceCache = this.sourceCaches[layer.source];
224
+ const sourceCache = this._sources[layer.source];
214
225
  if (!sourceCache) {
215
226
  return;
216
227
  }
@@ -239,30 +250,26 @@ class Style extends Evented {
239
250
 
240
251
  if (Object.keys(this._updatedSources).length) return false;
241
252
 
242
- for (const id in this.sourceCaches) if (!this.sourceCaches[id].loaded()) return false;
253
+ for (const id in this._sources) if (!this._sources[id].loaded()) return false;
243
254
 
244
255
  if (!this.imageManager.isLoaded()) return false;
245
256
 
246
257
  return true;
247
258
  }
248
259
 
249
- _serializeLayers(ids) {
250
- return ids.map(id => this._layers[id].serialize());
251
- }
252
-
253
260
  hasTransitions() {
254
- if (this.light?.hasTransition()) {
261
+ if (this._light?.hasTransition()) {
255
262
  return true;
256
263
  }
257
264
 
258
- for (const id in this.sourceCaches) {
259
- if (this.sourceCaches[id].hasTransition()) {
265
+ for (const id in this._sources) {
266
+ if (this._sources[id].hasTransition()) {
260
267
  return true;
261
268
  }
262
269
  }
263
270
 
264
- for (const id in this._layers) {
265
- if (this._layers[id].hasTransition()) {
271
+ for (const layer of this._layers.values()) {
272
+ if (layer.hasTransition()) {
266
273
  return true;
267
274
  }
268
275
  }
@@ -285,11 +292,8 @@ class Style extends Evented {
285
292
  }
286
293
 
287
294
  if (this._changed) {
288
- const updatedIds = Object.keys(this._updatedLayers);
289
- const removedIds = Object.keys(this._removedLayers);
290
-
291
- if (updatedIds.length || removedIds.length) {
292
- this._updateWorkerLayers(updatedIds, removedIds);
295
+ if (this._updatedLayers.size || this._removedLayers.size) {
296
+ this._updateWorkerLayers();
293
297
  }
294
298
  for (const id in this._updatedSources) {
295
299
  const action = this._updatedSources[id];
@@ -302,45 +306,40 @@ class Style extends Evented {
302
306
  }
303
307
 
304
308
  for (const id in this._updatedPaintProps) {
305
- this._layers[id].updateTransitions(parameters);
309
+ this._layers.get(id).updateTransitions(parameters);
306
310
  }
307
311
 
308
- this.light.updateTransitions(parameters);
312
+ this._light.updateTransitions(parameters);
309
313
 
310
314
  this._resetUpdates();
311
315
 
312
316
  this.fire(new Event('data', { dataType: 'style' }));
313
317
  }
314
318
 
315
- for (const sourceId in this.sourceCaches) {
316
- this.sourceCaches[sourceId].used = false;
319
+ for (const sourceId in this._sources) {
320
+ this._sources[sourceId].used = false;
317
321
  }
318
322
 
319
- for (const layerId of this._order) {
320
- const layer = this._layers[layerId];
321
-
323
+ for (const layer of this._layers.values()) {
322
324
  layer.recalculate(parameters);
323
325
  if (!layer.isHidden(parameters.zoom) && layer.source) {
324
- this.sourceCaches[layer.source].used = true;
326
+ this._sources[layer.source].used = true;
325
327
  }
326
328
  }
327
329
 
328
- this.light.recalculate(parameters);
330
+ this._light.recalculate(parameters);
329
331
  this.z = parameters.zoom;
330
332
  }
331
333
 
332
- _updateWorkerLayers(updatedIds, removedIds) {
333
- this.dispatcher.broadcast('updateLayers', {
334
- layers: this._serializeLayers(updatedIds),
335
- removedIds: removedIds
336
- });
334
+ _updateWorkerLayers() {
335
+ this.#layerIndex.update();
337
336
  }
338
337
 
339
338
  _resetUpdates() {
340
339
  this._changed = false;
341
340
 
342
- this._updatedLayers = {};
343
- this._removedLayers = {};
341
+ this._updatedLayers.clear();
342
+ this._removedLayers.clear();
344
343
 
345
344
  this._updatedSources = {};
346
345
  this._updatedPaintProps = {};
@@ -375,7 +374,7 @@ class Style extends Evented {
375
374
  addSource(id, source) {
376
375
  this._checkLoaded();
377
376
 
378
- if (this.sourceCaches[id] !== undefined) {
377
+ if (this._sources[id] !== undefined) {
379
378
  throw new Error('There is already a source with this ID');
380
379
  }
381
380
 
@@ -385,11 +384,17 @@ class Style extends Evented {
385
384
  );
386
385
  }
387
386
 
388
- const sourceCache = (this.sourceCaches[id] = new SourceCache(id, source, this.dispatcher));
387
+ this.sources[id] = source;
388
+
389
+ const sourceCache = (this._sources[id] = new SourceCache(id, source, {
390
+ resources: this.#resources,
391
+ layerIndex: this.#layerIndex,
392
+ showTileBoundaries: this.map.showTileBoundaries
393
+ }));
389
394
  sourceCache.style = this;
390
395
  sourceCache.setEventedParent(this, () => ({
391
396
  isSourceLoaded: this.loaded(),
392
- source: sourceCache.serialize(),
397
+ source: sourceCache,
393
398
  sourceId: id
394
399
  }));
395
400
 
@@ -405,19 +410,19 @@ class Style extends Evented {
405
410
  removeSource(id) {
406
411
  this._checkLoaded();
407
412
 
408
- if (this.sourceCaches[id] === undefined) {
413
+ if (this._sources[id] === undefined) {
409
414
  throw new Error('There is no source with this ID');
410
415
  }
411
- for (const layerId in this._layers) {
412
- if (this._layers[layerId].source === id) {
416
+ for (const [layerId, layer] of this._layers) {
417
+ if (layer.source === id) {
413
418
  return this.fire(
414
419
  new ErrorEvent(new Error(`Source "${id}" cannot be removed while layer "${layerId}" is using it.`))
415
420
  );
416
421
  }
417
422
  }
418
423
 
419
- const sourceCache = this.sourceCaches[id];
420
- delete this.sourceCaches[id];
424
+ const sourceCache = this._sources[id];
425
+ delete this._sources[id];
421
426
  delete this._updatedSources[id];
422
427
  sourceCache.fire(new Event('data', { sourceDataType: 'metadata', dataType: 'source', sourceId: id }));
423
428
  sourceCache.setEventedParent(null);
@@ -435,10 +440,11 @@ class Style extends Evented {
435
440
  setGeoJSONSourceData(id, data) {
436
441
  this._checkLoaded();
437
442
 
438
- assert(this.sourceCaches[id] !== undefined, 'There is no source with this ID');
439
- const geojsonSource = this.sourceCaches[id].getSource();
443
+ assert(this._sources[id] !== undefined, 'There is no source with this ID');
444
+ const geojsonSource = this._sources[id].getSource();
440
445
  assert(geojsonSource.type === 'geojson');
441
446
 
447
+ this.sources[id].data = data;
442
448
  geojsonSource.setData(data);
443
449
  this._changed = true;
444
450
  }
@@ -449,7 +455,36 @@ class Style extends Evented {
449
455
  * @returns {Object} source
450
456
  */
451
457
  getSource(id) {
452
- return this.sourceCaches[id]?.getSource();
458
+ return this._sources[id]?.getSource();
459
+ }
460
+
461
+ _insertLayer(id, layer, before, move) {
462
+ if (!before) {
463
+ if (move) {
464
+ this._layers.delete(id);
465
+ }
466
+ this._layers.set(id, layer);
467
+ this._layerOrderChanged = true;
468
+ return;
469
+ }
470
+ let beforeFound;
471
+ const _layers = new Map();
472
+ for (const [key, value] of this._layers.entries()) {
473
+ if (key === before) {
474
+ _layers.set(id, layer);
475
+ beforeFound = true;
476
+ }
477
+ if (move && key === id) {
478
+ continue;
479
+ }
480
+ _layers.set(key, value);
481
+ }
482
+ if (!beforeFound) {
483
+ this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`)));
484
+ return;
485
+ }
486
+ this._layers = _layers;
487
+ this._layerOrderChanged = true;
453
488
  }
454
489
 
455
490
  /**
@@ -478,18 +513,9 @@ class Style extends Evented {
478
513
 
479
514
  layer.setEventedParent(this, { layer: { id: id } });
480
515
 
481
- const index = before ? this._order.indexOf(before) : this._order.length;
482
- if (before && index === -1) {
483
- this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`)));
484
- return;
485
- }
516
+ this._insertLayer(id, layer, before);
486
517
 
487
- this._order.splice(index, 0, id);
488
- this._layerOrderChanged = true;
489
-
490
- this._layers[id] = layer;
491
-
492
- if (this._removedLayers[id] && layer.source) {
518
+ if (this._removedLayers.has(id) && layer.source) {
493
519
  // If, in the current batch, we have already removed this layer
494
520
  // and we are now re-adding it with a different `type`, then we
495
521
  // need to clear (rather than just reload) the underyling source's
@@ -497,13 +523,13 @@ class Style extends Evented {
497
523
  // buffers that are set up for the _previous_ version of this
498
524
  // layer, causing, e.g.:
499
525
  // https://github.com/mapbox/mapbox-gl-js/issues/3633
500
- const removed = this._removedLayers[id];
501
- delete this._removedLayers[id];
526
+ const removed = this._removedLayers.get(id);
527
+ this._removedLayers.delete(id);
502
528
  if (removed.type !== layer.type) {
503
529
  this._updatedSources[layer.source] = 'clear';
504
530
  } else {
505
531
  this._updatedSources[layer.source] = 'reload';
506
- this.sourceCaches[layer.source].pause();
532
+ this._sources[layer.source].pause();
507
533
  }
508
534
  }
509
535
  this._updateLayer(layer);
@@ -519,7 +545,7 @@ class Style extends Evented {
519
545
  this._checkLoaded();
520
546
  this._changed = true;
521
547
 
522
- const layer = this._layers[id];
548
+ const layer = this._layers.get(id);
523
549
  if (!layer) {
524
550
  this.fire(new ErrorEvent(new Error(`The layer '${id}' does not exist in the map's style and cannot be moved.`)));
525
551
  return;
@@ -529,17 +555,7 @@ class Style extends Evented {
529
555
  return;
530
556
  }
531
557
 
532
- const index = this._order.indexOf(id);
533
- this._order.splice(index, 1);
534
-
535
- const newIndex = before ? this._order.indexOf(before) : this._order.length;
536
- if (before && newIndex === -1) {
537
- this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`)));
538
- return;
539
- }
540
- this._order.splice(newIndex, 0, id);
541
-
542
- this._layerOrderChanged = true;
558
+ this._insertLayer(id, layer, before, true);
543
559
  }
544
560
 
545
561
  /**
@@ -553,7 +569,7 @@ class Style extends Evented {
553
569
  removeLayer(id) {
554
570
  this._checkLoaded();
555
571
 
556
- const layer = this._layers[id];
572
+ const layer = this._layers.get(id);
557
573
  if (!layer) {
558
574
  this.fire(
559
575
  new ErrorEvent(new Error(`The layer '${id}' does not exist in the map's style and cannot be removed.`))
@@ -563,14 +579,11 @@ class Style extends Evented {
563
579
 
564
580
  layer.setEventedParent(null);
565
581
 
566
- const index = this._order.indexOf(id);
567
- this._order.splice(index, 1);
568
-
569
582
  this._layerOrderChanged = true;
570
583
  this._changed = true;
571
- this._removedLayers[id] = layer;
572
- delete this._layers[id];
573
- delete this._updatedLayers[id];
584
+ this._removedLayers.set(id, layer);
585
+ this._layers.delete(id);
586
+ this._updatedLayers.delete(id);
574
587
  delete this._updatedPaintProps[id];
575
588
  }
576
589
 
@@ -581,7 +594,7 @@ class Style extends Evented {
581
594
  * @returns {?Object} a layer, if one with the given `id` exists
582
595
  */
583
596
  getLayer(id) {
584
- return this._layers[id];
597
+ return this._layers.get(id);
585
598
  }
586
599
 
587
600
  setLayerZoomRange(layerId, minzoom, maxzoom) {
@@ -597,15 +610,13 @@ class Style extends Evented {
597
610
  return;
598
611
  }
599
612
 
600
- if (layer.minzoom === minzoom && layer.maxzoom === maxzoom) return;
601
-
602
- if (minzoom != null) {
603
- layer.minzoom = minzoom;
604
- }
605
- if (maxzoom != null) {
606
- layer.maxzoom = maxzoom;
613
+ if (layer._setZoomRange(minzoom, maxzoom)) {
614
+ this._updateLayer(layer);
607
615
  }
608
- this._updateLayer(layer);
616
+ }
617
+
618
+ get layers() {
619
+ return Array.from(this._layers.values());
609
620
  }
610
621
 
611
622
  setFilter(layerId, filter) {
@@ -656,6 +667,10 @@ class Style extends Evented {
656
667
  if (deepEqual(layer.getLayoutProperty(name), value)) return;
657
668
 
658
669
  layer.setLayoutProperty(name, value);
670
+ const layerObject = this.layers.find(({ id }) => id === layerId);
671
+ layerObject.layout ??= {};
672
+ layerObject.layout[name] = value;
673
+
659
674
  this._updateLayer(layer);
660
675
  }
661
676
 
@@ -682,13 +697,21 @@ class Style extends Evented {
682
697
 
683
698
  if (deepEqual(layer.getPaintProperty(name), value)) return;
684
699
 
700
+ const layerObject = this.layers.find(({ id }) => id === layerId);
701
+ layerObject.paint ??= {};
702
+ layerObject.paint[name] = value;
703
+
704
+ this._updatePaintProperty(layer, name, value);
705
+ }
706
+
707
+ _updatePaintProperty(layer, name, value) {
685
708
  const requiresRelayout = layer.setPaintProperty(name, value);
686
709
  if (requiresRelayout) {
687
710
  this._updateLayer(layer);
688
711
  }
689
712
 
690
713
  this._changed = true;
691
- this._updatedPaintProps[layerId] = true;
714
+ this._updatedPaintProps[layer.id] = true;
692
715
  }
693
716
 
694
717
  getPaintProperty(layer, name) {
@@ -699,13 +722,17 @@ class Style extends Evented {
699
722
  this._checkLoaded();
700
723
  const sourceId = feature.source;
701
724
  const sourceLayer = feature.sourceLayer;
702
- const sourceCache = this.sourceCaches[sourceId];
725
+ const sourceCache = this._sources[sourceId];
703
726
 
704
727
  if (sourceCache === undefined) {
705
728
  this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`)));
706
729
  return;
707
730
  }
708
731
  const sourceType = sourceCache.getSource().type;
732
+ if (sourceType === 'geojson' && sourceLayer) {
733
+ this.fire(new ErrorEvent(new Error('GeoJSON sources cannot have a sourceLayer parameter.')));
734
+ return;
735
+ }
709
736
  if (sourceType === 'vector' && !sourceLayer) {
710
737
  this.fire(new ErrorEvent(new Error('The sourceLayer parameter must be provided for vector source types.')));
711
738
  return;
@@ -718,11 +745,37 @@ class Style extends Evented {
718
745
  sourceCache.setFeatureState(sourceLayer, feature.id, state);
719
746
  }
720
747
 
748
+ removeFeatureState(target, key) {
749
+ this._checkLoaded();
750
+ const sourceId = target.source;
751
+ const sourceCache = this._sources[sourceId];
752
+
753
+ if (sourceCache === undefined) {
754
+ this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`)));
755
+ return;
756
+ }
757
+
758
+ const sourceType = sourceCache.getSource().type;
759
+ const sourceLayer = sourceType === 'vector' ? target.sourceLayer : undefined;
760
+
761
+ if (sourceType === 'vector' && !sourceLayer) {
762
+ this.fire(new ErrorEvent(new Error('The sourceLayer parameter must be provided for vector source types.')));
763
+ return;
764
+ }
765
+
766
+ if (key && typeof target.id !== 'string' && typeof target.id !== 'number') {
767
+ this.fire(new ErrorEvent(new Error('A feature id is required to remove its specific state property.')));
768
+ return;
769
+ }
770
+
771
+ sourceCache.removeFeatureState(sourceLayer, target.id, key);
772
+ }
773
+
721
774
  getFeatureState(feature) {
722
775
  this._checkLoaded();
723
776
  const sourceId = feature.source;
724
777
  const sourceLayer = feature.sourceLayer;
725
- const sourceCache = this.sourceCaches[sourceId];
778
+ const sourceCache = this._sources[sourceId];
726
779
 
727
780
  if (sourceCache === undefined) {
728
781
  this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`)));
@@ -741,63 +794,79 @@ class Style extends Evented {
741
794
  return Object.assign({ duration: 300, delay: 0 }, this.stylesheet?.transition);
742
795
  }
743
796
 
744
- serialize() {
745
- return filterObject(
746
- {
747
- version: this.stylesheet.version,
748
- name: this.stylesheet.name,
749
- metadata: this.stylesheet.metadata,
750
- light: this.stylesheet.light,
751
- center: this.stylesheet.center,
752
- zoom: this.stylesheet.zoom,
753
- bearing: this.stylesheet.bearing,
754
- pitch: this.stylesheet.pitch,
755
- sprite: this.stylesheet.sprite,
756
- glyphs: this.stylesheet.glyphs,
757
- transition: this.stylesheet.transition,
758
- sources: mapObject(this.sourceCaches, source => source.serialize()),
759
- layers: this._order.map(id => this._layers[id].serialize())
760
- },
761
- value => value !== undefined
762
- );
763
- }
764
-
765
797
  _updateLayer(layer) {
766
- this._updatedLayers[layer.id] = true;
798
+ this._updatedLayers.set(layer.id, layer);
767
799
  if (layer.source && !this._updatedSources[layer.source]) {
768
800
  this._updatedSources[layer.source] = 'reload';
769
- this.sourceCaches[layer.source].pause();
801
+ this._sources[layer.source].pause();
770
802
  }
771
803
  this._changed = true;
772
804
  }
773
805
 
774
806
  _flattenAndSortRenderedFeatures(sourceResults) {
775
- const features = [];
807
+ // Feature order is complicated.
808
+ // The order between features in two 2D layers is always determined by layer order.
809
+ // The order between features in two 3D layers is always determined by depth.
810
+ // The order between a feature in a 2D layer and a 3D layer is tricky:
811
+ // Most often layer order determines the feature order in this case. If
812
+ // a line layer is above a extrusion layer the line feature will be rendered
813
+ // above the extrusion. If the line layer is below the extrusion layer,
814
+ // it will be rendered below it.
815
+ //
816
+ // There is a weird case though.
817
+ // You have layers in this order: extrusion_layer_a, line_layer, extrusion_layer_b
818
+ // Each layer has a feature that overlaps the other features.
819
+ // The feature in extrusion_layer_a is closer than the feature in extrusion_layer_b so it is rendered above.
820
+ // The feature in line_layer is rendered above extrusion_layer_a.
821
+ // This means that that the line_layer feature is above the extrusion_layer_b feature despite
822
+ // it being in an earlier layer.
823
+
824
+ const isLayer3D = layer => layer.type === 'fill-extrusion';
825
+
826
+ const layerIndex = new Map();
776
827
  const features3D = [];
777
- for (let l = this._order.length - 1; l >= 0; l--) {
778
- const layerId = this._order[l];
779
- for (const sourceResult of sourceResults) {
780
- const layerFeatures = sourceResult[layerId];
781
- if (layerFeatures) {
782
- if (this._layers[layerId].type === 'fill-extrusion') {
828
+ const layers = Array.from(this._layers.values());
829
+ for (let l = layers.length - 1; l >= 0; l--) {
830
+ const layer = layers[l];
831
+ if (isLayer3D(layer)) {
832
+ layerIndex.set(layer.id, l);
833
+ for (const sourceResult of sourceResults) {
834
+ const layerFeatures = sourceResult[layer.id];
835
+ if (layerFeatures) {
783
836
  for (const featureWrapper of layerFeatures) {
784
837
  features3D.push(featureWrapper);
785
838
  }
786
- } else {
787
- for (const featureWrapper of layerFeatures) {
788
- features.push(featureWrapper.feature);
789
- }
790
839
  }
791
840
  }
792
841
  }
793
842
  }
794
843
 
795
844
  features3D.sort((a, b) => {
796
- return a.intersectionZ - b.intersectionZ;
845
+ return b.intersectionZ - a.intersectionZ;
797
846
  });
798
847
 
799
- for (const featureWrapper of features3D) {
800
- features.push(featureWrapper.feature);
848
+ const features = [];
849
+ for (let l = layers.length - 1; l >= 0; l--) {
850
+ const layer = layers[l];
851
+
852
+ if (isLayer3D(layer)) {
853
+ // add all 3D features that are in or above the current layer
854
+ for (let i = features3D.length - 1; i >= 0; i--) {
855
+ const topmost3D = features3D[i].feature;
856
+ if (layerIndex.get(topmost3D.layer.id) < l) break;
857
+ features.push(topmost3D);
858
+ features3D.pop();
859
+ }
860
+ } else {
861
+ for (const sourceResult of sourceResults) {
862
+ const layerFeatures = sourceResult[layer.id];
863
+ if (layerFeatures) {
864
+ for (const featureWrapper of layerFeatures) {
865
+ features.push(featureWrapper.feature);
866
+ }
867
+ }
868
+ }
869
+ }
801
870
  }
802
871
 
803
872
  return features;
@@ -811,7 +880,7 @@ class Style extends Evented {
811
880
  return [];
812
881
  }
813
882
  for (const layerId of params.layers) {
814
- const layer = this._layers[layerId];
883
+ const layer = this._layers.get(layerId);
815
884
  if (layer) {
816
885
  includedSources[layer.source] = true;
817
886
  }
@@ -819,10 +888,10 @@ class Style extends Evented {
819
888
  }
820
889
 
821
890
  const sourceResults = [];
822
- for (const id in this.sourceCaches) {
891
+ for (const id in this._sources) {
823
892
  if (params.layers && !includedSources[id]) continue;
824
893
  sourceResults.push(
825
- queryRenderedFeatures(this.sourceCaches[id], this._layers, queryGeometry.viewport, params, transform)
894
+ queryRenderedFeatures(this._sources[id], this._layers, queryGeometry.viewport, params, transform)
826
895
  );
827
896
  }
828
897
 
@@ -832,7 +901,7 @@ class Style extends Evented {
832
901
  sourceResults.push(
833
902
  queryRenderedSymbols(
834
903
  this._layers,
835
- this.sourceCaches,
904
+ this._sources,
836
905
  queryGeometry.viewport,
837
906
  params,
838
907
  this.placement.collisionIndex,
@@ -844,18 +913,18 @@ class Style extends Evented {
844
913
  }
845
914
 
846
915
  querySourceFeatures(sourceID, params) {
847
- const sourceCache = this.sourceCaches[sourceID];
916
+ const sourceCache = this._sources[sourceID];
848
917
  return sourceCache ? querySourceFeatures(sourceCache, params) : [];
849
918
  }
850
919
 
851
920
  getLight() {
852
- return this.light.getLight();
921
+ return this._light.getLight();
853
922
  }
854
923
 
855
924
  setLight(lightOptions) {
856
925
  this._checkLoaded();
857
926
 
858
- const light = this.light.getLight();
927
+ const light = this._light.getLight();
859
928
  let _update = false;
860
929
  for (const key in lightOptions) {
861
930
  if (!deepEqual(lightOptions[key], light[key])) {
@@ -876,35 +945,40 @@ class Style extends Evented {
876
945
  )
877
946
  };
878
947
 
879
- this.light.setLight(lightOptions);
880
- this.light.updateTransitions(parameters);
948
+ this._light.setLight(lightOptions);
949
+ this._light.updateTransitions(parameters);
881
950
  }
882
951
 
883
952
  _remove() {
884
- rtlTextPluginEvented.off('pluginAvailable', this._rtlTextPluginCallback);
885
- for (const id in this.sourceCaches) {
886
- this.sourceCaches[id].clearTiles();
953
+ this._rtlTextPluginCallbackUnregister?.();
954
+ for (const id in this._sources) {
955
+ this._sources[id].clearTiles();
887
956
  }
888
- this.dispatcher.remove();
889
957
  }
890
958
 
891
959
  _clearSource(id) {
892
- this.sourceCaches[id].clearTiles();
960
+ this._sources[id].clearTiles();
893
961
  }
894
962
 
895
963
  _reloadSource(id) {
896
- this.sourceCaches[id].resume();
897
- this.sourceCaches[id].reload();
964
+ this._sources[id].resume();
965
+ this._sources[id].reload();
898
966
  }
899
967
 
900
968
  _updateSources(transform) {
901
- for (const id in this.sourceCaches) {
902
- this.sourceCaches[id].update(transform);
969
+ for (const id in this._sources) {
970
+ this._sources[id].update(transform);
971
+ }
972
+ }
973
+
974
+ _reloadSources() {
975
+ for (const sourceCache of Object.values(this._sources)) {
976
+ sourceCache.reload(); // Should be a no-op if called before any tiles load
903
977
  }
904
978
  }
905
979
 
906
980
  _generateCollisionBoxes() {
907
- for (const id in this.sourceCaches) {
981
+ for (const id in this._sources) {
908
982
  this._reloadSource(id);
909
983
  }
910
984
  }
@@ -915,26 +989,25 @@ class Style extends Evented {
915
989
 
916
990
  const layerTiles = {};
917
991
 
918
- for (const layerID of this._order) {
919
- const styleLayer = this._layers[layerID];
920
- if (styleLayer.type !== 'symbol') continue;
992
+ for (const layer of this._layers.values()) {
993
+ if (layer.type !== 'symbol') continue;
921
994
 
922
- if (!layerTiles[styleLayer.source]) {
923
- const sourceCache = this.sourceCaches[styleLayer.source];
924
- layerTiles[styleLayer.source] = sourceCache
995
+ if (!layerTiles[layer.source]) {
996
+ const sourceCache = this._sources[layer.source];
997
+ layerTiles[layer.source] = sourceCache
925
998
  .getRenderableIds(true)
926
999
  .map(id => sourceCache.getTileByID(id))
927
1000
  .sort((a, b) => b.tileID.overscaledZ - a.tileID.overscaledZ || (a.tileID.isLessThan(b.tileID) ? -1 : 1));
928
1001
  }
929
1002
 
930
1003
  const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(
931
- styleLayer,
932
- layerTiles[styleLayer.source],
1004
+ layer,
1005
+ layerTiles[layer.source],
933
1006
  transform.center.lng
934
1007
  );
935
1008
  symbolBucketsChanged = symbolBucketsChanged || layerBucketsChanged;
936
1009
  }
937
- this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
1010
+ this.crossTileSymbolIndex.pruneUnusedLayers(this._layers.keys());
938
1011
 
939
1012
  // Anything that changes our "in progress" layer and tile indices requires us
940
1013
  // to start over. When we start over, we do a full placement instead of incremental
@@ -949,7 +1022,7 @@ class Style extends Evented {
949
1022
  ) {
950
1023
  this.pauseablePlacement = new PauseablePlacement(
951
1024
  transform,
952
- this._order,
1025
+ this._layers.size - 1,
953
1026
  forceFullPlacement,
954
1027
  showCollisionBoxes,
955
1028
  fadeDuration,
@@ -965,7 +1038,7 @@ class Style extends Evented {
965
1038
  // render frame
966
1039
  this.placement.setStale();
967
1040
  } else {
968
- this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
1041
+ this.pauseablePlacement.continuePlacement(Array.from(this._layers.values()), layerTiles);
969
1042
 
970
1043
  if (this.pauseablePlacement.isDone()) {
971
1044
  this.placement = this.pauseablePlacement.commit(this.placement, browser.now());
@@ -981,10 +1054,9 @@ class Style extends Evented {
981
1054
  }
982
1055
 
983
1056
  if (placementCommitted || symbolBucketsChanged) {
984
- for (const layerID of this._order) {
985
- const styleLayer = this._layers[layerID];
986
- if (styleLayer.type !== 'symbol') continue;
987
- this.placement.updateLayerOpacities(styleLayer, layerTiles[styleLayer.source]);
1057
+ for (const layer of this._layers.values()) {
1058
+ if (layer.type !== 'symbol') continue;
1059
+ this.placement.updateLayerOpacities(layer, layerTiles[layer.source]);
988
1060
  }
989
1061
  }
990
1062
 
@@ -994,24 +1066,22 @@ class Style extends Evented {
994
1066
  }
995
1067
 
996
1068
  _releaseSymbolFadeTiles() {
997
- for (const id in this.sourceCaches) {
998
- this.sourceCaches[id].releaseSymbolFadeTiles();
1069
+ for (const id in this._sources) {
1070
+ this._sources[id].releaseSymbolFadeTiles();
999
1071
  }
1000
1072
  }
1001
1073
 
1002
1074
  // Callbacks from web workers
1003
-
1004
- getImages(_mapId, { icons }) {
1075
+ getImages({ icons }) {
1005
1076
  return this.imageManager.getImages(icons);
1006
1077
  }
1007
1078
 
1008
- loadGlyphRange(_mapId, { stack, range }) {
1079
+ loadGlyphRange({ stack, range }) {
1009
1080
  return this.glyphManager.loadGlyphRange(stack, range);
1010
1081
  }
1011
1082
  }
1012
1083
 
1013
1084
  Style.getSourceType = getSourceType;
1014
1085
  Style.setSourceType = setSourceType;
1015
- Style.registerForPluginAvailability = registerForPluginAvailability;
1016
1086
 
1017
1087
  module.exports = Style;