@mapwhit/tilerenderer 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/build/min/package.json +1 -1
  2. package/package.json +1 -1
  3. package/src/data/array_types.js +115 -64
  4. package/src/data/bucket/circle_bucket.js +42 -5
  5. package/src/data/bucket/fill_bucket.js +31 -13
  6. package/src/data/bucket/fill_extrusion_bucket.js +8 -6
  7. package/src/data/bucket/line_bucket.js +38 -14
  8. package/src/data/bucket/symbol_attributes.js +13 -5
  9. package/src/data/bucket/symbol_bucket.js +87 -33
  10. package/src/data/bucket/symbol_collision_buffers.js +1 -1
  11. package/src/data/bucket.js +3 -1
  12. package/src/data/feature_index.js +24 -11
  13. package/src/data/segment.js +15 -7
  14. package/src/render/draw_circle.js +45 -4
  15. package/src/render/draw_symbol.js +190 -22
  16. package/src/render/painter.js +1 -1
  17. package/src/source/geojson_source.js +118 -21
  18. package/src/source/geojson_source_diff.js +148 -0
  19. package/src/source/geojson_tiler.js +89 -0
  20. package/src/source/source.js +16 -5
  21. package/src/source/source_cache.js +6 -6
  22. package/src/source/source_state.js +4 -2
  23. package/src/source/tile.js +5 -3
  24. package/src/source/vector_tile_source.js +2 -0
  25. package/src/source/worker_tile.js +4 -2
  26. package/src/style/pauseable_placement.js +39 -7
  27. package/src/style/style.js +86 -34
  28. package/src/style/style_layer/circle_style_layer_properties.js +8 -1
  29. package/src/style/style_layer/fill_style_layer_properties.js +8 -1
  30. package/src/style/style_layer/line_style_layer_properties.js +4 -0
  31. package/src/style/style_layer/symbol_style_layer_properties.js +17 -2
  32. package/src/style-spec/reference/v8.json +161 -4
  33. package/src/symbol/one_em.js +4 -0
  34. package/src/symbol/placement.js +406 -173
  35. package/src/symbol/projection.js +3 -3
  36. package/src/symbol/quads.js +1 -6
  37. package/src/symbol/shaping.js +16 -27
  38. package/src/symbol/symbol_layout.js +243 -81
  39. package/src/util/vectortile_to_geojson.js +3 -4
  40. package/src/source/geojson_worker_source.js +0 -97
@@ -0,0 +1,89 @@
1
+ import rewind from '@mapwhit/geojson-rewind';
2
+ import geojsonvt from 'geojson-vt';
3
+ import Supercluster from 'supercluster';
4
+ import FeatureIndex from '../data/feature_index.js';
5
+ import GeoJSONWrapper from './geojson_wrapper.js';
6
+ import { makeSingleSourceLayerWorkerTile as makeWorkerTile } from './worker_tile.js';
7
+
8
+ /**
9
+ * Creates tiles from GeoJSON data.
10
+ */
11
+ export default function makeTiler(resources, layerIndex) {
12
+ let geoJSONIndex;
13
+ let createGeoJSONIndex;
14
+
15
+ /**
16
+ * Fetches (if appropriate), parses, and index geojson data into tiles. This
17
+ * preparatory method must be called before {@link GeoJSONWorkerSource#loadTile}
18
+ * can correctly serve up tiles.
19
+ *
20
+ * Defers to {@link GeoJSONWorkerSource#loadGeoJSON} for the fetching/parsing,
21
+ * expecting `callback(error, data)` to be called with either an error or a
22
+ * parsed GeoJSON object.
23
+ *
24
+ * @param params
25
+ */
26
+ function loadData(params) {
27
+ const { cluster, data, geojsonVtOptions, superclusterOptions } = params;
28
+ geoJSONIndex = undefined;
29
+ createGeoJSONIndex = cluster
30
+ ? () => {
31
+ rewind(data, true);
32
+ return new Supercluster(superclusterOptions).load(data.features);
33
+ }
34
+ : () => {
35
+ rewind(data, true);
36
+ return geojsonvt(data, geojsonVtOptions);
37
+ };
38
+ }
39
+
40
+ function getTile(tileID) {
41
+ if (!geoJSONIndex) {
42
+ if (!createGeoJSONIndex) {
43
+ return; // we couldn't load the file
44
+ }
45
+
46
+ try {
47
+ geoJSONIndex = createGeoJSONIndex();
48
+ } finally {
49
+ createGeoJSONIndex = undefined;
50
+ }
51
+ }
52
+ const { z, x, y } = tileID.canonical;
53
+ return geoJSONIndex.getTile(z, x, y);
54
+ }
55
+
56
+ /**
57
+ * Returns a single tile.
58
+ */
59
+ async function loadTile(params) {
60
+ const { tileID, source, promoteId } = params;
61
+ const geoJSONTile = getTile(tileID);
62
+ if (!geoJSONTile) {
63
+ return; // nothing in the given tile
64
+ }
65
+ const sourceLayer = new GeoJSONWrapper(geoJSONTile.features);
66
+ const layerFamilies = layerIndex.familiesBySource.get(source);
67
+ if (!layerFamilies) {
68
+ return;
69
+ }
70
+ params.featureIndex = new FeatureIndex(tileID, promoteId);
71
+ const { name } = sourceLayer;
72
+ const features = new Array(sourceLayer.length);
73
+ for (let index = 0; index < sourceLayer.length; index++) {
74
+ const feature = sourceLayer.feature(index);
75
+ const id = params.featureIndex.getId(feature, name);
76
+ features[index] = { feature, id, index, sourceLayerIndex: 0 };
77
+ }
78
+
79
+ const result = await makeWorkerTile(params, features, layerFamilies.get(name), resources);
80
+
81
+ result.vectorTile = sourceLayer;
82
+ return result;
83
+ }
84
+
85
+ return {
86
+ loadData,
87
+ loadTile
88
+ };
89
+ }
@@ -25,6 +25,7 @@ import { bindAll } from '../util/object.js';
25
25
  */
26
26
 
27
27
  import geojson from './geojson_source.js';
28
+ import geojsonTiler from './geojson_tiler.js';
28
29
  import image from './image_source.js';
29
30
  import rasterDem from './raster_dem_tile_source.js';
30
31
  import raster from './raster_tile_source.js';
@@ -38,6 +39,9 @@ const sourceTypes = {
38
39
  image
39
40
  };
40
41
 
42
+ const tilerTypes = {
43
+ geojson: geojsonTiler
44
+ };
41
45
  /*
42
46
  * Creates a tiled data source instance given an options object.
43
47
  *
@@ -48,11 +52,18 @@ const sourceTypes = {
48
52
  * @returns {Source}
49
53
  */
50
54
  export function create(id, specification, eventedParent, { resources, layerIndex, showTileBoundaries }) {
51
- const source = new sourceTypes[specification.type](id, specification, eventedParent, {
52
- resources,
53
- layerIndex,
54
- showTileBoundaries
55
- });
55
+ const tiler = tilerTypes[specification.type]?.(resources, layerIndex);
56
+
57
+ const source = new sourceTypes[specification.type](
58
+ id,
59
+ specification,
60
+ eventedParent,
61
+ tiler ?? {
62
+ resources,
63
+ layerIndex,
64
+ showTileBoundaries
65
+ }
66
+ );
56
67
 
57
68
  bindAll(['load', 'abort', 'unload', 'serialize', 'prepare'], source);
58
69
  return source;
@@ -799,27 +799,27 @@ class SourceCache extends Evented {
799
799
  * Set the value of a particular state for a feature
800
800
  * @private
801
801
  */
802
- setFeatureState(sourceLayer, feature, state) {
802
+ setFeatureState(sourceLayer, featureId, state) {
803
803
  sourceLayer = sourceLayer || '_geojsonTileLayer';
804
- this._state.updateState(sourceLayer, feature, state);
804
+ this._state.updateState(sourceLayer, featureId, state);
805
805
  }
806
806
 
807
807
  /**
808
808
  * Resets the value of a particular state key for a feature
809
809
  * @private
810
810
  */
811
- removeFeatureState(sourceLayer, feature, key) {
811
+ removeFeatureState(sourceLayer, featureId, key) {
812
812
  sourceLayer = sourceLayer || '_geojsonTileLayer';
813
- this._state.removeFeatureState(sourceLayer, feature, key);
813
+ this._state.removeFeatureState(sourceLayer, featureId, key);
814
814
  }
815
815
 
816
816
  /**
817
817
  * Get the entire state object for a feature
818
818
  * @private
819
819
  */
820
- getFeatureState(sourceLayer, feature) {
820
+ getFeatureState(sourceLayer, featureId) {
821
821
  sourceLayer = sourceLayer || '_geojsonTileLayer';
822
- return this._state.getState(sourceLayer, feature);
822
+ return this._state.getState(sourceLayer, featureId);
823
823
  }
824
824
  }
825
825
 
@@ -13,7 +13,8 @@ class SourceFeatureState {
13
13
  #stateChanges = {};
14
14
  #deletedStates = {};
15
15
 
16
- updateState(sourceLayer, feature, newState) {
16
+ updateState(sourceLayer, featureId, newState) {
17
+ const feature = String(featureId);
17
18
  const changes = (this.#stateChanges[sourceLayer] ??= {});
18
19
  const featureState = (changes[feature] ??= {});
19
20
  Object.assign(featureState, newState);
@@ -76,7 +77,8 @@ class SourceFeatureState {
76
77
  }
77
78
  }
78
79
 
79
- getState(sourceLayer, feature) {
80
+ getState(sourceLayer, featureId) {
81
+ const feature = String(featureId);
80
82
  const base = this.#state[sourceLayer];
81
83
  const changes = this.#stateChanges[sourceLayer];
82
84
  const reconciledState = Object.assign({}, base?.[feature], changes?.[feature]);
@@ -191,11 +191,12 @@ class Tile {
191
191
  }
192
192
 
193
193
  querySourceFeatures(result, params) {
194
- if (!this.latestFeatureIndex?.vectorTile) {
194
+ const featureIndex = this.latestFeatureIndex;
195
+ if (!featureIndex?.vectorTile) {
195
196
  return;
196
197
  }
197
198
 
198
- const vtLayers = this.latestFeatureIndex.loadVTLayers();
199
+ const vtLayers = featureIndex.loadVTLayers();
199
200
 
200
201
  const sourceLayer = params ? params.sourceLayer : '';
201
202
  const layer = vtLayers._geojsonTileLayer || vtLayers[sourceLayer];
@@ -211,7 +212,8 @@ class Tile {
211
212
  for (let i = 0; i < layer.length; i++) {
212
213
  const feature = layer.feature(i);
213
214
  if (filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) {
214
- const geojsonFeature = new GeoJSONFeature(feature, z, x, y);
215
+ const id = featureIndex.getId(feature, sourceLayer);
216
+ const geojsonFeature = new GeoJSONFeature(feature, z, x, y, id);
215
217
  geojsonFeature.tile = coord;
216
218
  result.push(geojsonFeature);
217
219
  }
@@ -22,6 +22,7 @@ class VectorTileSource extends Evented {
22
22
  this.url = options.url;
23
23
  this.scheme = options.scheme ?? 'xyz';
24
24
  this.tileSize = options.tileSize ?? 512;
25
+ this.promoteId = options.promoteId;
25
26
 
26
27
  if (this.tileSize !== 512) {
27
28
  throw new Error('vector tile sources must have a tileSize of 512');
@@ -93,6 +94,7 @@ class VectorTileSource extends Evented {
93
94
  source: this.id,
94
95
  pixelRatio: browser.devicePixelRatio,
95
96
  showCollisionBoxes: this.map.showCollisionBoxes,
97
+ promoteId: this.promoteId,
96
98
  painter: this.map.painter
97
99
  };
98
100
  tile.workerID ??= true;
@@ -27,7 +27,9 @@ async function makeWorkerTile(params, { layers }, layerIndex, resources) {
27
27
  const sourceLayerIndex = sourceLayerCoder.encode(sourceLayerId);
28
28
  const features = new Array(sourceLayer.length);
29
29
  for (let index = 0; index < sourceLayer.length; index++) {
30
- features[index] = { feature: sourceLayer.feature(index), index, sourceLayerIndex };
30
+ const feature = sourceLayer.feature(index);
31
+ const id = options.featureIndex.getId(feature, sourceLayerId);
32
+ features[index] = { feature, id, index, sourceLayerIndex };
31
33
  }
32
34
 
33
35
  makeBucketsForSourceLayer(sourceLayerFamilies, sourceLayerIndex, features, params, options);
@@ -47,7 +49,7 @@ export async function makeSingleSourceLayerWorkerTile(params, features, sourceLa
47
49
  function initializeBucketsOptions(params) {
48
50
  const tileID = createTileID(params);
49
51
 
50
- const featureIndex = new FeatureIndex(tileID);
52
+ const featureIndex = params.featureIndex ?? new FeatureIndex(tileID, params.promoteId);
51
53
  featureIndex.bucketLayerIDs = [];
52
54
 
53
55
  return {
@@ -2,27 +2,59 @@ import { Placement } from '../symbol/placement.js';
2
2
  import browser from '../util/browser.js';
3
3
 
4
4
  class LayerPlacement {
5
- constructor() {
5
+ constructor(styleLayer) {
6
+ this._sortAcrossTiles =
7
+ styleLayer._layout.get('symbol-z-order') !== 'viewport-y' &&
8
+ styleLayer._layout.get('symbol-sort-key').constantOr(1) !== undefined;
9
+
6
10
  this._currentTileIndex = 0;
11
+ this._currentPartIndex = 0;
7
12
  this._seenCrossTileIDs = {};
13
+ this._bucketParts = [];
8
14
  }
9
15
 
10
16
  continuePlacement(tiles, placement, showCollisionBoxes, styleLayer, shouldPausePlacement) {
17
+ const bucketParts = this._bucketParts;
18
+
11
19
  while (this._currentTileIndex < tiles.length) {
12
20
  const tile = tiles[this._currentTileIndex];
13
- placement.placeLayerTile(styleLayer, tile, showCollisionBoxes, this._seenCrossTileIDs);
21
+ placement.getBucketParts(bucketParts, styleLayer, tile, this._sortAcrossTiles);
14
22
 
15
23
  this._currentTileIndex++;
16
24
  if (shouldPausePlacement()) {
17
25
  return true;
18
26
  }
19
27
  }
28
+
29
+ if (this._sortAcrossTiles) {
30
+ this._sortAcrossTiles = false;
31
+ bucketParts.sort((a, b) => a.sortKey - b.sortKey);
32
+ }
33
+
34
+ while (this._currentPartIndex < bucketParts.length) {
35
+ const bucketPart = bucketParts[this._currentPartIndex];
36
+ placement.placeLayerBucketPart(bucketPart, this._seenCrossTileIDs, showCollisionBoxes);
37
+
38
+ this._currentPartIndex++;
39
+ if (shouldPausePlacement()) {
40
+ return true;
41
+ }
42
+ }
43
+ return false;
20
44
  }
21
45
  }
22
46
 
23
47
  class PauseablePlacement {
24
- constructor(transform, maxIndex, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions) {
25
- this.placement = new Placement(transform, fadeDuration, crossSourceCollisions);
48
+ constructor(
49
+ transform,
50
+ maxIndex,
51
+ forceFullPlacement,
52
+ showCollisionBoxes,
53
+ fadeDuration,
54
+ crossSourceCollisions,
55
+ prevPlacement
56
+ ) {
57
+ this.placement = new Placement(transform, fadeDuration, crossSourceCollisions, prevPlacement);
26
58
  this._currentPlacementIndex = maxIndex;
27
59
  this._forceFullPlacement = forceFullPlacement;
28
60
  this._showCollisionBoxes = showCollisionBoxes;
@@ -50,7 +82,7 @@ class PauseablePlacement {
50
82
  (!layer.maxzoom || layer.maxzoom > placementZoom)
51
83
  ) {
52
84
  if (!this._inProgressLayer) {
53
- this._inProgressLayer = new LayerPlacement();
85
+ this._inProgressLayer = new LayerPlacement(layer);
54
86
  }
55
87
 
56
88
  const pausePlacement = this._inProgressLayer.continuePlacement(
@@ -77,8 +109,8 @@ class PauseablePlacement {
77
109
  this._done = true;
78
110
  }
79
111
 
80
- commit(previousPlacement, now) {
81
- this.placement.commit(previousPlacement, now);
112
+ commit(now) {
113
+ this.placement.commit(now);
82
114
  return this.placement;
83
115
  }
84
116
  }
@@ -41,6 +41,7 @@ class Style extends Evented {
41
41
  loadGlyphRange: this.loadGlyphRange.bind(this)
42
42
  });
43
43
  #layerIndex = new StyleLayerIndex();
44
+ #opsQueue = [];
44
45
 
45
46
  constructor(map, options = {}) {
46
47
  super();
@@ -89,7 +90,10 @@ class Style extends Evented {
89
90
  }
90
91
 
91
92
  setGlobalStateProperty(name, value) {
92
- this._checkLoaded();
93
+ if (!this._loaded) {
94
+ this.#opsQueue.push(() => this.setGlobalStateProperty(name, value));
95
+ return;
96
+ }
93
97
 
94
98
  const newValue = value === null ? (this.stylesheet.state?.[name]?.default ?? null) : value;
95
99
 
@@ -107,7 +111,10 @@ class Style extends Evented {
107
111
  }
108
112
 
109
113
  setGlobalState(newStylesheetState) {
110
- this._checkLoaded();
114
+ if (!this._loaded) {
115
+ this.#opsQueue.push(() => this.setGlobalState(newStylesheetState));
116
+ return;
117
+ }
111
118
 
112
119
  const changedGlobalStateRefs = [];
113
120
 
@@ -225,6 +232,9 @@ class Style extends Evented {
225
232
  this.light = this.stylesheet.light;
226
233
  this._light = new Light(this.light);
227
234
 
235
+ this.#opsQueue.forEach(op => op());
236
+ this.#opsQueue = [];
237
+
228
238
  this.fire(new Event('data', { dataType: 'style' }));
229
239
  this.fire(new Event('style.load'));
230
240
  }
@@ -301,12 +311,6 @@ class Style extends Evented {
301
311
  return false;
302
312
  }
303
313
 
304
- _checkLoaded() {
305
- if (!this._loaded) {
306
- throw new Error('Style is not done loading');
307
- }
308
- }
309
-
310
314
  /**
311
315
  * Apply queued style updates in a batch and recalculate zoom-dependent paint properties.
312
316
  */
@@ -390,13 +394,18 @@ class Style extends Evented {
390
394
  }
391
395
 
392
396
  listImages() {
393
- this._checkLoaded();
397
+ if (!this._loaded) {
398
+ return;
399
+ }
394
400
 
395
401
  return this.imageManager.listImages();
396
402
  }
397
403
 
398
404
  addSource(id, source) {
399
- this._checkLoaded();
405
+ if (!this._loaded) {
406
+ this.#opsQueue.push(() => this.addSource(id, source));
407
+ return;
408
+ }
400
409
 
401
410
  if (this._sources[id] !== undefined) {
402
411
  throw new Error('There is already a source with this ID');
@@ -432,7 +441,10 @@ class Style extends Evented {
432
441
  * @throws {Error} if no source is found with the given ID
433
442
  */
434
443
  removeSource(id) {
435
- this._checkLoaded();
444
+ if (!this._loaded) {
445
+ this.#opsQueue.push(() => this.removeSource(id));
446
+ return;
447
+ }
436
448
 
437
449
  if (this._sources[id] === undefined) {
438
450
  throw new Error('There is no source with this ID');
@@ -464,7 +476,10 @@ class Style extends Evented {
464
476
  * @param {GeoJSON|string} data GeoJSON source
465
477
  */
466
478
  setGeoJSONSourceData(id, data) {
467
- this._checkLoaded();
479
+ if (!this._loaded) {
480
+ this.#opsQueue.push(() => this.setGeoJSONSourceData(id, data));
481
+ return;
482
+ }
468
483
 
469
484
  assert(this._sources[id] !== undefined, 'There is no source with this ID');
470
485
  const geojsonSource = this._sources[id].getSource();
@@ -519,7 +534,10 @@ class Style extends Evented {
519
534
  * @param {string} [before] ID of an existing layer to insert before
520
535
  */
521
536
  addLayer(layerObject, before) {
522
- this._checkLoaded();
537
+ if (!this._loaded) {
538
+ this.#opsQueue.push(() => this.addLayer(layerObject, before));
539
+ return;
540
+ }
523
541
 
524
542
  const id = layerObject.id;
525
543
 
@@ -568,7 +586,10 @@ class Style extends Evented {
568
586
  * @param {string} [before] ID of an existing layer to insert before
569
587
  */
570
588
  moveLayer(id, before) {
571
- this._checkLoaded();
589
+ if (!this._loaded) {
590
+ this.#opsQueue.push(() => this.moveLayer(id, before));
591
+ return;
592
+ }
572
593
  this._changed = true;
573
594
 
574
595
  const layer = this._layers.get(id);
@@ -593,7 +614,10 @@ class Style extends Evented {
593
614
  * @fires error
594
615
  */
595
616
  removeLayer(id) {
596
- this._checkLoaded();
617
+ if (!this._loaded) {
618
+ this.#opsQueue.push(() => this.removeLayer(id));
619
+ return;
620
+ }
597
621
 
598
622
  const layer = this._layers.get(id);
599
623
  if (!layer) {
@@ -624,7 +648,10 @@ class Style extends Evented {
624
648
  }
625
649
 
626
650
  setLayerZoomRange(layerId, minzoom, maxzoom) {
627
- this._checkLoaded();
651
+ if (!this._loaded) {
652
+ this.#opsQueue.push(() => this.setLayerZoomRange(layerId, minzoom, maxzoom));
653
+ return;
654
+ }
628
655
 
629
656
  const layer = this.getLayer(layerId);
630
657
  if (!layer) {
@@ -646,7 +673,10 @@ class Style extends Evented {
646
673
  }
647
674
 
648
675
  setFilter(layerId, filter) {
649
- this._checkLoaded();
676
+ if (!this._loaded) {
677
+ this.#opsQueue.push(() => this.setFilter(layerId, filter));
678
+ return;
679
+ }
650
680
 
651
681
  const layer = this.getLayer(layerId);
652
682
  if (!layer) {
@@ -680,7 +710,10 @@ class Style extends Evented {
680
710
  }
681
711
 
682
712
  setLayoutProperty(layerId, name, value) {
683
- this._checkLoaded();
713
+ if (!this._loaded) {
714
+ this.#opsQueue.push(() => this.setLayoutProperty(layerId, name, value));
715
+ return;
716
+ }
684
717
 
685
718
  const layer = this.getLayer(layerId);
686
719
  if (!layer) {
@@ -713,7 +746,10 @@ class Style extends Evented {
713
746
  }
714
747
 
715
748
  setPaintProperty(layerId, name, value) {
716
- this._checkLoaded();
749
+ if (!this._loaded) {
750
+ this.#opsQueue.push(() => this.setPaintProperty(layerId, name, value));
751
+ return;
752
+ }
717
753
 
718
754
  const layer = this.getLayer(layerId);
719
755
  if (!layer) {
@@ -748,10 +784,13 @@ class Style extends Evented {
748
784
  return this.getLayer(layer).getPaintProperty(name);
749
785
  }
750
786
 
751
- setFeatureState(feature, state) {
752
- this._checkLoaded();
753
- const sourceId = feature.source;
754
- const sourceLayer = feature.sourceLayer;
787
+ setFeatureState(target, state) {
788
+ if (!this._loaded) {
789
+ this.#opsQueue.push(() => this.setFeatureState(target, state));
790
+ return;
791
+ }
792
+ const sourceId = target.source;
793
+ const sourceLayer = target.sourceLayer;
755
794
  const sourceCache = this._sources[sourceId];
756
795
 
757
796
  if (sourceCache === undefined) {
@@ -767,16 +806,19 @@ class Style extends Evented {
767
806
  this.fire(new ErrorEvent(new Error('The sourceLayer parameter must be provided for vector source types.')));
768
807
  return;
769
808
  }
770
- if (feature.id == null || feature.id === '') {
809
+ if (target.id === undefined) {
771
810
  this.fire(new ErrorEvent(new Error('The feature id parameter must be provided.')));
772
811
  return;
773
812
  }
774
813
 
775
- sourceCache.setFeatureState(sourceLayer, feature.id, state);
814
+ sourceCache.setFeatureState(sourceLayer, target.id, state);
776
815
  }
777
816
 
778
817
  removeFeatureState(target, key) {
779
- this._checkLoaded();
818
+ if (!this._loaded) {
819
+ this.#opsQueue.push(() => this.removeFeatureState(target, key));
820
+ return;
821
+ }
780
822
  const sourceId = target.source;
781
823
  const sourceCache = this._sources[sourceId];
782
824
 
@@ -801,10 +843,12 @@ class Style extends Evented {
801
843
  sourceCache.removeFeatureState(sourceLayer, target.id, key);
802
844
  }
803
845
 
804
- getFeatureState(feature) {
805
- this._checkLoaded();
806
- const sourceId = feature.source;
807
- const sourceLayer = feature.sourceLayer;
846
+ getFeatureState(target) {
847
+ if (!this._loaded) {
848
+ return;
849
+ }
850
+ const sourceId = target.source;
851
+ const sourceLayer = target.sourceLayer;
808
852
  const sourceCache = this._sources[sourceId];
809
853
 
810
854
  if (sourceCache === undefined) {
@@ -817,7 +861,11 @@ class Style extends Evented {
817
861
  return;
818
862
  }
819
863
 
820
- return sourceCache.getFeatureState(sourceLayer, feature.id);
864
+ if (target.id === undefined) {
865
+ this.fire(new ErrorEvent(new Error('The feature id parameter must be provided.')));
866
+ }
867
+
868
+ return sourceCache.getFeatureState(sourceLayer, target.id);
821
869
  }
822
870
 
823
871
  getTransition() {
@@ -962,7 +1010,10 @@ class Style extends Evented {
962
1010
  }
963
1011
 
964
1012
  setLight(lightOptions) {
965
- this._checkLoaded();
1013
+ if (!this._loaded) {
1014
+ this.#opsQueue.push(() => this.setLight(lightOptions));
1015
+ return;
1016
+ }
966
1017
 
967
1018
  const light = this._light.getLight();
968
1019
  let _update = false;
@@ -1070,7 +1121,8 @@ class Style extends Evented {
1070
1121
  forceFullPlacement,
1071
1122
  showCollisionBoxes,
1072
1123
  fadeDuration,
1073
- crossSourceCollisions
1124
+ crossSourceCollisions,
1125
+ this.placement
1074
1126
  );
1075
1127
  this._layerOrderChanged = false;
1076
1128
  }
@@ -1085,7 +1137,7 @@ class Style extends Evented {
1085
1137
  this.pauseablePlacement.continuePlacement(Array.from(this._layers.values()), layerTiles);
1086
1138
 
1087
1139
  if (this.pauseablePlacement.isDone()) {
1088
- this.placement = this.pauseablePlacement.commit(this.placement, browser.now());
1140
+ this.placement = this.pauseablePlacement.commit(browser.now());
1089
1141
  placementCommitted = true;
1090
1142
  }
1091
1143
 
@@ -2,6 +2,13 @@
2
2
 
3
3
  import { DataConstantProperty, DataDrivenProperty, Properties } from '../properties.js';
4
4
 
5
+ const layout = new Properties({
6
+ 'circle-sort-key': new DataDrivenProperty({
7
+ type: 'number',
8
+ expression: { parameters: ['zoom', 'feature'] }
9
+ })
10
+ });
11
+
5
12
  const paint = new Properties({
6
13
  'circle-radius': new DataDrivenProperty({
7
14
  type: 'number',
@@ -73,4 +80,4 @@ const paint = new Properties({
73
80
  })
74
81
  });
75
82
 
76
- export default { paint };
83
+ export default { paint, layout };
@@ -2,6 +2,13 @@
2
2
 
3
3
  import { CrossFadedDataDrivenProperty, DataConstantProperty, DataDrivenProperty, Properties } from '../properties.js';
4
4
 
5
+ const layout = new Properties({
6
+ 'fill-sort-key': new DataDrivenProperty({
7
+ type: 'number',
8
+ expression: { parameters: ['zoom', 'feature'] }
9
+ })
10
+ });
11
+
5
12
  const paint = new Properties({
6
13
  'fill-antialias': new DataConstantProperty({ type: 'boolean', default: true, expression: { parameters: ['zoom'] } }),
7
14
  'fill-opacity': new DataDrivenProperty({
@@ -42,4 +49,4 @@ const paint = new Properties({
42
49
  })
43
50
  });
44
51
 
45
- export default { paint };
52
+ export default { paint, layout };
@@ -31,6 +31,10 @@ const layout = new Properties({
31
31
  type: 'number',
32
32
  default: 1.05,
33
33
  expression: { interpolated: true, parameters: ['zoom'] }
34
+ }),
35
+ 'line-sort-key': new DataDrivenProperty({
36
+ type: 'number',
37
+ expression: { parameters: ['zoom', 'feature'] }
34
38
  })
35
39
  });
36
40
  const paint = new Properties({