@mapwhit/tilerenderer 1.2.1 → 1.3.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 (35) hide show
  1. package/build/min/package.json +1 -1
  2. package/package.json +4 -4
  3. package/src/data/bucket/fill_extrusion_bucket.js +34 -39
  4. package/src/data/bucket/line_bucket.js +40 -48
  5. package/src/data/bucket/symbol_bucket.js +4 -5
  6. package/src/data/feature_index.js +10 -3
  7. package/src/geo/transform.js +13 -13
  8. package/src/index.js +1 -1
  9. package/src/source/geojson_wrapper.js +1 -9
  10. package/src/source/query_features.js +4 -1
  11. package/src/source/rtl_text_plugin.js +3 -1
  12. package/src/source/source_cache.js +3 -3
  13. package/src/source/tile.js +1 -1
  14. package/src/style/evaluation_parameters.js +5 -4
  15. package/src/style/query_utils.js +82 -7
  16. package/src/style/style.js +78 -25
  17. package/src/style/style_layer/circle_style_layer.js +26 -45
  18. package/src/style/style_layer/fill_extrusion_style_layer.js +26 -33
  19. package/src/style/style_layer/heatmap_style_layer.js +21 -7
  20. package/src/style/style_layer/line_style_layer.js +7 -7
  21. package/src/style/style_layer.js +3 -3
  22. package/src/style-spec/feature_filter/index.js +24 -19
  23. package/src/symbol/anchor.js +1 -1
  24. package/src/symbol/check_max_angle.js +6 -6
  25. package/src/symbol/collision_feature.js +9 -15
  26. package/src/symbol/collision_index.js +33 -27
  27. package/src/symbol/get_anchors.js +6 -5
  28. package/src/symbol/projection.js +3 -3
  29. package/src/symbol/quads.js +1 -1
  30. package/src/symbol/symbol_layout.js +1 -2
  31. package/src/ui/camera.js +1 -1
  32. package/src/ui/map.js +24 -24
  33. package/src/util/classify_rings.js +2 -4
  34. package/src/util/vectortile_to_geojson.js +65 -26
  35. package/src/symbol/clip_line.js +0 -72
@@ -1,4 +1,8 @@
1
- import Point from '@mapbox/point-geometry';
1
+ import glMatrix from '@mapbox/gl-matrix';
2
+ import { polygonIntersectsBufferedPoint } from '@mapwhit/geometry';
3
+ import { Point } from '@mapwhit/point-geometry';
4
+
5
+ const { vec4 } = glMatrix;
2
6
 
3
7
  export function getMaximumPaintValue(property, layer, bucket) {
4
8
  const value = layer._paint.get(property).value;
@@ -18,16 +22,87 @@ export function translate(queryGeometry, translate, translateAnchor, bearing, pi
18
22
  return queryGeometry;
19
23
  }
20
24
 
21
- const pt = Point.convert(translate)._mult(pixelsToTileUnits);
25
+ const pt = new Point(translate[0], translate[1])._mult(pixelsToTileUnits);
22
26
 
23
27
  if (translateAnchor === 'viewport') {
24
28
  pt._rotate(-bearing);
25
29
  }
26
30
 
27
- const translated = [];
28
- for (let i = 0; i < queryGeometry.length; i++) {
29
- const point = queryGeometry[i];
30
- translated.push(point.sub(pt));
31
+ pt;
32
+
33
+ return queryGeometry.map(point => point.sub(pt));
34
+ }
35
+
36
+ const circleIntersectionTests = new Map([
37
+ [
38
+ 'map',
39
+ new Map([
40
+ [
41
+ 'map',
42
+ ({ queryGeometry, point, size }) => {
43
+ return polygonIntersectsBufferedPoint(queryGeometry, point, size);
44
+ }
45
+ ],
46
+ [
47
+ 'viewport',
48
+ ({ queryGeometry, point, pixelPosMatrix, size, transform }) => {
49
+ const projectedCenter = vec4.transformMat4([], [point.x, point.y, 0, 1], pixelPosMatrix);
50
+ const adjustedSize = (size * projectedCenter[3]) / transform.cameraToCenterDistance;
51
+ return polygonIntersectsBufferedPoint(queryGeometry, point, adjustedSize);
52
+ }
53
+ ]
54
+ ])
55
+ ],
56
+ [
57
+ 'viewport',
58
+ new Map([
59
+ [
60
+ 'map',
61
+ ({ queryGeometry, point, pixelPosMatrix, size, transform }) => {
62
+ const projectedCenter = vec4.transformMat4([], [point.x, point.y, 0, 1], pixelPosMatrix);
63
+ const adjustedSize = (size * transform.cameraToCenterDistance) / projectedCenter[3];
64
+ return polygonIntersectsBufferedPoint(queryGeometry, projectPoint(point, pixelPosMatrix), adjustedSize);
65
+ }
66
+ ],
67
+ [
68
+ 'viewport',
69
+ ({ queryGeometry, point, pixelPosMatrix, size }) => {
70
+ return polygonIntersectsBufferedPoint(queryGeometry, projectPoint(point, pixelPosMatrix), size);
71
+ }
72
+ ]
73
+ ])
74
+ ]
75
+ ]);
76
+
77
+ export function circleIntersection({
78
+ queryGeometry,
79
+ geometry,
80
+ pixelPosMatrix,
81
+ size,
82
+ transform,
83
+ pitchAlignment = 'map',
84
+ pitchScale = 'map'
85
+ }) {
86
+ const intersectionTest = circleIntersectionTests.get(pitchAlignment).get(pitchScale);
87
+ const param = { queryGeometry, pixelPosMatrix, size, transform };
88
+ for (const ring of geometry) {
89
+ for (const point of ring) {
90
+ param.point = point;
91
+ if (intersectionTest(param)) {
92
+ return true;
93
+ }
94
+ }
31
95
  }
32
- return translated;
96
+ return false;
97
+ }
98
+
99
+ function projectPoint(p, pixelPosMatrix) {
100
+ const point = vec4.transformMat4([], [p.x, p.y, 0, 1], pixelPosMatrix);
101
+ return new Point(point[0] / point[3], point[1] / point[3]);
102
+ }
103
+
104
+ export function projectQueryGeometry(queryGeometry, pixelPosMatrix) {
105
+ return queryGeometry.map(p => {
106
+ return projectPoint(p, pixelPosMatrix);
107
+ });
33
108
  }
@@ -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) {
@@ -749,7 +785,10 @@ class Style extends Evented {
749
785
  }
750
786
 
751
787
  setFeatureState(feature, state) {
752
- this._checkLoaded();
788
+ if (!this._loaded) {
789
+ this.#opsQueue.push(() => this.setFeatureState(feature, state));
790
+ return;
791
+ }
753
792
  const sourceId = feature.source;
754
793
  const sourceLayer = feature.sourceLayer;
755
794
  const sourceCache = this._sources[sourceId];
@@ -776,7 +815,10 @@ class Style extends Evented {
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
 
@@ -802,7 +844,9 @@ class Style extends Evented {
802
844
  }
803
845
 
804
846
  getFeatureState(feature) {
805
- this._checkLoaded();
847
+ if (!this._loaded) {
848
+ return;
849
+ }
806
850
  const sourceId = feature.source;
807
851
  const sourceLayer = feature.sourceLayer;
808
852
  const sourceCache = this._sources[sourceId];
@@ -920,8 +964,9 @@ class Style extends Evented {
920
964
  }
921
965
 
922
966
  const sourceResults = [];
967
+ params = params ? { ...params, globalState: this._globalState } : { globalState: this._globalState };
923
968
  for (const id in this._sources) {
924
- if (params.layers && !includedSources[id]) {
969
+ if (params?.layers && !includedSources[id]) {
925
970
  continue;
926
971
  }
927
972
  sourceResults.push(
@@ -948,7 +993,12 @@ class Style extends Evented {
948
993
 
949
994
  querySourceFeatures(sourceID, params) {
950
995
  const sourceCache = this._sources[sourceID];
951
- return sourceCache ? querySourceFeatures(sourceCache, params) : [];
996
+ return sourceCache
997
+ ? querySourceFeatures(
998
+ sourceCache,
999
+ params ? { ...params, globalState: this._globalState } : { globalState: this._globalState }
1000
+ )
1001
+ : [];
952
1002
  }
953
1003
 
954
1004
  getLight() {
@@ -956,7 +1006,10 @@ class Style extends Evented {
956
1006
  }
957
1007
 
958
1008
  setLight(lightOptions) {
959
- this._checkLoaded();
1009
+ if (!this._loaded) {
1010
+ this.#opsQueue.push(() => this.setLight(lightOptions));
1011
+ return;
1012
+ }
960
1013
 
961
1014
  const light = this._light.getLight();
962
1015
  let _update = false;
@@ -1,14 +1,15 @@
1
- import glMatrix from '@mapbox/gl-matrix';
2
- import Point from '@mapbox/point-geometry';
3
- import { polygonIntersectsBufferedPoint } from '@mapwhit/geometry';
4
1
  import CircleBucket from '../../data/bucket/circle_bucket.js';
5
- import { getMaximumPaintValue, translate, translateDistance } from '../query_utils.js';
2
+ import {
3
+ circleIntersection,
4
+ getMaximumPaintValue,
5
+ projectQueryGeometry,
6
+ translate,
7
+ translateDistance
8
+ } from '../query_utils.js';
6
9
  import StyleLayer from '../style_layer.js';
7
10
  import properties from './circle_style_layer_properties.js';
8
11
 
9
- const { vec4 } = glMatrix;
10
-
11
- class CircleStyleLayer extends StyleLayer {
12
+ export default class CircleStyleLayer extends StyleLayer {
12
13
  constructor(layer, globalState) {
13
14
  super(layer, properties, globalState);
14
15
  }
@@ -51,48 +52,28 @@ class CircleStyleLayer extends StyleLayer {
51
52
  // // Otherwise, compare geometry in the plane of the viewport
52
53
  // // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance
53
54
  // // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance
55
+
54
56
  const pitchScale = this._paint.get('circle-pitch-scale');
55
57
  const pitchAlignment = this._paint.get('circle-pitch-alignment');
56
- const alignWithMap = pitchAlignment === 'map';
57
- const alignWithViewport = pitchAlignment === 'viewport';
58
- const transformedPolygon = alignWithMap
59
- ? translatedPolygon
60
- : projectQueryGeometry(translatedPolygon, pixelPosMatrix);
61
- const transformedSize = alignWithMap ? size * pixelsToTileUnits : size;
62
- const adjustViewportToMap = pitchScale === 'viewport' && alignWithMap;
63
- const adjustMapToViewport = pitchScale === 'map' && alignWithViewport;
64
-
65
- for (const ring of geometry) {
66
- for (const point of ring) {
67
- const transformedPoint = alignWithMap ? point : projectPoint(point, pixelPosMatrix);
68
58
 
69
- let adjustedSize = transformedSize;
70
- const projectedCenter = vec4.transformMat4([], [point.x, point.y, 0, 1], pixelPosMatrix);
71
- if (adjustViewportToMap) {
72
- adjustedSize *= projectedCenter[3] / transform.cameraToCenterDistance;
73
- } else if (adjustMapToViewport) {
74
- adjustedSize *= transform.cameraToCenterDistance / projectedCenter[3];
75
- }
76
-
77
- if (polygonIntersectsBufferedPoint(transformedPolygon, transformedPoint, adjustedSize)) {
78
- return true;
79
- }
80
- }
59
+ let transformedPolygon;
60
+ let transformedSize;
61
+ if (pitchAlignment === 'map') {
62
+ transformedPolygon = translatedPolygon;
63
+ transformedSize = size * pixelsToTileUnits;
64
+ } else {
65
+ transformedPolygon = projectQueryGeometry(translatedPolygon, pixelPosMatrix);
66
+ transformedSize = size;
81
67
  }
82
68
 
83
- return false;
69
+ return circleIntersection({
70
+ queryGeometry: transformedPolygon,
71
+ geometry,
72
+ pixelPosMatrix,
73
+ size: transformedSize,
74
+ transform,
75
+ pitchAlignment,
76
+ pitchScale
77
+ });
84
78
  }
85
79
  }
86
-
87
- function projectPoint(p, pixelPosMatrix) {
88
- const point = vec4.transformMat4([], [p.x, p.y, 0, 1], pixelPosMatrix);
89
- return new Point(point[0] / point[3], point[1] / point[3]);
90
- }
91
-
92
- function projectQueryGeometry(queryGeometry, pixelPosMatrix) {
93
- return queryGeometry.map(p => {
94
- return projectPoint(p, pixelPosMatrix);
95
- });
96
- }
97
-
98
- export default CircleStyleLayer;
@@ -1,6 +1,6 @@
1
1
  import glMatrix from '@mapbox/gl-matrix';
2
- import Point from '@mapbox/point-geometry';
3
2
  import { polygonIntersectsMultiPolygon, polygonIntersectsPolygon } from '@mapwhit/geometry';
3
+ import { clone, equals, sub } from '@mapwhit/point-geometry';
4
4
  import FillExtrusionBucket from '../../data/bucket/fill_extrusion_bucket.js';
5
5
  import { translate, translateDistance } from '../query_utils.js';
6
6
  import StyleLayer from '../style_layer.js';
@@ -45,11 +45,9 @@ export class FillExtrusionStyleLayer extends StyleLayer {
45
45
  const height = this._paint.get('fill-extrusion-height').evaluate(feature, featureState);
46
46
  const base = this._paint.get('fill-extrusion-base').evaluate(feature, featureState);
47
47
 
48
- const projectedQueryGeometry = projectQueryGeometry(translatedPolygon, pixelPosMatrix, transform, 0);
48
+ const projectedQueryGeometry = projectQueryGeometry(translatedPolygon, pixelPosMatrix, 0);
49
49
 
50
- const projected = projectExtrusion(geometry, base, height, pixelPosMatrix);
51
- const projectedBase = projected[0];
52
- const projectedTop = projected[1];
50
+ const { projectedBase, projectedTop } = projectExtrusion(geometry, base, height, pixelPosMatrix);
53
51
  return checkIntersection(projectedBase, projectedTop, projectedQueryGeometry);
54
52
  }
55
53
  }
@@ -72,7 +70,7 @@ export function getIntersectionDistance(projectedQueryGeometry, projectedFace) {
72
70
  let i = 0;
73
71
  const a = projectedFace[i++];
74
72
  let b;
75
- while (!b || a.equals(b)) {
73
+ while (!b || equals(a, b)) {
76
74
  b = projectedFace[i++];
77
75
  if (!b) {
78
76
  return Number.POSITIVE_INFINITY;
@@ -84,9 +82,9 @@ export function getIntersectionDistance(projectedQueryGeometry, projectedFace) {
84
82
  const c = projectedFace[i];
85
83
  const p = projectedQueryGeometry[0];
86
84
 
87
- const ab = b.sub(a);
88
- const ac = c.sub(a);
89
- const ap = p.sub(a);
85
+ const ab = sub(clone(b), a);
86
+ const ac = sub(clone(c), a);
87
+ const ap = sub(clone(p), a);
90
88
 
91
89
  const dotABAB = dot(ab, ab);
92
90
  const dotABAC = dot(ab, ac);
@@ -153,8 +151,8 @@ function checkIntersection(projectedBase, projectedTop, projectedQueryGeometry)
153
151
  * performance improvement.
154
152
  */
155
153
  function projectExtrusion(geometry, zBase, zTop, m) {
156
- const projectedBase = [];
157
- const projectedTop = [];
154
+ const projectedBase = new Array(geometry.length);
155
+ const projectedTop = new Array(geometry.length);
158
156
 
159
157
  const baseXZ = m[8] * zBase;
160
158
  const baseYZ = m[9] * zBase;
@@ -165,12 +163,12 @@ function projectExtrusion(geometry, zBase, zTop, m) {
165
163
  const topZZ = m[10] * zTop;
166
164
  const topWZ = m[11] * zTop;
167
165
 
168
- for (const r of geometry) {
169
- const ringBase = [];
170
- const ringTop = [];
171
- for (const p of r) {
172
- const x = p.x;
173
- const y = p.y;
166
+ for (let i = 0; i < geometry.length; i++) {
167
+ const ring = geometry[i];
168
+ const ringBase = new Array(ring.length);
169
+ const ringTop = new Array(ring.length);
170
+ for (let j = 0; j < ring.length; j++) {
171
+ const { x, y } = ring[j];
174
172
 
175
173
  const sX = m[0] * x + m[4] * y + m[12];
176
174
  const sY = m[1] * x + m[5] * y + m[13];
@@ -181,32 +179,27 @@ function projectExtrusion(geometry, zBase, zTop, m) {
181
179
  const baseY = sY + baseYZ;
182
180
  const baseZ = sZ + baseZZ;
183
181
  const baseW = sW + baseWZ;
182
+ ringBase[j] = { x: baseX / baseW, y: baseY / baseW, z: baseZ / baseW };
184
183
 
185
184
  const topX = sX + topXZ;
186
185
  const topY = sY + topYZ;
187
186
  const topZ = sZ + topZZ;
188
187
  const topW = sW + topWZ;
189
-
190
- const b = new Point(baseX / baseW, baseY / baseW);
191
- b.z = baseZ / baseW;
192
- ringBase.push(b);
193
-
194
- const t = new Point(topX / topW, topY / topW);
195
- t.z = topZ / topW;
196
- ringTop.push(t);
188
+ ringTop[j] = { x: topX / topW, y: topY / topW, z: topZ / topW };
197
189
  }
198
- projectedBase.push(ringBase);
199
- projectedTop.push(ringTop);
190
+ projectedBase[i] = ringBase;
191
+ projectedTop[i] = ringTop;
200
192
  }
201
- return [projectedBase, projectedTop];
193
+ return { projectedBase, projectedTop };
202
194
  }
203
195
 
204
- function projectQueryGeometry(queryGeometry, pixelPosMatrix, transform, z) {
205
- const projectedQueryGeometry = [];
206
- for (const p of queryGeometry) {
207
- const v = [p.x, p.y, z, 1];
196
+ function projectQueryGeometry(queryGeometry, pixelPosMatrix, z) {
197
+ const projectedQueryGeometry = new Array(queryGeometry.length);
198
+ for (let i = 0; i < queryGeometry.length; i++) {
199
+ const { x, y } = queryGeometry[i];
200
+ const v = [x, y, z, 1];
208
201
  vec4.transformMat4(v, v, pixelPosMatrix);
209
- projectedQueryGeometry.push(new Point(v[0] / v[3], v[1] / v[3]));
202
+ projectedQueryGeometry[i] = { x: v[0] / v[3], y: v[1] / v[3] };
210
203
  }
211
204
  return projectedQueryGeometry;
212
205
  }
@@ -1,9 +1,10 @@
1
1
  import HeatmapBucket from '../../data/bucket/heatmap_bucket.js';
2
2
  import renderColorRamp from '../../util/color_ramp.js';
3
+ import { circleIntersection, getMaximumPaintValue } from '../query_utils.js';
3
4
  import StyleLayer from '../style_layer.js';
4
5
  import properties from './heatmap_style_layer_properties.js';
5
6
 
6
- class HeatmapStyleLayer extends StyleLayer {
7
+ export default class HeatmapStyleLayer extends StyleLayer {
7
8
  createBucket(options) {
8
9
  return new HeatmapBucket(options);
9
10
  }
@@ -34,17 +35,30 @@ class HeatmapStyleLayer extends StyleLayer {
34
35
  }
35
36
  }
36
37
 
37
- queryRadius() {
38
- return 0;
38
+ queryRadius(bucket) {
39
+ return getMaximumPaintValue('heatmap-radius', this, bucket);
39
40
  }
40
41
 
41
- queryIntersectsFeature() {
42
- return false;
42
+ queryIntersectsFeature(
43
+ queryGeometry,
44
+ feature,
45
+ featureState,
46
+ geometry,
47
+ zoom,
48
+ transform,
49
+ pixelsToTileUnits,
50
+ pixelPosMatrix
51
+ ) {
52
+ return circleIntersection({
53
+ queryGeometry,
54
+ geometry,
55
+ pixelPosMatrix,
56
+ size: this._paint.get('heatmap-radius').evaluate(feature, featureState) * pixelsToTileUnits,
57
+ transform
58
+ });
43
59
  }
44
60
 
45
61
  hasOffscreenPass() {
46
62
  return this._paint.get('heatmap-opacity') !== 0 && !this.isHidden();
47
63
  }
48
64
  }
49
-
50
- export default HeatmapStyleLayer;
@@ -1,5 +1,5 @@
1
- import Point from '@mapbox/point-geometry';
2
1
  import { polygonIntersectsBufferedMultiLine } from '@mapwhit/geometry';
2
+ import { add, clone, div, mult, perp, sub, unit } from '@mapwhit/point-geometry';
3
3
  import LineBucket from '../../data/bucket/line_bucket.js';
4
4
  import renderColorRamp from '../../util/color_ramp.js';
5
5
  import EvaluationParameters from '../evaluation_parameters.js';
@@ -111,22 +111,22 @@ function offsetLine(rings, offset) {
111
111
  newRings[k] = newRing;
112
112
 
113
113
  let b = ring[0];
114
- let aToB = new Point(0, 0);
114
+ let aToB = { x: 0, y: 0 };
115
115
  for (let i = 0; i < ring.length - 1; i++) {
116
116
  const c = ring[i + 1];
117
- const bToC = c.sub(b)._unit()._perp();
118
- const extrude = aToB._add(bToC)._unit();
117
+ const bToC = perp(unit(sub(clone(c), b)));
118
+ const extrude = unit(add(aToB, bToC));
119
119
  const cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y;
120
120
  if (cosHalfAngle !== 0) {
121
- extrude._div(cosHalfAngle);
121
+ div(extrude, cosHalfAngle);
122
122
  }
123
- newRing[i] = extrude._mult(offset)._add(b);
123
+ newRing[i] = add(mult(extrude, offset), b);
124
124
 
125
125
  b = c;
126
126
  aToB = bToC;
127
127
  }
128
128
 
129
- newRing[ring.length - 1] = aToB._unit()._mult(offset)._add(b);
129
+ newRing[ring.length - 1] = add(mult(unit(aToB), offset), b);
130
130
  }
131
131
  return newRings;
132
132
  }
@@ -35,10 +35,10 @@ class StyleLayer extends Evented {
35
35
  this.source = layer.source;
36
36
  this['source-layer'] = this.sourceLayer = layer['source-layer'];
37
37
  this.filter = layer.filter;
38
- this._featureFilter = featureFilter(layer.filter);
38
+ this._featureFilter = featureFilter(layer.filter, globalState);
39
39
  }
40
40
 
41
- this._featureFilter ??= featureFilter.addGlobalStateRefs(() => true);
41
+ this._featureFilter ??= featureFilter(undefined, globalState);
42
42
 
43
43
  if (properties.layout) {
44
44
  this._unevaluatedLayout = new Layout(properties.layout, globalState);
@@ -59,7 +59,7 @@ class StyleLayer extends Evented {
59
59
  setFilter(filter) {
60
60
  this.#key = undefined;
61
61
  this.filter = filter;
62
- this._featureFilter = featureFilter(filter);
62
+ this._featureFilter.setValue(filter);
63
63
  }
64
64
 
65
65
  _setZoomRange(minzoom, maxzoom) {