@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
|
@@ -64,29 +64,34 @@ const filterSpec = {
|
|
|
64
64
|
* @returns {Function} filter-evaluating function
|
|
65
65
|
*/
|
|
66
66
|
export default function createFilter(filter, globalState) {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
let evaluate;
|
|
68
|
+
const expression = (globalProperties, feature) => evaluate(globalProperties, feature);
|
|
69
|
+
expression.setValue = setValue;
|
|
70
|
+
setValue(filter);
|
|
71
|
+
return expression;
|
|
72
|
+
|
|
73
|
+
function setValue(filter) {
|
|
74
|
+
if (filter === null || filter === undefined) {
|
|
75
|
+
evaluate = () => true;
|
|
76
|
+
addGlobalStateRefs(expression);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!isExpressionFilter(filter)) {
|
|
81
|
+
filter = convertFilter(filter);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const compiled = createExpression(filter, filterSpec, globalState);
|
|
85
|
+
if (compiled.result === 'error') {
|
|
86
|
+
throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', '));
|
|
87
|
+
}
|
|
88
|
+
evaluate = (globalProperties, feature) => compiled.value.evaluate(globalProperties, feature);
|
|
89
|
+
addGlobalStateRefs(expression, () => findGlobalStateRefs(compiled.value.expression));
|
|
69
90
|
}
|
|
70
|
-
|
|
71
|
-
if (!isExpressionFilter(filter)) {
|
|
72
|
-
filter = convertFilter(filter);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const compiled = createExpression(filter, filterSpec, globalState);
|
|
76
|
-
if (compiled.result === 'error') {
|
|
77
|
-
throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', '));
|
|
78
|
-
}
|
|
79
|
-
return Object.assign(
|
|
80
|
-
addGlobalStateRefs(
|
|
81
|
-
(globalProperties, feature) => compiled.value.evaluate(globalProperties, feature),
|
|
82
|
-
() => findGlobalStateRefs(compiled.value.expression)
|
|
83
|
-
)
|
|
84
|
-
);
|
|
85
91
|
}
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
function addGlobalStateRefs(filter, getGlobalStateRefs = () => new Set()) {
|
|
88
94
|
filter.getGlobalStateRefs = getGlobalStateRefs;
|
|
89
|
-
return filter;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
// Comparison function to sort numbers and strings
|
package/src/symbol/anchor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { angleTo, dist } from '@mapwhit/point-geometry';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Labels placed around really sharp angles aren't readable. Check if any
|
|
@@ -13,7 +13,7 @@ export default checkMaxAngle;
|
|
|
13
13
|
* @returns {boolean} whether the label should be placed
|
|
14
14
|
* @private
|
|
15
15
|
*/
|
|
16
|
-
function checkMaxAngle(line, anchor, labelLength, windowSize, maxAngle) {
|
|
16
|
+
export default function checkMaxAngle(line, anchor, labelLength, windowSize, maxAngle) {
|
|
17
17
|
// horizontal labels always pass
|
|
18
18
|
if (anchor.segment === undefined) {
|
|
19
19
|
return true;
|
|
@@ -32,11 +32,11 @@ function checkMaxAngle(line, anchor, labelLength, windowSize, maxAngle) {
|
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
anchorDistance -= line[index]
|
|
35
|
+
anchorDistance -= dist(line[index], p);
|
|
36
36
|
p = line[index];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
anchorDistance += line[index]
|
|
39
|
+
anchorDistance += dist(line[index], line[index + 1]);
|
|
40
40
|
index++;
|
|
41
41
|
|
|
42
42
|
// store recent corners and their total angle difference
|
|
@@ -54,7 +54,7 @@ function checkMaxAngle(line, anchor, labelLength, windowSize, maxAngle) {
|
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
let angleDelta =
|
|
57
|
+
let angleDelta = angleTo(prev, current) - angleTo(current, next);
|
|
58
58
|
// restrict angle to -pi..pi range
|
|
59
59
|
angleDelta = Math.abs(((angleDelta + 3 * Math.PI) % (Math.PI * 2)) - Math.PI);
|
|
60
60
|
|
|
@@ -75,7 +75,7 @@ function checkMaxAngle(line, anchor, labelLength, windowSize, maxAngle) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
index++;
|
|
78
|
-
anchorDistance +=
|
|
78
|
+
anchorDistance += dist(current, next);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// no part of the line had an angle greater than the maximum allowed. check passes.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Point from '@
|
|
1
|
+
import { dist, Point, rotate as rotatePoint } from '@mapwhit/point-geometry';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A CollisionFeature represents the area of the tile covered by a single label.
|
|
@@ -67,17 +67,11 @@ class CollisionFeature {
|
|
|
67
67
|
// See https://github.com/mapbox/mapbox-gl-js/issues/6075
|
|
68
68
|
// Doesn't account for icon-text-fit
|
|
69
69
|
|
|
70
|
-
const tl = new Point(x1, y1);
|
|
71
|
-
const tr = new Point(x2, y1);
|
|
72
|
-
const bl = new Point(x1, y2);
|
|
73
|
-
const br = new Point(x2, y2);
|
|
74
|
-
|
|
75
70
|
const rotateRadians = (rotate * Math.PI) / 180;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
br._rotate(rotateRadians);
|
|
71
|
+
const tl = rotatePoint({ x: x1, y: y1 }, rotateRadians);
|
|
72
|
+
const tr = rotatePoint({ x: x2, y: y1 }, rotateRadians);
|
|
73
|
+
const bl = rotatePoint({ x: x1, y: y2 }, rotateRadians);
|
|
74
|
+
const br = rotatePoint({ x: x2, y: y2 }, rotateRadians);
|
|
81
75
|
|
|
82
76
|
// Collision features require an "on-axis" geometry,
|
|
83
77
|
// so take the envelope of the rotated geometry
|
|
@@ -162,11 +156,11 @@ class CollisionFeature {
|
|
|
162
156
|
index = 0;
|
|
163
157
|
break;
|
|
164
158
|
}
|
|
165
|
-
anchorDistance -= line[index]
|
|
159
|
+
anchorDistance -= dist(line[index], p);
|
|
166
160
|
p = line[index];
|
|
167
161
|
} while (anchorDistance > paddingStartDistance);
|
|
168
162
|
|
|
169
|
-
let segmentLength = line[index]
|
|
163
|
+
let segmentLength = dist(line[index], line[index + 1]);
|
|
170
164
|
|
|
171
165
|
for (let i = -nPitchPaddingBoxes; i < nBoxes + nPitchPaddingBoxes; i++) {
|
|
172
166
|
// the distance the box will be from the anchor
|
|
@@ -197,7 +191,7 @@ class CollisionFeature {
|
|
|
197
191
|
return;
|
|
198
192
|
}
|
|
199
193
|
|
|
200
|
-
segmentLength = line[index]
|
|
194
|
+
segmentLength = dist(line[index], line[index + 1]);
|
|
201
195
|
}
|
|
202
196
|
|
|
203
197
|
// the distance the box will be from the beginning of the segment
|
|
@@ -205,7 +199,7 @@ class CollisionFeature {
|
|
|
205
199
|
|
|
206
200
|
const p0 = line[index];
|
|
207
201
|
const p1 = line[index + 1];
|
|
208
|
-
const boxAnchorPoint = p1.
|
|
202
|
+
const boxAnchorPoint = Point.clone(p1)._sub(p0)._unit()._mult(segmentBoxDistance)._add(p0)._round();
|
|
209
203
|
|
|
210
204
|
// If the box is within boxSize of the anchor, force the box to be used
|
|
211
205
|
// (so even 0-width labels use at least one box)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Point from '@mapbox/point-geometry';
|
|
2
1
|
import { polygonIntersectsPolygon } from '@mapwhit/geometry';
|
|
3
2
|
import * as projection from '../symbol/projection.js';
|
|
4
3
|
import Grid from './grid_index.js';
|
|
@@ -20,10 +19,9 @@ const viewportPadding = 100;
|
|
|
20
19
|
* there's room for a symbol, then insertCollisionBox/Circles actually puts the
|
|
21
20
|
* symbol in the index. The two step process allows paired symbols to be inserted
|
|
22
21
|
* together even if they overlap.
|
|
23
|
-
*
|
|
24
|
-
* @private
|
|
25
22
|
*/
|
|
26
|
-
|
|
23
|
+
|
|
24
|
+
export default class CollisionIndex {
|
|
27
25
|
constructor(
|
|
28
26
|
transform,
|
|
29
27
|
grid = new Grid(transform.width + 2 * viewportPadding, transform.height + 2 * viewportPadding, 25),
|
|
@@ -116,7 +114,7 @@ class CollisionIndex {
|
|
|
116
114
|
const lineOffsetX = symbol.lineOffsetX * fontSize;
|
|
117
115
|
const lineOffsetY = symbol.lineOffsetY * fontSize;
|
|
118
116
|
|
|
119
|
-
const tileUnitAnchorPoint =
|
|
117
|
+
const tileUnitAnchorPoint = { x: symbol.anchorX, y: symbol.anchorY };
|
|
120
118
|
// projection.project generates NDC coordinates, as opposed to the
|
|
121
119
|
// pixel-based grid coordinates generated by this.projectPoint
|
|
122
120
|
const labelPlaneAnchorPoint = projection.project(tileUnitAnchorPoint, labelPlaneMatrix).point;
|
|
@@ -253,18 +251,28 @@ class CollisionIndex {
|
|
|
253
251
|
return {};
|
|
254
252
|
}
|
|
255
253
|
|
|
256
|
-
const query =
|
|
254
|
+
const query = new Array(viewportQueryGeometry.length);
|
|
257
255
|
let minX = Number.POSITIVE_INFINITY;
|
|
258
256
|
let minY = Number.POSITIVE_INFINITY;
|
|
259
257
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
260
258
|
let maxY = Number.NEGATIVE_INFINITY;
|
|
261
|
-
for (
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
259
|
+
for (let i = 0; i < viewportQueryGeometry.length; i++) {
|
|
260
|
+
let { x, y } = viewportQueryGeometry[i];
|
|
261
|
+
x += viewportPadding;
|
|
262
|
+
y += viewportPadding;
|
|
263
|
+
if (x < minX) {
|
|
264
|
+
minX = x;
|
|
265
|
+
}
|
|
266
|
+
if (x > maxX) {
|
|
267
|
+
maxX = x;
|
|
268
|
+
}
|
|
269
|
+
if (y < minY) {
|
|
270
|
+
minY = y;
|
|
271
|
+
}
|
|
272
|
+
if (y > maxY) {
|
|
273
|
+
maxY = y;
|
|
274
|
+
}
|
|
275
|
+
query[i] = { x, y };
|
|
268
276
|
}
|
|
269
277
|
|
|
270
278
|
const features = this.grid.query(minX, minY, maxX, maxY).concat(this.ignoredGrid.query(minX, minY, maxX, maxY));
|
|
@@ -288,10 +296,10 @@ class CollisionIndex {
|
|
|
288
296
|
// distinction doesn't matter as much, and box geometry is easier
|
|
289
297
|
// to work with.
|
|
290
298
|
const bbox = [
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
299
|
+
{ x: feature.x1, y: feature.y1 },
|
|
300
|
+
{ x: feature.x2, y: feature.y1 },
|
|
301
|
+
{ x: feature.x2, y: feature.y2 },
|
|
302
|
+
{ x: feature.x1, y: feature.y2 }
|
|
295
303
|
];
|
|
296
304
|
if (!polygonIntersectsPolygon(query, bbox)) {
|
|
297
305
|
continue;
|
|
@@ -343,19 +351,19 @@ class CollisionIndex {
|
|
|
343
351
|
projectPoint(posMatrix, x, y) {
|
|
344
352
|
const p = [x, y, 0, 1];
|
|
345
353
|
projection.xyTransformMat4(p, p, posMatrix);
|
|
346
|
-
return
|
|
347
|
-
((p[0] / p[3] + 1) / 2) * this.transform.width + viewportPadding,
|
|
348
|
-
((-p[1] / p[3] + 1) / 2) * this.transform.height + viewportPadding
|
|
349
|
-
|
|
354
|
+
return {
|
|
355
|
+
x: ((p[0] / p[3] + 1) / 2) * this.transform.width + viewportPadding,
|
|
356
|
+
y: ((-p[1] / p[3] + 1) / 2) * this.transform.height + viewportPadding
|
|
357
|
+
};
|
|
350
358
|
}
|
|
351
359
|
|
|
352
360
|
projectAndGetPerspectiveRatio(posMatrix, x, y) {
|
|
353
361
|
const p = [x, y, 0, 1];
|
|
354
362
|
projection.xyTransformMat4(p, p, posMatrix);
|
|
355
|
-
const a =
|
|
356
|
-
((p[0] / p[3] + 1) / 2) * this.transform.width + viewportPadding,
|
|
357
|
-
((-p[1] / p[3] + 1) / 2) * this.transform.height + viewportPadding
|
|
358
|
-
|
|
363
|
+
const a = {
|
|
364
|
+
x: ((p[0] / p[3] + 1) / 2) * this.transform.width + viewportPadding,
|
|
365
|
+
y: ((-p[1] / p[3] + 1) / 2) * this.transform.height + viewportPadding
|
|
366
|
+
};
|
|
359
367
|
return {
|
|
360
368
|
point: a,
|
|
361
369
|
// See perspective ratio comment in symbol_sdf.vertex
|
|
@@ -379,5 +387,3 @@ class CollisionIndex {
|
|
|
379
387
|
function markCollisionCircleUsed(collisionCircles, index, used) {
|
|
380
388
|
collisionCircles[index + 4] = used ? 1 : 0;
|
|
381
389
|
}
|
|
382
|
-
|
|
383
|
-
export default CollisionIndex;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { angleTo, dist } from '@mapwhit/point-geometry';
|
|
1
2
|
import Anchor from '../symbol/anchor.js';
|
|
2
3
|
import interpolate from '../util/interpolate.js';
|
|
3
4
|
import checkMaxAngle from './check_max_angle.js';
|
|
@@ -5,7 +6,7 @@ import checkMaxAngle from './check_max_angle.js';
|
|
|
5
6
|
function getLineLength(line) {
|
|
6
7
|
let lineLength = 0;
|
|
7
8
|
for (let k = 0; k < line.length - 1; k++) {
|
|
8
|
-
lineLength += line[k]
|
|
9
|
+
lineLength += dist(line[k], line[k + 1]);
|
|
9
10
|
}
|
|
10
11
|
return lineLength;
|
|
11
12
|
}
|
|
@@ -32,7 +33,7 @@ export function getCenterAnchor(line, maxAngle, shapedText, shapedIcon, glyphSiz
|
|
|
32
33
|
const a = line[i];
|
|
33
34
|
const b = line[i + 1];
|
|
34
35
|
|
|
35
|
-
const segmentDistance =
|
|
36
|
+
const segmentDistance = dist(a, b);
|
|
36
37
|
|
|
37
38
|
if (prevDistance + segmentDistance > centerDistance) {
|
|
38
39
|
// The center is on this segment
|
|
@@ -40,7 +41,7 @@ export function getCenterAnchor(line, maxAngle, shapedText, shapedIcon, glyphSiz
|
|
|
40
41
|
const x = interpolate(a.x, b.x, t);
|
|
41
42
|
const y = interpolate(a.y, b.y, t);
|
|
42
43
|
|
|
43
|
-
const anchor = new Anchor(x, y,
|
|
44
|
+
const anchor = new Anchor(x, y, angleTo(b, a), i);
|
|
44
45
|
anchor._round();
|
|
45
46
|
if (!angleWindowSize || checkMaxAngle(line, anchor, labelLength, angleWindowSize, maxAngle)) {
|
|
46
47
|
return anchor;
|
|
@@ -117,8 +118,8 @@ function resample(
|
|
|
117
118
|
const a = line[i];
|
|
118
119
|
const b = line[i + 1];
|
|
119
120
|
|
|
120
|
-
const segmentDist =
|
|
121
|
-
const angle =
|
|
121
|
+
const segmentDist = dist(a, b);
|
|
122
|
+
const angle = angleTo(b, a);
|
|
122
123
|
|
|
123
124
|
while (markedDistance + spacing < distance + segmentDist) {
|
|
124
125
|
markedDistance += spacing;
|
package/src/symbol/projection.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import glMatrix from '@mapbox/gl-matrix';
|
|
2
|
-
import Point from '@
|
|
2
|
+
import { Point } from '@mapwhit/point-geometry';
|
|
3
3
|
import { addDynamicAttributes } from '../data/bucket/symbol_bucket.js';
|
|
4
4
|
import properties from '../style/style_layer/symbol_style_layer_properties.js';
|
|
5
5
|
import { WritingMode } from '../symbol/shaping.js';
|
|
@@ -448,10 +448,10 @@ function projectTruncatedLineSegment(
|
|
|
448
448
|
// point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the
|
|
449
449
|
// plane of the camera.
|
|
450
450
|
const projectedUnitVertex = project(
|
|
451
|
-
previousTilePoint.
|
|
451
|
+
Point.clone(previousTilePoint)._add(Point.clone(previousTilePoint)._sub(currentTilePoint)._unit()),
|
|
452
452
|
projectionMatrix
|
|
453
453
|
).point;
|
|
454
|
-
const projectedUnitSegment = previousProjectedPoint.
|
|
454
|
+
const projectedUnitSegment = Point.clone(previousProjectedPoint)._sub(projectedUnitVertex);
|
|
455
455
|
|
|
456
456
|
return previousProjectedPoint.add(projectedUnitSegment._mult(minimumLength / projectedUnitSegment.mag()));
|
|
457
457
|
}
|
package/src/symbol/quads.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findPoleOfInaccessibility } from '@mapwhit/geometry';
|
|
1
|
+
import { clipLine, findPoleOfInaccessibility } from '@mapwhit/geometry';
|
|
2
2
|
import murmur3 from 'murmurhash-js';
|
|
3
3
|
import SymbolBucket from '../data/bucket/symbol_bucket.js';
|
|
4
4
|
import EXTENT from '../data/extent.js';
|
|
@@ -7,7 +7,6 @@ import classifyRings from '../util/classify_rings.js';
|
|
|
7
7
|
import { allowsLetterSpacing, allowsVerticalWritingMode } from '../util/script_detection.js';
|
|
8
8
|
import warn from '../util/warn.js';
|
|
9
9
|
import Anchor from './anchor.js';
|
|
10
|
-
import clipLine from './clip_line.js';
|
|
11
10
|
import CollisionFeature from './collision_feature.js';
|
|
12
11
|
import { getAnchors, getCenterAnchor } from './get_anchors.js';
|
|
13
12
|
import { getGlyphQuads, getIconQuads } from './quads.js';
|
package/src/ui/camera.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Point from '@mapbox/point-geometry';
|
|
2
1
|
import { Event, Evented } from '@mapwhit/events';
|
|
2
|
+
import { Point } from '@mapwhit/point-geometry';
|
|
3
3
|
import LngLat from '../geo/lng_lat.js';
|
|
4
4
|
import LngLatBounds from '../geo/lng_lat_bounds.js';
|
|
5
5
|
import browser from '../util/browser.js';
|
package/src/ui/map.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Point from '@mapbox/point-geometry';
|
|
2
1
|
import { ErrorEvent, Event } from '@mapwhit/events';
|
|
3
2
|
import LngLat from '../geo/lng_lat.js';
|
|
4
3
|
import LngLatBounds from '../geo/lng_lat_bounds.js';
|
|
@@ -287,10 +286,10 @@ class Map extends Camera {
|
|
|
287
286
|
*/
|
|
288
287
|
getBounds() {
|
|
289
288
|
return new LngLatBounds()
|
|
290
|
-
.extend(this.transform.pointLocation(
|
|
291
|
-
.extend(this.transform.pointLocation(
|
|
292
|
-
.extend(this.transform.pointLocation(
|
|
293
|
-
.extend(this.transform.pointLocation(
|
|
289
|
+
.extend(this.transform.pointLocation({ x: 0, y: 0 }))
|
|
290
|
+
.extend(this.transform.pointLocation({ x: this.transform.width, y: 0 }))
|
|
291
|
+
.extend(this.transform.pointLocation({ x: this.transform.width, y: this.transform.height }))
|
|
292
|
+
.extend(this.transform.pointLocation({ x: 0, y: this.transform.height }));
|
|
294
293
|
}
|
|
295
294
|
|
|
296
295
|
/**
|
|
@@ -453,7 +452,7 @@ class Map extends Camera {
|
|
|
453
452
|
* @see [Show polygon information on click](https://www.mapbox.com/mapbox-gl-js/example/polygon-popup-on-click/)
|
|
454
453
|
*/
|
|
455
454
|
unproject(point) {
|
|
456
|
-
return this.transform.pointLocation(
|
|
455
|
+
return this.transform.pointLocation(toPoint(point));
|
|
457
456
|
}
|
|
458
457
|
|
|
459
458
|
/**
|
|
@@ -577,33 +576,26 @@ class Map extends Camera {
|
|
|
577
576
|
}
|
|
578
577
|
|
|
579
578
|
return this.style.queryRenderedFeatures(this._makeQueryGeometry(geometry), options, this.transform) || [];
|
|
580
|
-
|
|
581
|
-
function isPointLike(input) {
|
|
582
|
-
return input instanceof Point || Array.isArray(input);
|
|
583
|
-
}
|
|
584
579
|
}
|
|
585
580
|
|
|
586
|
-
_makeQueryGeometry(
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
581
|
+
_makeQueryGeometry(
|
|
582
|
+
pointOrBox = [
|
|
583
|
+
{ x: 0, y: 0 },
|
|
584
|
+
{ x: this.transform.width, y: this.transform.height }
|
|
585
|
+
]
|
|
586
|
+
) {
|
|
592
587
|
let queryGeometry;
|
|
593
588
|
|
|
594
|
-
if (pointOrBox
|
|
595
|
-
|
|
596
|
-
queryGeometry = [point];
|
|
589
|
+
if (typeof pointOrBox.x === 'number' || typeof pointOrBox[0] === 'number') {
|
|
590
|
+
queryGeometry = [toPoint(pointOrBox)];
|
|
597
591
|
} else {
|
|
598
|
-
const box = [
|
|
599
|
-
queryGeometry = [box[0],
|
|
592
|
+
const box = [toPoint(pointOrBox[0]), toPoint(pointOrBox[1])];
|
|
593
|
+
queryGeometry = [box[0], { x: box[1].x, y: box[0].y }, box[1], { x: box[0].x, y: box[1].y }, box[0]];
|
|
600
594
|
}
|
|
601
595
|
|
|
602
596
|
return {
|
|
603
597
|
viewport: queryGeometry,
|
|
604
|
-
worldCoordinate: queryGeometry.map(p =>
|
|
605
|
-
return this.transform.pointCoordinate(p);
|
|
606
|
-
})
|
|
598
|
+
worldCoordinate: queryGeometry.map(p => this.transform.pointCoordinate(p))
|
|
607
599
|
};
|
|
608
600
|
}
|
|
609
601
|
|
|
@@ -1558,6 +1550,14 @@ function removeNode(node) {
|
|
|
1558
1550
|
}
|
|
1559
1551
|
}
|
|
1560
1552
|
|
|
1553
|
+
function toPoint(p) {
|
|
1554
|
+
return Array.isArray(p) ? { x: p[0], y: p[1] } : p;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
function isPointLike(input) {
|
|
1558
|
+
return Array.isArray(input) || (typeof input.x === 'number' && typeof input.y === 'number');
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
1561
|
/**
|
|
1562
1562
|
* Interface for interactive controls added to the map. This is an
|
|
1563
1563
|
* specification for implementers to model: it is not
|
|
@@ -2,7 +2,7 @@ import { calculateSignedArea } from '@mapwhit/geometry';
|
|
|
2
2
|
import quickselect from 'quickselect';
|
|
3
3
|
|
|
4
4
|
// classifies an array of rings into polygons with outer rings and holes
|
|
5
|
-
export default function classifyRings(rings, maxRings) {
|
|
5
|
+
export default function classifyRings(rings, maxRings = -1) {
|
|
6
6
|
if (rings.length <= 1) {
|
|
7
7
|
return [rings];
|
|
8
8
|
}
|
|
@@ -19,9 +19,7 @@ export default function classifyRings(rings, maxRings) {
|
|
|
19
19
|
|
|
20
20
|
ring.area = Math.abs(area);
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
ccw = area < 0;
|
|
24
|
-
}
|
|
22
|
+
ccw ??= area < 0;
|
|
25
23
|
|
|
26
24
|
if (ccw === area < 0) {
|
|
27
25
|
append(polygon);
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { VectorTileFeature } from '@mapwhit/vector-tile';
|
|
2
|
+
import classifyRings from './classify_rings.js';
|
|
3
|
+
|
|
4
|
+
export default class GeoJSONFeature {
|
|
5
|
+
#vectorTileFeature;
|
|
6
|
+
#geometry;
|
|
7
|
+
#xyz;
|
|
8
|
+
|
|
2
9
|
constructor(vectorTileFeature, z, x, y) {
|
|
3
10
|
this.type = 'Feature';
|
|
4
11
|
|
|
5
|
-
this
|
|
6
|
-
|
|
7
|
-
vectorTileFeature._x = x;
|
|
8
|
-
vectorTileFeature._y = y;
|
|
12
|
+
this.#vectorTileFeature = vectorTileFeature;
|
|
13
|
+
this.#xyz = { z, x, y };
|
|
9
14
|
|
|
10
15
|
this.properties = vectorTileFeature.properties;
|
|
11
16
|
|
|
@@ -15,32 +20,66 @@ class Feature {
|
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
get geometry() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
this.#geometry ??= toGeoJSONGeometry(this.#vectorTileFeature, this.#xyz);
|
|
24
|
+
return this.#geometry;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const invPi = 360 / Math.PI;
|
|
29
|
+
|
|
30
|
+
function toGeoJSONGeometry(vtf, { x, y, z }) {
|
|
31
|
+
const size = vtf.extent * 2 ** z;
|
|
32
|
+
const scale = 360 / size;
|
|
33
|
+
const x0 = vtf.extent * x;
|
|
34
|
+
const y0 = vtf.extent * y;
|
|
35
|
+
let coords = vtf.loadGeometry();
|
|
36
|
+
let type = VectorTileFeature.types[vtf.type];
|
|
37
|
+
|
|
38
|
+
switch (vtf.type) {
|
|
39
|
+
case 1: {
|
|
40
|
+
const points = new Array(coords.length);
|
|
41
|
+
for (let i = 0; i < coords.length; i++) {
|
|
42
|
+
points[i] = coords[i][0];
|
|
43
|
+
}
|
|
44
|
+
coords = points;
|
|
45
|
+
project(coords);
|
|
46
|
+
break;
|
|
24
47
|
}
|
|
25
|
-
|
|
48
|
+
|
|
49
|
+
case 2:
|
|
50
|
+
for (let i = 0; i < coords.length; i++) {
|
|
51
|
+
project(coords[i]);
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
|
|
55
|
+
case 3:
|
|
56
|
+
coords = classifyRings(coords);
|
|
57
|
+
for (let i = 0; i < coords.length; i++) {
|
|
58
|
+
for (let j = 0; j < coords[i].length; j++) {
|
|
59
|
+
project(coords[i][j]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
26
63
|
}
|
|
27
64
|
|
|
28
|
-
|
|
29
|
-
|
|
65
|
+
if (coords.length === 1) {
|
|
66
|
+
coords = coords[0];
|
|
67
|
+
} else {
|
|
68
|
+
type = `Multi${type}`;
|
|
30
69
|
}
|
|
31
70
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
71
|
+
return {
|
|
72
|
+
type,
|
|
73
|
+
coordinates: coords
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function project(line) {
|
|
77
|
+
for (let i = 0; i < line.length; i++) {
|
|
78
|
+
const { x, y } = line[i];
|
|
79
|
+
const lon = (x + x0) * scale - 180;
|
|
80
|
+
const y2 = 180 - (y + y0) * scale;
|
|
81
|
+
const lat = invPi * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;
|
|
82
|
+
line[i] = [lon, lat];
|
|
41
83
|
}
|
|
42
|
-
return json;
|
|
43
84
|
}
|
|
44
85
|
}
|
|
45
|
-
|
|
46
|
-
export default Feature;
|
package/src/symbol/clip_line.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import Point from '@mapbox/point-geometry';
|
|
2
|
-
export default clipLine;
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns the part of a multiline that intersects with the provided rectangular box.
|
|
6
|
-
*
|
|
7
|
-
* @param lines
|
|
8
|
-
* @param x1 the left edge of the box
|
|
9
|
-
* @param y1 the top edge of the box
|
|
10
|
-
* @param x2 the right edge of the box
|
|
11
|
-
* @param y2 the bottom edge of the box
|
|
12
|
-
* @returns lines
|
|
13
|
-
* @private
|
|
14
|
-
*/
|
|
15
|
-
function clipLine(lines, x1, y1, x2, y2) {
|
|
16
|
-
const clippedLines = [];
|
|
17
|
-
|
|
18
|
-
for (let l = 0; l < lines.length; l++) {
|
|
19
|
-
const line = lines[l];
|
|
20
|
-
let clippedLine;
|
|
21
|
-
|
|
22
|
-
for (let i = 0; i < line.length - 1; i++) {
|
|
23
|
-
let p0 = line[i];
|
|
24
|
-
let p1 = line[i + 1];
|
|
25
|
-
|
|
26
|
-
if (p0.x < x1 && p1.x < x1) {
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (p0.x < x1) {
|
|
30
|
-
p0 = new Point(x1, p0.y + (p1.y - p0.y) * ((x1 - p0.x) / (p1.x - p0.x)))._round();
|
|
31
|
-
} else if (p1.x < x1) {
|
|
32
|
-
p1 = new Point(x1, p0.y + (p1.y - p0.y) * ((x1 - p0.x) / (p1.x - p0.x)))._round();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (p0.y < y1 && p1.y < y1) {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (p0.y < y1) {
|
|
39
|
-
p0 = new Point(p0.x + (p1.x - p0.x) * ((y1 - p0.y) / (p1.y - p0.y)), y1)._round();
|
|
40
|
-
} else if (p1.y < y1) {
|
|
41
|
-
p1 = new Point(p0.x + (p1.x - p0.x) * ((y1 - p0.y) / (p1.y - p0.y)), y1)._round();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (p0.x >= x2 && p1.x >= x2) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (p0.x >= x2) {
|
|
48
|
-
p0 = new Point(x2, p0.y + (p1.y - p0.y) * ((x2 - p0.x) / (p1.x - p0.x)))._round();
|
|
49
|
-
} else if (p1.x >= x2) {
|
|
50
|
-
p1 = new Point(x2, p0.y + (p1.y - p0.y) * ((x2 - p0.x) / (p1.x - p0.x)))._round();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (p0.y >= y2 && p1.y >= y2) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (p0.y >= y2) {
|
|
57
|
-
p0 = new Point(p0.x + (p1.x - p0.x) * ((y2 - p0.y) / (p1.y - p0.y)), y2)._round();
|
|
58
|
-
} else if (p1.y >= y2) {
|
|
59
|
-
p1 = new Point(p0.x + (p1.x - p0.x) * ((y2 - p0.y) / (p1.y - p0.y)), y2)._round();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (!clippedLine || !p0.equals(clippedLine[clippedLine.length - 1])) {
|
|
63
|
-
clippedLine = [p0];
|
|
64
|
-
clippedLines.push(clippedLine);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
clippedLine.push(p1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return clippedLines;
|
|
72
|
-
}
|