@mapwhit/tilerenderer 1.2.0 → 1.2.2

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 (36) hide show
  1. package/build/min/package.json +1 -1
  2. package/package.json +5 -5
  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 +8 -2
  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/build/min/src/shaders/.dir +0 -0
  36. package/src/symbol/clip_line.js +0 -72
@@ -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
- class CollisionIndex {
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 = new Point(symbol.anchorX, symbol.anchorY);
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 (const point of viewportQueryGeometry) {
262
- const gridPoint = new Point(point.x + viewportPadding, point.y + viewportPadding);
263
- minX = Math.min(minX, gridPoint.x);
264
- minY = Math.min(minY, gridPoint.y);
265
- maxX = Math.max(maxX, gridPoint.x);
266
- maxY = Math.max(maxY, gridPoint.y);
267
- query.push(gridPoint);
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
- new Point(feature.x1, feature.y1),
292
- new Point(feature.x2, feature.y1),
293
- new Point(feature.x2, feature.y2),
294
- new Point(feature.x1, feature.y2)
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 new Point(
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 = new Point(
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].dist(line[k + 1]);
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 = a.dist(b);
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, b.angleTo(a), i);
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 = a.dist(b);
121
- const angle = b.angleTo(a);
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;
@@ -1,5 +1,5 @@
1
1
  import glMatrix from '@mapbox/gl-matrix';
2
- import Point from '@mapbox/point-geometry';
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.add(previousTilePoint.sub(currentTilePoint)._unit()),
451
+ Point.clone(previousTilePoint)._add(Point.clone(previousTilePoint)._sub(currentTilePoint)._unit()),
452
452
  projectionMatrix
453
453
  ).point;
454
- const projectedUnitSegment = previousProjectedPoint.sub(projectedUnitVertex);
454
+ const projectedUnitSegment = Point.clone(previousProjectedPoint)._sub(projectedUnitVertex);
455
455
 
456
456
  return previousProjectedPoint.add(projectedUnitSegment._mult(minimumLength / projectedUnitSegment.mag()));
457
457
  }
@@ -1,4 +1,4 @@
1
- import Point from '@mapbox/point-geometry';
1
+ import { Point } from '@mapwhit/point-geometry';
2
2
  import { GLYPH_PBF_BORDER } from '../style/parse_glyph_pbf.js';
3
3
 
4
4
  /**
@@ -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(new Point(0, 0)))
291
- .extend(this.transform.pointLocation(new Point(this.transform.width, 0)))
292
- .extend(this.transform.pointLocation(new Point(this.transform.width, this.transform.height)))
293
- .extend(this.transform.pointLocation(new Point(0, this.transform.height)));
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(Point.convert(point));
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(pointOrBox) {
587
- if (pointOrBox === undefined) {
588
- // bounds was omitted: use full viewport
589
- pointOrBox = [Point.convert([0, 0]), Point.convert([this.transform.width, this.transform.height])];
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 instanceof Point || typeof pointOrBox[0] === 'number') {
595
- const point = Point.convert(pointOrBox);
596
- queryGeometry = [point];
589
+ if (typeof pointOrBox.x === 'number' || typeof pointOrBox[0] === 'number') {
590
+ queryGeometry = [toPoint(pointOrBox)];
597
591
  } else {
598
- const box = [Point.convert(pointOrBox[0]), Point.convert(pointOrBox[1])];
599
- queryGeometry = [box[0], new Point(box[1].x, box[0].y), box[1], new Point(box[0].x, box[1].y), 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
- if (ccw === undefined) {
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
- class Feature {
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._vectorTileFeature = vectorTileFeature;
6
- vectorTileFeature._z = z;
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
- if (this._geometry === undefined) {
19
- this._geometry = this._vectorTileFeature.toGeoJSON(
20
- this._vectorTileFeature._x,
21
- this._vectorTileFeature._y,
22
- this._vectorTileFeature._z
23
- ).geometry;
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
- return this._geometry;
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
- set geometry(g) {
29
- this._geometry = g;
65
+ if (coords.length === 1) {
66
+ coords = coords[0];
67
+ } else {
68
+ type = `Multi${type}`;
30
69
  }
31
70
 
32
- toJSON() {
33
- const json = {
34
- geometry: this.geometry
35
- };
36
- for (const i in this) {
37
- if (i === '_geometry' || i === '_vectorTileFeature') {
38
- continue;
39
- }
40
- json[i] = this[i];
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;
File without changes
@@ -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
- }