@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.
- package/build/min/package.json +1 -1
- package/package.json +4 -4
- package/src/data/bucket/fill_extrusion_bucket.js +34 -39
- package/src/data/bucket/line_bucket.js +40 -48
- package/src/data/bucket/symbol_bucket.js +4 -5
- package/src/data/feature_index.js +10 -3
- package/src/geo/transform.js +13 -13
- package/src/index.js +1 -1
- package/src/source/geojson_wrapper.js +1 -9
- package/src/source/query_features.js +4 -1
- package/src/source/rtl_text_plugin.js +3 -1
- package/src/source/source_cache.js +3 -3
- package/src/source/tile.js +1 -1
- package/src/style/evaluation_parameters.js +5 -4
- package/src/style/query_utils.js +82 -7
- package/src/style/style.js +78 -25
- package/src/style/style_layer/circle_style_layer.js +26 -45
- package/src/style/style_layer/fill_extrusion_style_layer.js +26 -33
- package/src/style/style_layer/heatmap_style_layer.js +21 -7
- package/src/style/style_layer/line_style_layer.js +7 -7
- package/src/style/style_layer.js +3 -3
- package/src/style-spec/feature_filter/index.js +24 -19
- package/src/symbol/anchor.js +1 -1
- package/src/symbol/check_max_angle.js +6 -6
- package/src/symbol/collision_feature.js +9 -15
- package/src/symbol/collision_index.js +33 -27
- package/src/symbol/get_anchors.js +6 -5
- package/src/symbol/projection.js +3 -3
- package/src/symbol/quads.js +1 -1
- package/src/symbol/symbol_layout.js +1 -2
- package/src/ui/camera.js +1 -1
- package/src/ui/map.js +24 -24
- package/src/util/classify_rings.js +2 -4
- package/src/util/vectortile_to_geojson.js +65 -26
- package/src/symbol/clip_line.js +0 -72
package/src/style/query_utils.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
}
|
package/src/style/style.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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,
|
|
48
|
+
const projectedQueryGeometry = projectQueryGeometry(translatedPolygon, pixelPosMatrix, 0);
|
|
49
49
|
|
|
50
|
-
const
|
|
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 ||
|
|
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 =
|
|
88
|
-
const ac =
|
|
89
|
-
const ap =
|
|
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 (
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const 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
|
|
199
|
-
projectedTop
|
|
190
|
+
projectedBase[i] = ringBase;
|
|
191
|
+
projectedTop[i] = ringTop;
|
|
200
192
|
}
|
|
201
|
-
return
|
|
193
|
+
return { projectedBase, projectedTop };
|
|
202
194
|
}
|
|
203
195
|
|
|
204
|
-
function projectQueryGeometry(queryGeometry, pixelPosMatrix,
|
|
205
|
-
const projectedQueryGeometry =
|
|
206
|
-
for (
|
|
207
|
-
const
|
|
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
|
|
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
|
|
38
|
+
queryRadius(bucket) {
|
|
39
|
+
return getMaximumPaintValue('heatmap-radius', this, bucket);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
queryIntersectsFeature(
|
|
42
|
-
|
|
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 =
|
|
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 =
|
|
118
|
-
const extrude = aToB
|
|
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
|
|
121
|
+
div(extrude, cosHalfAngle);
|
|
122
122
|
}
|
|
123
|
-
newRing[i] = extrude
|
|
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
|
|
129
|
+
newRing[ring.length - 1] = add(mult(unit(aToB), offset), b);
|
|
130
130
|
}
|
|
131
131
|
return newRings;
|
|
132
132
|
}
|
package/src/style/style_layer.js
CHANGED
|
@@ -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
|
|
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
|
|
62
|
+
this._featureFilter.setValue(filter);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
_setZoomRange(minzoom, maxzoom) {
|