@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
@@ -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
  }
@@ -126,8 +136,7 @@ class Style extends Evented {
126
136
 
127
137
  const sourceIdsToReload = new Set();
128
138
 
129
- for (const layerId in this._layers) {
130
- const layer = this._layers[layerId];
139
+ for (const layer of this._layers.values()) {
131
140
  const layoutAffectingGlobalStateRefs = layer.getLayoutAffectingGlobalStateRefs();
132
141
  const paintAffectingGlobalStateRefs = layer.getPaintAffectingGlobalStateRefs();
133
142
 
@@ -143,7 +152,7 @@ class Style extends Evented {
143
152
  }
144
153
  }
145
154
 
146
- for (const id in this.sourceCaches) {
155
+ for (const id in this._sources) {
147
156
  if (sourceIdsToReload.has(id)) {
148
157
  this._reloadSource(id);
149
158
  this._changed = true;
@@ -162,7 +171,9 @@ class Style extends Evented {
162
171
  _load(json) {
163
172
  this._loaded = true;
164
173
  this.stylesheet = json;
174
+ properties.forEach(prop => (this[prop] = json[prop]));
165
175
 
176
+ this.sources = {};
166
177
  for (const id in json.sources) {
167
178
  this.addSource(id, json.sources[id]);
168
179
  }
@@ -186,20 +197,22 @@ class Style extends Evented {
186
197
 
187
198
  this.glyphManager.setGlyphsLoader(json.glyphs);
188
199
 
189
- const layers = deref(this.stylesheet.layers);
200
+ const layers = this.stylesheet.layers;
190
201
 
191
- this._order = layers.map(layer => layer.id);
192
-
193
- this._layers = {};
202
+ this._layers.clear();
194
203
  for (let layer of layers) {
204
+ if (layer.ref) {
205
+ continue; // just ignore layers that reference other layers
206
+ }
195
207
  layer = createStyleLayer(layer);
196
208
  layer.setEventedParent(this, { layer: { id: layer.id } });
197
- this._layers[layer.id] = layer;
209
+ this._layers.set(layer.id, layer);
198
210
  }
199
211
 
200
- this.dispatcher.broadcast('setLayers', this._serializeLayers(this._order));
212
+ this.#layerIndex.replace(this._layers);
201
213
 
202
- this.light = new Light(this.stylesheet.light);
214
+ this.light = this.stylesheet.light;
215
+ this._light = new Light(this.light);
203
216
 
204
217
  this.setGlobalState(this.stylesheet.state ?? null);
205
218
 
@@ -208,7 +221,7 @@ class Style extends Evented {
208
221
  }
209
222
 
210
223
  _validateLayer(layer) {
211
- const sourceCache = this.sourceCaches[layer.source];
224
+ const sourceCache = this._sources[layer.source];
212
225
  if (!sourceCache) {
213
226
  return;
214
227
  }
@@ -237,30 +250,26 @@ class Style extends Evented {
237
250
 
238
251
  if (Object.keys(this._updatedSources).length) return false;
239
252
 
240
- 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;
241
254
 
242
255
  if (!this.imageManager.isLoaded()) return false;
243
256
 
244
257
  return true;
245
258
  }
246
259
 
247
- _serializeLayers(ids) {
248
- return ids.map(id => this._layers[id].serialize());
249
- }
250
-
251
260
  hasTransitions() {
252
- if (this.light?.hasTransition()) {
261
+ if (this._light?.hasTransition()) {
253
262
  return true;
254
263
  }
255
264
 
256
- for (const id in this.sourceCaches) {
257
- if (this.sourceCaches[id].hasTransition()) {
265
+ for (const id in this._sources) {
266
+ if (this._sources[id].hasTransition()) {
258
267
  return true;
259
268
  }
260
269
  }
261
270
 
262
- for (const id in this._layers) {
263
- if (this._layers[id].hasTransition()) {
271
+ for (const layer of this._layers.values()) {
272
+ if (layer.hasTransition()) {
264
273
  return true;
265
274
  }
266
275
  }
@@ -283,11 +292,8 @@ class Style extends Evented {
283
292
  }
284
293
 
285
294
  if (this._changed) {
286
- const updatedIds = Object.keys(this._updatedLayers);
287
- const removedIds = Object.keys(this._removedLayers);
288
-
289
- if (updatedIds.length || removedIds.length) {
290
- this._updateWorkerLayers(updatedIds, removedIds);
295
+ if (this._updatedLayers.size || this._removedLayers.size) {
296
+ this._updateWorkerLayers();
291
297
  }
292
298
  for (const id in this._updatedSources) {
293
299
  const action = this._updatedSources[id];
@@ -300,45 +306,40 @@ class Style extends Evented {
300
306
  }
301
307
 
302
308
  for (const id in this._updatedPaintProps) {
303
- this._layers[id].updateTransitions(parameters);
309
+ this._layers.get(id).updateTransitions(parameters);
304
310
  }
305
311
 
306
- this.light.updateTransitions(parameters);
312
+ this._light.updateTransitions(parameters);
307
313
 
308
314
  this._resetUpdates();
309
315
 
310
316
  this.fire(new Event('data', { dataType: 'style' }));
311
317
  }
312
318
 
313
- for (const sourceId in this.sourceCaches) {
314
- this.sourceCaches[sourceId].used = false;
319
+ for (const sourceId in this._sources) {
320
+ this._sources[sourceId].used = false;
315
321
  }
316
322
 
317
- for (const layerId of this._order) {
318
- const layer = this._layers[layerId];
319
-
323
+ for (const layer of this._layers.values()) {
320
324
  layer.recalculate(parameters);
321
325
  if (!layer.isHidden(parameters.zoom) && layer.source) {
322
- this.sourceCaches[layer.source].used = true;
326
+ this._sources[layer.source].used = true;
323
327
  }
324
328
  }
325
329
 
326
- this.light.recalculate(parameters);
330
+ this._light.recalculate(parameters);
327
331
  this.z = parameters.zoom;
328
332
  }
329
333
 
330
- _updateWorkerLayers(updatedIds, removedIds) {
331
- this.dispatcher.broadcast('updateLayers', {
332
- layers: this._serializeLayers(updatedIds),
333
- removedIds: removedIds
334
- });
334
+ _updateWorkerLayers() {
335
+ this.#layerIndex.update();
335
336
  }
336
337
 
337
338
  _resetUpdates() {
338
339
  this._changed = false;
339
340
 
340
- this._updatedLayers = {};
341
- this._removedLayers = {};
341
+ this._updatedLayers.clear();
342
+ this._removedLayers.clear();
342
343
 
343
344
  this._updatedSources = {};
344
345
  this._updatedPaintProps = {};
@@ -373,7 +374,7 @@ class Style extends Evented {
373
374
  addSource(id, source) {
374
375
  this._checkLoaded();
375
376
 
376
- if (this.sourceCaches[id] !== undefined) {
377
+ if (this._sources[id] !== undefined) {
377
378
  throw new Error('There is already a source with this ID');
378
379
  }
379
380
 
@@ -383,11 +384,17 @@ class Style extends Evented {
383
384
  );
384
385
  }
385
386
 
386
- 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
+ }));
387
394
  sourceCache.style = this;
388
395
  sourceCache.setEventedParent(this, () => ({
389
396
  isSourceLoaded: this.loaded(),
390
- source: sourceCache.serialize(),
397
+ source: sourceCache,
391
398
  sourceId: id
392
399
  }));
393
400
 
@@ -403,19 +410,19 @@ class Style extends Evented {
403
410
  removeSource(id) {
404
411
  this._checkLoaded();
405
412
 
406
- if (this.sourceCaches[id] === undefined) {
413
+ if (this._sources[id] === undefined) {
407
414
  throw new Error('There is no source with this ID');
408
415
  }
409
- for (const layerId in this._layers) {
410
- if (this._layers[layerId].source === id) {
416
+ for (const [layerId, layer] of this._layers) {
417
+ if (layer.source === id) {
411
418
  return this.fire(
412
419
  new ErrorEvent(new Error(`Source "${id}" cannot be removed while layer "${layerId}" is using it.`))
413
420
  );
414
421
  }
415
422
  }
416
423
 
417
- const sourceCache = this.sourceCaches[id];
418
- delete this.sourceCaches[id];
424
+ const sourceCache = this._sources[id];
425
+ delete this._sources[id];
419
426
  delete this._updatedSources[id];
420
427
  sourceCache.fire(new Event('data', { sourceDataType: 'metadata', dataType: 'source', sourceId: id }));
421
428
  sourceCache.setEventedParent(null);
@@ -433,10 +440,11 @@ class Style extends Evented {
433
440
  setGeoJSONSourceData(id, data) {
434
441
  this._checkLoaded();
435
442
 
436
- assert(this.sourceCaches[id] !== undefined, 'There is no source with this ID');
437
- 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();
438
445
  assert(geojsonSource.type === 'geojson');
439
446
 
447
+ this.sources[id].data = data;
440
448
  geojsonSource.setData(data);
441
449
  this._changed = true;
442
450
  }
@@ -447,7 +455,36 @@ class Style extends Evented {
447
455
  * @returns {Object} source
448
456
  */
449
457
  getSource(id) {
450
- 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;
451
488
  }
452
489
 
453
490
  /**
@@ -476,18 +513,9 @@ class Style extends Evented {
476
513
 
477
514
  layer.setEventedParent(this, { layer: { id: id } });
478
515
 
479
- const index = before ? this._order.indexOf(before) : this._order.length;
480
- if (before && index === -1) {
481
- this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`)));
482
- return;
483
- }
516
+ this._insertLayer(id, layer, before);
484
517
 
485
- this._order.splice(index, 0, id);
486
- this._layerOrderChanged = true;
487
-
488
- this._layers[id] = layer;
489
-
490
- if (this._removedLayers[id] && layer.source) {
518
+ if (this._removedLayers.has(id) && layer.source) {
491
519
  // If, in the current batch, we have already removed this layer
492
520
  // and we are now re-adding it with a different `type`, then we
493
521
  // need to clear (rather than just reload) the underyling source's
@@ -495,13 +523,13 @@ class Style extends Evented {
495
523
  // buffers that are set up for the _previous_ version of this
496
524
  // layer, causing, e.g.:
497
525
  // https://github.com/mapbox/mapbox-gl-js/issues/3633
498
- const removed = this._removedLayers[id];
499
- delete this._removedLayers[id];
526
+ const removed = this._removedLayers.get(id);
527
+ this._removedLayers.delete(id);
500
528
  if (removed.type !== layer.type) {
501
529
  this._updatedSources[layer.source] = 'clear';
502
530
  } else {
503
531
  this._updatedSources[layer.source] = 'reload';
504
- this.sourceCaches[layer.source].pause();
532
+ this._sources[layer.source].pause();
505
533
  }
506
534
  }
507
535
  this._updateLayer(layer);
@@ -517,7 +545,7 @@ class Style extends Evented {
517
545
  this._checkLoaded();
518
546
  this._changed = true;
519
547
 
520
- const layer = this._layers[id];
548
+ const layer = this._layers.get(id);
521
549
  if (!layer) {
522
550
  this.fire(new ErrorEvent(new Error(`The layer '${id}' does not exist in the map's style and cannot be moved.`)));
523
551
  return;
@@ -527,17 +555,7 @@ class Style extends Evented {
527
555
  return;
528
556
  }
529
557
 
530
- const index = this._order.indexOf(id);
531
- this._order.splice(index, 1);
532
-
533
- const newIndex = before ? this._order.indexOf(before) : this._order.length;
534
- if (before && newIndex === -1) {
535
- this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`)));
536
- return;
537
- }
538
- this._order.splice(newIndex, 0, id);
539
-
540
- this._layerOrderChanged = true;
558
+ this._insertLayer(id, layer, before, true);
541
559
  }
542
560
 
543
561
  /**
@@ -551,7 +569,7 @@ class Style extends Evented {
551
569
  removeLayer(id) {
552
570
  this._checkLoaded();
553
571
 
554
- const layer = this._layers[id];
572
+ const layer = this._layers.get(id);
555
573
  if (!layer) {
556
574
  this.fire(
557
575
  new ErrorEvent(new Error(`The layer '${id}' does not exist in the map's style and cannot be removed.`))
@@ -561,14 +579,11 @@ class Style extends Evented {
561
579
 
562
580
  layer.setEventedParent(null);
563
581
 
564
- const index = this._order.indexOf(id);
565
- this._order.splice(index, 1);
566
-
567
582
  this._layerOrderChanged = true;
568
583
  this._changed = true;
569
- this._removedLayers[id] = layer;
570
- delete this._layers[id];
571
- delete this._updatedLayers[id];
584
+ this._removedLayers.set(id, layer);
585
+ this._layers.delete(id);
586
+ this._updatedLayers.delete(id);
572
587
  delete this._updatedPaintProps[id];
573
588
  }
574
589
 
@@ -579,7 +594,7 @@ class Style extends Evented {
579
594
  * @returns {?Object} a layer, if one with the given `id` exists
580
595
  */
581
596
  getLayer(id) {
582
- return this._layers[id];
597
+ return this._layers.get(id);
583
598
  }
584
599
 
585
600
  setLayerZoomRange(layerId, minzoom, maxzoom) {
@@ -595,15 +610,13 @@ class Style extends Evented {
595
610
  return;
596
611
  }
597
612
 
598
- if (layer.minzoom === minzoom && layer.maxzoom === maxzoom) return;
599
-
600
- if (minzoom != null) {
601
- layer.minzoom = minzoom;
602
- }
603
- if (maxzoom != null) {
604
- layer.maxzoom = maxzoom;
613
+ if (layer._setZoomRange(minzoom, maxzoom)) {
614
+ this._updateLayer(layer);
605
615
  }
606
- this._updateLayer(layer);
616
+ }
617
+
618
+ get layers() {
619
+ return Array.from(this._layers.values());
607
620
  }
608
621
 
609
622
  setFilter(layerId, filter) {
@@ -654,6 +667,10 @@ class Style extends Evented {
654
667
  if (deepEqual(layer.getLayoutProperty(name), value)) return;
655
668
 
656
669
  layer.setLayoutProperty(name, value);
670
+ const layerObject = this.layers.find(({ id }) => id === layerId);
671
+ layerObject.layout ??= {};
672
+ layerObject.layout[name] = value;
673
+
657
674
  this._updateLayer(layer);
658
675
  }
659
676
 
@@ -680,6 +697,10 @@ class Style extends Evented {
680
697
 
681
698
  if (deepEqual(layer.getPaintProperty(name), value)) return;
682
699
 
700
+ const layerObject = this.layers.find(({ id }) => id === layerId);
701
+ layerObject.paint ??= {};
702
+ layerObject.paint[name] = value;
703
+
683
704
  this._updatePaintProperty(layer, name, value);
684
705
  }
685
706
 
@@ -701,13 +722,17 @@ class Style extends Evented {
701
722
  this._checkLoaded();
702
723
  const sourceId = feature.source;
703
724
  const sourceLayer = feature.sourceLayer;
704
- const sourceCache = this.sourceCaches[sourceId];
725
+ const sourceCache = this._sources[sourceId];
705
726
 
706
727
  if (sourceCache === undefined) {
707
728
  this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`)));
708
729
  return;
709
730
  }
710
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
+ }
711
736
  if (sourceType === 'vector' && !sourceLayer) {
712
737
  this.fire(new ErrorEvent(new Error('The sourceLayer parameter must be provided for vector source types.')));
713
738
  return;
@@ -720,11 +745,37 @@ class Style extends Evented {
720
745
  sourceCache.setFeatureState(sourceLayer, feature.id, state);
721
746
  }
722
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
+
723
774
  getFeatureState(feature) {
724
775
  this._checkLoaded();
725
776
  const sourceId = feature.source;
726
777
  const sourceLayer = feature.sourceLayer;
727
- const sourceCache = this.sourceCaches[sourceId];
778
+ const sourceCache = this._sources[sourceId];
728
779
 
729
780
  if (sourceCache === undefined) {
730
781
  this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`)));
@@ -743,63 +794,79 @@ class Style extends Evented {
743
794
  return Object.assign({ duration: 300, delay: 0 }, this.stylesheet?.transition);
744
795
  }
745
796
 
746
- serialize() {
747
- return filterObject(
748
- {
749
- version: this.stylesheet.version,
750
- name: this.stylesheet.name,
751
- metadata: this.stylesheet.metadata,
752
- light: this.stylesheet.light,
753
- center: this.stylesheet.center,
754
- zoom: this.stylesheet.zoom,
755
- bearing: this.stylesheet.bearing,
756
- pitch: this.stylesheet.pitch,
757
- sprite: this.stylesheet.sprite,
758
- glyphs: this.stylesheet.glyphs,
759
- transition: this.stylesheet.transition,
760
- sources: mapObject(this.sourceCaches, source => source.serialize()),
761
- layers: this._order.map(id => this._layers[id].serialize())
762
- },
763
- value => value !== undefined
764
- );
765
- }
766
-
767
797
  _updateLayer(layer) {
768
- this._updatedLayers[layer.id] = true;
798
+ this._updatedLayers.set(layer.id, layer);
769
799
  if (layer.source && !this._updatedSources[layer.source]) {
770
800
  this._updatedSources[layer.source] = 'reload';
771
- this.sourceCaches[layer.source].pause();
801
+ this._sources[layer.source].pause();
772
802
  }
773
803
  this._changed = true;
774
804
  }
775
805
 
776
806
  _flattenAndSortRenderedFeatures(sourceResults) {
777
- 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();
778
827
  const features3D = [];
779
- for (let l = this._order.length - 1; l >= 0; l--) {
780
- const layerId = this._order[l];
781
- for (const sourceResult of sourceResults) {
782
- const layerFeatures = sourceResult[layerId];
783
- if (layerFeatures) {
784
- 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) {
785
836
  for (const featureWrapper of layerFeatures) {
786
837
  features3D.push(featureWrapper);
787
838
  }
788
- } else {
789
- for (const featureWrapper of layerFeatures) {
790
- features.push(featureWrapper.feature);
791
- }
792
839
  }
793
840
  }
794
841
  }
795
842
  }
796
843
 
797
844
  features3D.sort((a, b) => {
798
- return a.intersectionZ - b.intersectionZ;
845
+ return b.intersectionZ - a.intersectionZ;
799
846
  });
800
847
 
801
- for (const featureWrapper of features3D) {
802
- 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
+ }
803
870
  }
804
871
 
805
872
  return features;
@@ -813,7 +880,7 @@ class Style extends Evented {
813
880
  return [];
814
881
  }
815
882
  for (const layerId of params.layers) {
816
- const layer = this._layers[layerId];
883
+ const layer = this._layers.get(layerId);
817
884
  if (layer) {
818
885
  includedSources[layer.source] = true;
819
886
  }
@@ -821,10 +888,10 @@ class Style extends Evented {
821
888
  }
822
889
 
823
890
  const sourceResults = [];
824
- for (const id in this.sourceCaches) {
891
+ for (const id in this._sources) {
825
892
  if (params.layers && !includedSources[id]) continue;
826
893
  sourceResults.push(
827
- queryRenderedFeatures(this.sourceCaches[id], this._layers, queryGeometry.viewport, params, transform)
894
+ queryRenderedFeatures(this._sources[id], this._layers, queryGeometry.viewport, params, transform)
828
895
  );
829
896
  }
830
897
 
@@ -834,7 +901,7 @@ class Style extends Evented {
834
901
  sourceResults.push(
835
902
  queryRenderedSymbols(
836
903
  this._layers,
837
- this.sourceCaches,
904
+ this._sources,
838
905
  queryGeometry.viewport,
839
906
  params,
840
907
  this.placement.collisionIndex,
@@ -846,18 +913,18 @@ class Style extends Evented {
846
913
  }
847
914
 
848
915
  querySourceFeatures(sourceID, params) {
849
- const sourceCache = this.sourceCaches[sourceID];
916
+ const sourceCache = this._sources[sourceID];
850
917
  return sourceCache ? querySourceFeatures(sourceCache, params) : [];
851
918
  }
852
919
 
853
920
  getLight() {
854
- return this.light.getLight();
921
+ return this._light.getLight();
855
922
  }
856
923
 
857
924
  setLight(lightOptions) {
858
925
  this._checkLoaded();
859
926
 
860
- const light = this.light.getLight();
927
+ const light = this._light.getLight();
861
928
  let _update = false;
862
929
  for (const key in lightOptions) {
863
930
  if (!deepEqual(lightOptions[key], light[key])) {
@@ -878,35 +945,40 @@ class Style extends Evented {
878
945
  )
879
946
  };
880
947
 
881
- this.light.setLight(lightOptions);
882
- this.light.updateTransitions(parameters);
948
+ this._light.setLight(lightOptions);
949
+ this._light.updateTransitions(parameters);
883
950
  }
884
951
 
885
952
  _remove() {
886
- rtlTextPluginEvented.off('pluginAvailable', this._rtlTextPluginCallback);
887
- for (const id in this.sourceCaches) {
888
- this.sourceCaches[id].clearTiles();
953
+ this._rtlTextPluginCallbackUnregister?.();
954
+ for (const id in this._sources) {
955
+ this._sources[id].clearTiles();
889
956
  }
890
- this.dispatcher.remove();
891
957
  }
892
958
 
893
959
  _clearSource(id) {
894
- this.sourceCaches[id].clearTiles();
960
+ this._sources[id].clearTiles();
895
961
  }
896
962
 
897
963
  _reloadSource(id) {
898
- this.sourceCaches[id].resume();
899
- this.sourceCaches[id].reload();
964
+ this._sources[id].resume();
965
+ this._sources[id].reload();
900
966
  }
901
967
 
902
968
  _updateSources(transform) {
903
- for (const id in this.sourceCaches) {
904
- 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
905
977
  }
906
978
  }
907
979
 
908
980
  _generateCollisionBoxes() {
909
- for (const id in this.sourceCaches) {
981
+ for (const id in this._sources) {
910
982
  this._reloadSource(id);
911
983
  }
912
984
  }
@@ -917,26 +989,25 @@ class Style extends Evented {
917
989
 
918
990
  const layerTiles = {};
919
991
 
920
- for (const layerID of this._order) {
921
- const styleLayer = this._layers[layerID];
922
- if (styleLayer.type !== 'symbol') continue;
992
+ for (const layer of this._layers.values()) {
993
+ if (layer.type !== 'symbol') continue;
923
994
 
924
- if (!layerTiles[styleLayer.source]) {
925
- const sourceCache = this.sourceCaches[styleLayer.source];
926
- layerTiles[styleLayer.source] = sourceCache
995
+ if (!layerTiles[layer.source]) {
996
+ const sourceCache = this._sources[layer.source];
997
+ layerTiles[layer.source] = sourceCache
927
998
  .getRenderableIds(true)
928
999
  .map(id => sourceCache.getTileByID(id))
929
1000
  .sort((a, b) => b.tileID.overscaledZ - a.tileID.overscaledZ || (a.tileID.isLessThan(b.tileID) ? -1 : 1));
930
1001
  }
931
1002
 
932
1003
  const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(
933
- styleLayer,
934
- layerTiles[styleLayer.source],
1004
+ layer,
1005
+ layerTiles[layer.source],
935
1006
  transform.center.lng
936
1007
  );
937
1008
  symbolBucketsChanged = symbolBucketsChanged || layerBucketsChanged;
938
1009
  }
939
- this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
1010
+ this.crossTileSymbolIndex.pruneUnusedLayers(this._layers.keys());
940
1011
 
941
1012
  // Anything that changes our "in progress" layer and tile indices requires us
942
1013
  // to start over. When we start over, we do a full placement instead of incremental
@@ -951,7 +1022,7 @@ class Style extends Evented {
951
1022
  ) {
952
1023
  this.pauseablePlacement = new PauseablePlacement(
953
1024
  transform,
954
- this._order,
1025
+ this._layers.size - 1,
955
1026
  forceFullPlacement,
956
1027
  showCollisionBoxes,
957
1028
  fadeDuration,
@@ -967,7 +1038,7 @@ class Style extends Evented {
967
1038
  // render frame
968
1039
  this.placement.setStale();
969
1040
  } else {
970
- this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
1041
+ this.pauseablePlacement.continuePlacement(Array.from(this._layers.values()), layerTiles);
971
1042
 
972
1043
  if (this.pauseablePlacement.isDone()) {
973
1044
  this.placement = this.pauseablePlacement.commit(this.placement, browser.now());
@@ -983,10 +1054,9 @@ class Style extends Evented {
983
1054
  }
984
1055
 
985
1056
  if (placementCommitted || symbolBucketsChanged) {
986
- for (const layerID of this._order) {
987
- const styleLayer = this._layers[layerID];
988
- if (styleLayer.type !== 'symbol') continue;
989
- 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]);
990
1060
  }
991
1061
  }
992
1062
 
@@ -996,24 +1066,22 @@ class Style extends Evented {
996
1066
  }
997
1067
 
998
1068
  _releaseSymbolFadeTiles() {
999
- for (const id in this.sourceCaches) {
1000
- this.sourceCaches[id].releaseSymbolFadeTiles();
1069
+ for (const id in this._sources) {
1070
+ this._sources[id].releaseSymbolFadeTiles();
1001
1071
  }
1002
1072
  }
1003
1073
 
1004
1074
  // Callbacks from web workers
1005
-
1006
- getImages(_mapId, { icons }) {
1075
+ getImages({ icons }) {
1007
1076
  return this.imageManager.getImages(icons);
1008
1077
  }
1009
1078
 
1010
- loadGlyphRange(_mapId, { stack, range }) {
1079
+ loadGlyphRange({ stack, range }) {
1011
1080
  return this.glyphManager.loadGlyphRange(stack, range);
1012
1081
  }
1013
1082
  }
1014
1083
 
1015
1084
  Style.getSourceType = getSourceType;
1016
1085
  Style.setSourceType = setSourceType;
1017
- Style.registerForPluginAvailability = registerForPluginAvailability;
1018
1086
 
1019
1087
  module.exports = Style;