@mapwhit/tilerenderer 1.1.0 → 1.2.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 (45) hide show
  1. package/build/min/package.json +1 -1
  2. package/package.json +5 -5
  3. package/src/data/bucket/circle_bucket.js +3 -8
  4. package/src/data/bucket/fill_bucket.js +4 -17
  5. package/src/data/bucket/fill_extrusion_bucket.js +4 -15
  6. package/src/data/bucket/line_bucket.js +4 -17
  7. package/src/data/bucket/pattern_bucket_features.js +4 -4
  8. package/src/data/bucket/symbol_bucket.js +4 -8
  9. package/src/data/feature_index.js +1 -1
  10. package/src/source/geojson_source.js +4 -3
  11. package/src/source/geojson_worker_source.js +26 -10
  12. package/src/source/load_tilejson.js +18 -6
  13. package/src/source/raster_tile_source.js +4 -5
  14. package/src/source/source_cache.js +9 -2
  15. package/src/source/tile.js +3 -0
  16. package/src/source/vector_tile_source.js +4 -6
  17. package/src/source/vector_tile_worker_source.js +6 -26
  18. package/src/source/worker_tile.js +67 -53
  19. package/src/style/create_style_layer.js +2 -2
  20. package/src/style/evaluation_parameters.js +0 -2
  21. package/src/style/properties.js +24 -15
  22. package/src/style/style.js +25 -10
  23. package/src/style/style_layer/background_style_layer.js +2 -2
  24. package/src/style/style_layer/circle_style_layer.js +3 -3
  25. package/src/style/style_layer/fill_extrusion_style_layer.js +3 -3
  26. package/src/style/style_layer/fill_style_layer.js +3 -3
  27. package/src/style/style_layer/heatmap_style_layer.js +3 -3
  28. package/src/style/style_layer/hillshade_style_layer.js +3 -3
  29. package/src/style/style_layer/line_style_layer.js +3 -3
  30. package/src/style/style_layer/raster_style_layer.js +2 -2
  31. package/src/style/style_layer/symbol_style_layer.js +2 -2
  32. package/src/style/style_layer.js +28 -7
  33. package/src/style-spec/feature_filter/index.js +7 -5
  34. package/src/style-spec/reference/v8.json +68 -9
  35. package/src/style-spec/visibility.js +37 -0
  36. package/src/symbol/collision_index.js +2 -2
  37. package/src/symbol/symbol_layout.js +1 -1
  38. package/src/ui/map.js +11 -4
  39. package/src/util/classify_rings.js +1 -1
  40. package/src/util/group_layers.js +1 -1
  41. package/src/util/object.js +0 -45
  42. package/src/util/util.js +0 -55
  43. package/src/util/async.js +0 -29
  44. package/src/util/find_pole_of_inaccessibility.js +0 -144
  45. package/src/util/intersection_tests.js +0 -245
@@ -13,72 +13,86 @@ import { mapObject } from '../util/object.js';
13
13
  import { OverscaledTileID } from './tile_id.js';
14
14
  export default makeWorkerTile;
15
15
 
16
- async function makeWorkerTile(params, vectorTile, layerIndex, resources) {
17
- const tileID = createTileID(params);
18
-
19
- const overscaling = tileID.overscaleFactor();
20
- const { zoom, pixelRatio, source, showCollisionBoxes, globalState, justReloaded, painter } = params;
21
-
22
- const collisionBoxArray = new CollisionBoxArray();
23
- const sourceLayerCoder = dictionaryCoder(Object.keys(vectorTile.layers));
24
-
25
- const featureIndex = new FeatureIndex(tileID);
26
- featureIndex.bucketLayerIDs = [];
27
-
28
- const uniqueBuckets = new Map();
29
-
30
- const options = {
31
- featureIndex,
32
- iconDependencies: {},
33
- patternDependencies: {},
34
- glyphDependencies: {}
35
- };
16
+ async function makeWorkerTile(params, { layers }, layerIndex, resources) {
17
+ const options = initializeBucketsOptions(params);
36
18
 
19
+ const { source } = params;
20
+ const sourceLayerCoder = dictionaryCoder(Object.keys(layers));
37
21
  const layerFamilies = layerIndex.familiesBySource.get(source) ?? new Map();
38
22
  for (const [sourceLayerId, sourceLayerFamilies] of layerFamilies) {
39
- const sourceLayer = vectorTile.layers[sourceLayerId];
23
+ const sourceLayer = layers[sourceLayerId];
40
24
  if (!sourceLayer) {
41
25
  continue;
42
26
  }
43
-
44
27
  const sourceLayerIndex = sourceLayerCoder.encode(sourceLayerId);
45
28
  const features = new Array(sourceLayer.length);
46
29
  for (let index = 0; index < sourceLayer.length; index++) {
47
30
  features[index] = { feature: sourceLayer.feature(index), index, sourceLayerIndex };
48
31
  }
49
32
 
50
- for (const layers of sourceLayerFamilies.values()) {
51
- const layer = layers[0];
33
+ makeBucketsForSourceLayer(sourceLayerFamilies, sourceLayerIndex, features, params, options);
34
+ }
52
35
 
53
- if (layer.minzoom && zoom < Math.floor(layer.minzoom)) {
54
- continue;
55
- }
56
- if (layer.maxzoom && zoom >= layer.maxzoom) {
57
- continue;
58
- }
59
- if (layer.visibility === 'none') {
60
- continue;
61
- }
36
+ return await finalizeBuckets(params, options, resources);
37
+ }
38
+
39
+ export async function makeSingleSourceLayerWorkerTile(params, features, sourceLayerFamilies, resources) {
40
+ const options = initializeBucketsOptions(params);
41
+
42
+ makeBucketsForSourceLayer(sourceLayerFamilies, 0, features, params, options);
62
43
 
63
- recalculateLayers(layers, zoom, globalState);
64
-
65
- const bucket = layer.createBucket({
66
- index: featureIndex.bucketLayerIDs.length,
67
- layers,
68
- zoom,
69
- pixelRatio,
70
- overscaling,
71
- collisionBoxArray,
72
- sourceLayerIndex,
73
- sourceID: source,
74
- globalState
75
- });
76
- uniqueBuckets.set(layer.id, bucket);
77
- bucket.populate(features, options);
78
- featureIndex.bucketLayerIDs.push(layers.map(l => l.id));
44
+ return await finalizeBuckets(params, options, resources);
45
+ }
46
+
47
+ function initializeBucketsOptions(params) {
48
+ const tileID = createTileID(params);
49
+
50
+ const featureIndex = new FeatureIndex(tileID);
51
+ featureIndex.bucketLayerIDs = [];
52
+
53
+ return {
54
+ collisionBoxArray: new CollisionBoxArray(),
55
+ featureIndex,
56
+ glyphDependencies: {},
57
+ iconDependencies: {},
58
+ overscaling: tileID.overscaleFactor(),
59
+ patternDependencies: {},
60
+ uniqueBuckets: new Map()
61
+ };
62
+ }
63
+
64
+ function makeBucketsForSourceLayer(sourceLayerFamilies, sourceLayerIndex, features, params, options) {
65
+ const { zoom, pixelRatio, source } = params;
66
+ const { featureIndex, overscaling, collisionBoxArray, uniqueBuckets } = options;
67
+
68
+ for (const layers of sourceLayerFamilies.values()) {
69
+ const layer = layers[0];
70
+
71
+ if (layer.isHidden(zoom, layer.minzoom && Math.floor(layer.minzoom))) {
72
+ continue;
79
73
  }
74
+
75
+ recalculateLayers(layers, zoom);
76
+
77
+ const bucket = layer.createBucket({
78
+ index: featureIndex.bucketLayerIDs.length,
79
+ layers,
80
+ zoom,
81
+ pixelRatio,
82
+ overscaling,
83
+ collisionBoxArray,
84
+ sourceLayerIndex,
85
+ sourceID: source
86
+ });
87
+ uniqueBuckets.set(layer.id, bucket);
88
+ bucket.populate(features, options);
89
+ featureIndex.bucketLayerIDs.push(layers.map(l => l.id));
80
90
  }
91
+ }
81
92
 
93
+ async function finalizeBuckets(params, options, resources) {
94
+ const { zoom, showCollisionBoxes, justReloaded, painter } = params;
95
+ const { collisionBoxArray, featureIndex, uniqueBuckets } = options;
82
96
  const buckets = new Map();
83
97
  const { glyphAtlas, imageAtlas, glyphMap, iconMap } = await makeAtlasses(options, resources);
84
98
  let hasSymbolBuckets = false;
@@ -86,7 +100,7 @@ async function makeWorkerTile(params, vectorTile, layerIndex, resources) {
86
100
  for (const bucket of uniqueBuckets.values()) {
87
101
  if (bucket instanceof SymbolBucket) {
88
102
  hasSymbolBuckets = true;
89
- recalculateLayers(bucket.layers, zoom, globalState);
103
+ recalculateLayers(bucket.layers, zoom);
90
104
  performSymbolLayout(
91
105
  bucket,
92
106
  glyphMap,
@@ -102,7 +116,7 @@ async function makeWorkerTile(params, vectorTile, layerIndex, resources) {
102
116
  bucket.hasPattern &&
103
117
  (bucket instanceof LineBucket || bucket instanceof FillBucket || bucket instanceof FillExtrusionBucket)
104
118
  ) {
105
- recalculateLayers(bucket.layers, zoom, globalState);
119
+ recalculateLayers(bucket.layers, zoom);
106
120
  bucket.addFeatures(options, imageAtlas.patternPositions);
107
121
  }
108
122
  if (bucket.isEmpty()) {
@@ -152,9 +166,9 @@ function createTileID({ tileID }) {
152
166
  return new OverscaledTileID(overscaledZ, wrap, z, x, y);
153
167
  }
154
168
 
155
- function recalculateLayers(layers, zoom, globalState) {
169
+ function recalculateLayers(layers, zoom) {
156
170
  // Layers are shared and may have been used by a WorkerTile with a different zoom.
157
- const parameters = new EvaluationParameters(zoom, { globalState });
171
+ const parameters = new EvaluationParameters(zoom);
158
172
  for (const layer of layers) {
159
173
  layer.recalculate(parameters);
160
174
  }
@@ -20,6 +20,6 @@ const subclasses = {
20
20
  raster
21
21
  };
22
22
 
23
- export default function createStyleLayer(layer) {
24
- return new subclasses[layer.type](layer);
23
+ export default function createStyleLayer(layer, globalState) {
24
+ return new subclasses[layer.type](layer, globalState);
25
25
  }
@@ -12,13 +12,11 @@ export default class EvaluationParameters {
12
12
  this.fadeDuration = options.fadeDuration || 0;
13
13
  this.zoomHistory = options.zoomHistory || new ZoomHistory();
14
14
  this.transition = options.transition || {};
15
- this.globalState = options.globalState || {};
16
15
  } else {
17
16
  this.now = 0;
18
17
  this.fadeDuration = 0;
19
18
  this.zoomHistory = new ZoomHistory();
20
19
  this.transition = {};
21
- this.globalState = {};
22
20
  }
23
21
  }
24
22
 
@@ -1,6 +1,5 @@
1
1
  import { interpolate, normalizePropertyExpression } from '@mapwhit/style-expressions';
2
2
  import assert from 'assert';
3
- import { clone } from '../util/object.js';
4
3
  import { easeCubicInOut } from '../util/util.js';
5
4
  import EvaluationParameters from './evaluation_parameters.js';
6
5
 
@@ -60,12 +59,13 @@ import EvaluationParameters from './evaluation_parameters.js';
60
59
  * @private
61
60
  */
62
61
  export class PropertyValue {
63
- constructor(property, value) {
62
+ constructor(property, value, globalState) {
64
63
  this.property = property;
65
64
  this.value = value;
66
65
  this.expression = normalizePropertyExpression(
67
66
  value === undefined ? property.specification.default : value,
68
- property.specification
67
+ property.specification,
68
+ globalState
69
69
  );
70
70
  }
71
71
 
@@ -97,9 +97,9 @@ export class PropertyValue {
97
97
  * @private
98
98
  */
99
99
  export class TransitionablePropertyValue {
100
- constructor(property) {
100
+ constructor(property, globalState) {
101
101
  this.property = property;
102
- this.value = new PropertyValue(property, undefined);
102
+ this.value = new PropertyValue(property, undefined, globalState);
103
103
  }
104
104
 
105
105
  transitioned(parameters, prior) {
@@ -132,36 +132,39 @@ export class TransitionablePropertyValue {
132
132
  * @private
133
133
  */
134
134
  export class Transitionable {
135
- constructor(properties) {
135
+ #globalState; // reference to global state
136
+ constructor(properties, globalState) {
136
137
  this._properties = properties;
137
138
  this._values = Object.create(properties.defaultTransitionablePropertyValues);
139
+ this.#globalState = globalState;
138
140
  }
139
141
 
140
142
  getValue(name) {
141
- return clone(this._values[name].value.value);
143
+ return structuredClone(this._values[name].value.value);
142
144
  }
143
145
 
144
146
  setValue(name, value) {
145
147
  if (!this._values.hasOwnProperty(name)) {
146
- this._values[name] = new TransitionablePropertyValue(this._values[name].property);
148
+ this._values[name] = new TransitionablePropertyValue(this._values[name].property, this.#globalState);
147
149
  }
148
150
  // Note that we do not _remove_ an own property in the case where a value is being reset
149
151
  // to the default: the transition might still be non-default.
150
152
  this._values[name].value = new PropertyValue(
151
153
  this._values[name].property,
152
- value === null ? undefined : clone(value)
154
+ value === null ? undefined : structuredClone(value),
155
+ this.#globalState
153
156
  );
154
157
  }
155
158
 
156
159
  getTransition(name) {
157
- return clone(this._values[name].transition);
160
+ return structuredClone(this._values[name].transition);
158
161
  }
159
162
 
160
163
  setTransition(name, value) {
161
164
  if (!this._values.hasOwnProperty(name)) {
162
- this._values[name] = new TransitionablePropertyValue(this._values[name].property);
165
+ this._values[name] = new TransitionablePropertyValue(this._values[name].property, this.#globalState);
163
166
  }
164
- this._values[name].transition = clone(value) || undefined;
167
+ this._values[name].transition = structuredClone(value) || undefined;
165
168
  }
166
169
 
167
170
  serialize() {
@@ -308,17 +311,23 @@ export class Transitioning {
308
311
  * @private
309
312
  */
310
313
  export class Layout {
311
- constructor(properties) {
314
+ #globalState; // reference to global state
315
+ constructor(properties, globalState) {
312
316
  this._properties = properties;
313
317
  this._values = Object.create(properties.defaultPropertyValues);
318
+ this.#globalState = globalState;
314
319
  }
315
320
 
316
321
  getValue(name) {
317
- return clone(this._values[name].value);
322
+ return structuredClone(this._values[name].value);
318
323
  }
319
324
 
320
325
  setValue(name, value) {
321
- this._values[name] = new PropertyValue(this._values[name].property, value === null ? undefined : clone(value));
326
+ this._values[name] = new PropertyValue(
327
+ this._values[name].property,
328
+ value === null ? undefined : structuredClone(value),
329
+ this.#globalState
330
+ );
322
331
  }
323
332
 
324
333
  serialize() {
@@ -10,7 +10,7 @@ import { getType as getSourceType, setType as setSourceType } from '../source/so
10
10
  import SourceCache from '../source/source_cache.js';
11
11
  import CrossTileSymbolIndex from '../symbol/cross_tile_symbol_index.js';
12
12
  import browser from '../util/browser.js';
13
- import { clone, deepEqual } from '../util/object.js';
13
+ import { deepEqual } from '../util/object.js';
14
14
  import createStyleLayer from './create_style_layer.js';
15
15
  import Light from './light.js';
16
16
  import loadSprite from './load_sprite.js';
@@ -138,6 +138,8 @@ class Style extends Evented {
138
138
  for (const layer of this._layers.values()) {
139
139
  const layoutAffectingGlobalStateRefs = layer.getLayoutAffectingGlobalStateRefs();
140
140
  const paintAffectingGlobalStateRefs = layer.getPaintAffectingGlobalStateRefs();
141
+ const visibilityAffectingGlobalStateRefs = layer.getVisibilityAffectingGlobalStateRefs();
142
+ let visibilityToEval;
141
143
 
142
144
  for (const ref of globalStateRefs) {
143
145
  if (layoutAffectingGlobalStateRefs.has(ref)) {
@@ -148,6 +150,13 @@ class Style extends Evented {
148
150
  this._updatePaintProperty(layer, name, value);
149
151
  }
150
152
  }
153
+ if (visibilityAffectingGlobalStateRefs?.has(ref)) {
154
+ visibilityToEval = true;
155
+ }
156
+ }
157
+ if (visibilityToEval) {
158
+ layer.recalculateVisibility();
159
+ this._updateLayer(layer);
151
160
  }
152
161
  }
153
162
 
@@ -199,11 +208,14 @@ class Style extends Evented {
199
208
  const layers = this.stylesheet.layers;
200
209
 
201
210
  this._layers.clear();
211
+
212
+ this.setGlobalState(this.stylesheet.state ?? null);
213
+
202
214
  for (let layer of layers) {
203
215
  if (layer.ref) {
204
216
  continue; // just ignore layers that reference other layers
205
217
  }
206
- layer = createStyleLayer(layer);
218
+ layer = createStyleLayer(layer, this._globalState);
207
219
  layer.setEventedParent(this, { layer: { id: layer.id } });
208
220
  this._layers.set(layer.id, layer);
209
221
  }
@@ -213,8 +225,6 @@ class Style extends Evented {
213
225
  this.light = this.stylesheet.light;
214
226
  this._light = new Light(this.light);
215
227
 
216
- this.setGlobalState(this.stylesheet.state ?? null);
217
-
218
228
  this.fire(new Event('data', { dataType: 'style' }));
219
229
  this.fire(new Event('style.load'));
220
230
  }
@@ -244,7 +254,12 @@ class Style extends Evented {
244
254
  }
245
255
  }
246
256
 
247
- loaded() {
257
+ /**
258
+ * Returns `true` when style is loaded.
259
+ * @param {Boolean} ignoreTilesLoading set to `true` to check that style is loaded
260
+ * even when sources are loading tiles
261
+ */
262
+ loaded(ignoreTilesLoading) {
248
263
  if (!this._loaded) {
249
264
  return false;
250
265
  }
@@ -254,7 +269,7 @@ class Style extends Evented {
254
269
  }
255
270
 
256
271
  for (const id in this._sources) {
257
- if (!this._sources[id].loaded()) {
272
+ if (!this._sources[id].loaded(ignoreTilesLoading)) {
258
273
  return false;
259
274
  }
260
275
  }
@@ -515,11 +530,11 @@ class Style extends Evented {
515
530
 
516
531
  if (typeof layerObject.source === 'object') {
517
532
  this.addSource(id, layerObject.source);
518
- layerObject = clone(layerObject);
533
+ layerObject = structuredClone(layerObject);
519
534
  layerObject = Object.assign(layerObject, { source: id });
520
535
  }
521
536
 
522
- const layer = createStyleLayer(layerObject);
537
+ const layer = createStyleLayer(layerObject, this._globalState);
523
538
  this._validateLayer(layer);
524
539
 
525
540
  layer.setEventedParent(this, { layer: { id: id } });
@@ -651,7 +666,7 @@ class Style extends Evented {
651
666
  return;
652
667
  }
653
668
 
654
- layer.setFilter(clone(filter));
669
+ layer.setFilter(structuredClone(filter));
655
670
  this._updateLayer(layer);
656
671
  }
657
672
 
@@ -661,7 +676,7 @@ class Style extends Evented {
661
676
  * @returns {*} the layer's filter, if any
662
677
  */
663
678
  getFilter(layer) {
664
- return clone(this.getLayer(layer).filter);
679
+ return structuredClone(this.getLayer(layer).filter);
665
680
  }
666
681
 
667
682
  setLayoutProperty(layerId, name, value) {
@@ -2,8 +2,8 @@ import StyleLayer from '../style_layer.js';
2
2
  import properties from './background_style_layer_properties.js';
3
3
 
4
4
  class BackgroundStyleLayer extends StyleLayer {
5
- constructor(layer) {
6
- super(layer, properties);
5
+ constructor(layer, globalState) {
6
+ super(layer, properties, globalState);
7
7
  }
8
8
  }
9
9
 
@@ -1,7 +1,7 @@
1
1
  import glMatrix from '@mapbox/gl-matrix';
2
2
  import Point from '@mapbox/point-geometry';
3
+ import { polygonIntersectsBufferedPoint } from '@mapwhit/geometry';
3
4
  import CircleBucket from '../../data/bucket/circle_bucket.js';
4
- import { polygonIntersectsBufferedPoint } from '../../util/intersection_tests.js';
5
5
  import { getMaximumPaintValue, translate, translateDistance } from '../query_utils.js';
6
6
  import StyleLayer from '../style_layer.js';
7
7
  import properties from './circle_style_layer_properties.js';
@@ -9,8 +9,8 @@ import properties from './circle_style_layer_properties.js';
9
9
  const { vec4 } = glMatrix;
10
10
 
11
11
  class CircleStyleLayer extends StyleLayer {
12
- constructor(layer) {
13
- super(layer, properties);
12
+ constructor(layer, globalState) {
13
+ super(layer, properties, globalState);
14
14
  }
15
15
 
16
16
  createBucket(parameters) {
@@ -1,7 +1,7 @@
1
1
  import glMatrix from '@mapbox/gl-matrix';
2
2
  import Point from '@mapbox/point-geometry';
3
+ import { polygonIntersectsMultiPolygon, polygonIntersectsPolygon } from '@mapwhit/geometry';
3
4
  import FillExtrusionBucket from '../../data/bucket/fill_extrusion_bucket.js';
4
- import { polygonIntersectsMultiPolygon, polygonIntersectsPolygon } from '../../util/intersection_tests.js';
5
5
  import { translate, translateDistance } from '../query_utils.js';
6
6
  import StyleLayer from '../style_layer.js';
7
7
  import properties from './fill_extrusion_style_layer_properties.js';
@@ -9,8 +9,8 @@ import properties from './fill_extrusion_style_layer_properties.js';
9
9
  const { vec4 } = glMatrix;
10
10
 
11
11
  export class FillExtrusionStyleLayer extends StyleLayer {
12
- constructor(layer) {
13
- super(layer, properties);
12
+ constructor(layer, globalState) {
13
+ super(layer, properties, globalState);
14
14
  }
15
15
 
16
16
  createBucket(parameters) {
@@ -1,12 +1,12 @@
1
+ import { polygonIntersectsMultiPolygon } from '@mapwhit/geometry';
1
2
  import FillBucket from '../../data/bucket/fill_bucket.js';
2
- import { polygonIntersectsMultiPolygon } from '../../util/intersection_tests.js';
3
3
  import { translate, translateDistance } from '../query_utils.js';
4
4
  import StyleLayer from '../style_layer.js';
5
5
  import properties from './fill_style_layer_properties.js';
6
6
 
7
7
  class FillStyleLayer extends StyleLayer {
8
- constructor(layer) {
9
- super(layer, properties);
8
+ constructor(layer, globalState) {
9
+ super(layer, properties, globalState);
10
10
  }
11
11
 
12
12
  recalculate(parameters) {
@@ -8,8 +8,8 @@ class HeatmapStyleLayer extends StyleLayer {
8
8
  return new HeatmapBucket(options);
9
9
  }
10
10
 
11
- constructor(layer) {
12
- super(layer, properties);
11
+ constructor(layer, globalState) {
12
+ super(layer, properties, globalState);
13
13
 
14
14
  // make sure color ramp texture is generated for default heatmap color too
15
15
  this._updateColorRamp();
@@ -43,7 +43,7 @@ class HeatmapStyleLayer extends StyleLayer {
43
43
  }
44
44
 
45
45
  hasOffscreenPass() {
46
- return this._paint.get('heatmap-opacity') !== 0 && this.visibility !== 'none';
46
+ return this._paint.get('heatmap-opacity') !== 0 && !this.isHidden();
47
47
  }
48
48
  }
49
49
 
@@ -2,12 +2,12 @@ import StyleLayer from '../style_layer.js';
2
2
  import properties from './hillshade_style_layer_properties.js';
3
3
 
4
4
  class HillshadeStyleLayer extends StyleLayer {
5
- constructor(layer) {
6
- super(layer, properties);
5
+ constructor(layer, globalState) {
6
+ super(layer, properties, globalState);
7
7
  }
8
8
 
9
9
  hasOffscreenPass() {
10
- return this._paint.get('hillshade-exaggeration') !== 0 && this.visibility !== 'none';
10
+ return this._paint.get('hillshade-exaggeration') !== 0 && !this.isHidden();
11
11
  }
12
12
  }
13
13
 
@@ -1,7 +1,7 @@
1
1
  import Point from '@mapbox/point-geometry';
2
+ import { polygonIntersectsBufferedMultiLine } from '@mapwhit/geometry';
2
3
  import LineBucket from '../../data/bucket/line_bucket.js';
3
4
  import renderColorRamp from '../../util/color_ramp.js';
4
- import { polygonIntersectsBufferedMultiLine } from '../../util/intersection_tests.js';
5
5
  import EvaluationParameters from '../evaluation_parameters.js';
6
6
  import { DataDrivenProperty } from '../properties.js';
7
7
  import { getMaximumPaintValue, translate, translateDistance } from '../query_utils.js';
@@ -29,8 +29,8 @@ const lineFloorwidthProperty = new LineFloorwidthProperty(properties.paint.prope
29
29
  lineFloorwidthProperty.useIntegerZoom = true;
30
30
 
31
31
  class LineStyleLayer extends StyleLayer {
32
- constructor(layer) {
33
- super(layer, properties);
32
+ constructor(layer, globalState) {
33
+ super(layer, properties, globalState);
34
34
  }
35
35
 
36
36
  _handleSpecialPaintPropertyUpdate(name) {
@@ -2,8 +2,8 @@ import StyleLayer from '../style_layer.js';
2
2
  import properties from './raster_style_layer_properties.js';
3
3
 
4
4
  class RasterStyleLayer extends StyleLayer {
5
- constructor(layer) {
6
- super(layer, properties);
5
+ constructor(layer, globalState) {
6
+ super(layer, properties, globalState);
7
7
  }
8
8
  }
9
9
 
@@ -6,8 +6,8 @@ import StyleLayer from '../style_layer.js';
6
6
  import properties from './symbol_style_layer_properties.js';
7
7
 
8
8
  class SymbolStyleLayer extends StyleLayer {
9
- constructor(layer) {
10
- super(layer, properties);
9
+ constructor(layer, globalState) {
10
+ super(layer, properties, globalState);
11
11
  }
12
12
 
13
13
  recalculate(parameters) {
@@ -1,6 +1,7 @@
1
1
  import { Evented } from '@mapwhit/events';
2
2
  import { supportsPropertyExpression } from '@mapwhit/style-expressions';
3
3
  import featureFilter from '../style-spec/feature_filter/index.js';
4
+ import visibilityExpression from '../style-spec/visibility.js';
4
5
  import createKey from '../util/key.js';
5
6
  import { Layout, PossiblyEvaluatedPropertyValue, Transitionable } from './properties.js';
6
7
 
@@ -16,7 +17,7 @@ const TRANSITION_SUFFIX = '-transition';
16
17
  class StyleLayer extends Evented {
17
18
  #key;
18
19
 
19
- constructor(layer, properties) {
20
+ constructor(layer, properties, globalState) {
20
21
  super();
21
22
 
22
23
  this.id = layer.id;
@@ -28,6 +29,8 @@ class StyleLayer extends Evented {
28
29
  this.paint = {};
29
30
  this.layout = {};
30
31
 
32
+ this._visibilityExpression = visibilityExpression(this.visibility, globalState);
33
+
31
34
  if (layer.type !== 'background') {
32
35
  this.source = layer.source;
33
36
  this['source-layer'] = this.sourceLayer = layer['source-layer'];
@@ -38,10 +41,10 @@ class StyleLayer extends Evented {
38
41
  this._featureFilter ??= featureFilter.addGlobalStateRefs(() => true);
39
42
 
40
43
  if (properties.layout) {
41
- this._unevaluatedLayout = new Layout(properties.layout);
44
+ this._unevaluatedLayout = new Layout(properties.layout, globalState);
42
45
  }
43
46
 
44
- this._transitionablePaint = new Transitionable(properties.paint);
47
+ this._transitionablePaint = new Transitionable(properties.paint, globalState);
45
48
 
46
49
  for (const property in layer.paint) {
47
50
  this.setPaintProperty(property, layer.paint[property]);
@@ -94,6 +97,10 @@ class StyleLayer extends Evented {
94
97
  getLayoutAffectingGlobalStateRefs() {
95
98
  const globalStateRefs = new Set();
96
99
 
100
+ for (const globalStateRef of this._visibilityExpression.getGlobalStateRefs()) {
101
+ globalStateRefs.add(globalStateRef);
102
+ }
103
+
97
104
  if (this._unevaluatedLayout) {
98
105
  for (const propertyName in this._unevaluatedLayout._values) {
99
106
  const value = this._unevaluatedLayout._values[propertyName];
@@ -134,11 +141,21 @@ class StyleLayer extends Evented {
134
141
  return globalStateRefs;
135
142
  }
136
143
 
144
+ /**
145
+ * Get list of global state references that are used within visibility expression.
146
+ * This is used to determine if layer visibility needs to be updated when global state property changes.
147
+ */
148
+ getVisibilityAffectingGlobalStateRefs() {
149
+ return this._visibilityExpression.getGlobalStateRefs();
150
+ }
151
+
137
152
  setLayoutProperty(name, value) {
138
153
  this.#key = undefined;
139
154
  this.layout[name] = value;
140
155
  if (name === 'visibility') {
141
- this.visibility = value === 'none' ? value : 'visible';
156
+ this.visibility = value;
157
+ this._visibilityExpression.setValue(value);
158
+ this.recalculateVisibility();
142
159
  return;
143
160
  }
144
161
 
@@ -184,14 +201,14 @@ class StyleLayer extends Evented {
184
201
  // No-op; can be overridden by derived classes.
185
202
  }
186
203
 
187
- isHidden(zoom) {
188
- if (this.minzoom && zoom < this.minzoom) {
204
+ isHidden(zoom = this.minzoom, minzoom = this.minzoom) {
205
+ if (minzoom && zoom < minzoom) {
189
206
  return true;
190
207
  }
191
208
  if (this.maxzoom && zoom >= this.maxzoom) {
192
209
  return true;
193
210
  }
194
- return this.visibility === 'none';
211
+ return this.visibility === 'none' || this._visibility === 'none';
195
212
  }
196
213
 
197
214
  updateTransitions(parameters) {
@@ -202,6 +219,10 @@ class StyleLayer extends Evented {
202
219
  return this._transitioningPaint.hasTransition();
203
220
  }
204
221
 
222
+ recalculateVisibility() {
223
+ this._visibility = this._visibilityExpression.evaluate();
224
+ }
225
+
205
226
  recalculate(parameters) {
206
227
  if (parameters.getCrossfadeParameters) {
207
228
  this._crossfadeParameters = parameters.getCrossfadeParameters();