@mapbox/mapbox-gl-style-spec 14.28.0 → 14.29.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/composite.ts +5 -5
- package/diff.ts +38 -40
- package/dist/index.cjs +362 -238
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +101 -13
- package/dist/index.es.js +362 -238
- package/dist/index.es.js.map +1 -1
- package/expression/compound_expression.ts +6 -6
- package/expression/definitions/assertion.ts +9 -12
- package/expression/definitions/at.ts +1 -1
- package/expression/definitions/at_interpolated.ts +1 -1
- package/expression/definitions/case.ts +1 -2
- package/expression/definitions/coalesce.ts +1 -1
- package/expression/definitions/coercion.ts +10 -12
- package/expression/definitions/collator.ts +8 -8
- package/expression/definitions/comparison.ts +0 -1
- package/expression/definitions/distance.ts +135 -73
- package/expression/definitions/format.ts +10 -13
- package/expression/definitions/image.ts +8 -8
- package/expression/definitions/index.ts +92 -90
- package/expression/definitions/interpolate.ts +18 -19
- package/expression/definitions/let.ts +1 -2
- package/expression/definitions/literal.ts +1 -1
- package/expression/definitions/match.ts +7 -8
- package/expression/definitions/number_format.ts +7 -8
- package/expression/definitions/step.ts +11 -11
- package/expression/definitions/within.ts +23 -24
- package/expression/evaluation_context.ts +4 -4
- package/expression/expression.ts +1 -1
- package/expression/index.ts +43 -21
- package/expression/parsing_context.ts +2 -2
- package/expression/stops.ts +2 -2
- package/expression/values.ts +2 -2
- package/feature_filter/index.ts +3 -3
- package/function/convert.ts +8 -8
- package/function/index.ts +4 -2
- package/group_by_layout.ts +6 -4
- package/package.json +1 -1
- package/read_style.ts +2 -2
- package/reference/v8.json +86 -5
- package/types/config_options.ts +1 -1
- package/types.ts +16 -6
- package/util/properties.ts +1 -1
- package/validate/validate.ts +15 -5
- package/validate/validate_appearance.ts +7 -7
- package/validate/validate_array.ts +7 -7
- package/validate/validate_enum.ts +2 -3
- package/validate/validate_fog.ts +2 -2
- package/validate/validate_function.ts +14 -13
- package/validate/validate_layer.ts +10 -9
- package/validate/validate_light.ts +2 -2
- package/validate/validate_lights.ts +3 -3
- package/validate/validate_number.ts +10 -7
- package/validate/validate_object.ts +11 -10
- package/validate/validate_option.ts +24 -9
- package/validate/validate_property.ts +4 -4
- package/validate/validate_rain.ts +1 -1
- package/validate/validate_snow.ts +1 -1
- package/validate/validate_style.ts +2 -1
- package/validate/validate_terrain.ts +2 -2
- package/validate_mapbox_api_supported.ts +5 -6
- package/validate_style.min.ts +2 -1
- package/visit.ts +11 -10
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {isValue} from '../values';
|
|
2
|
-
import {NumberType} from '../types';
|
|
2
|
+
import {NumberType, ValueType} from '../types';
|
|
3
3
|
import {classifyRings, updateBBox, boxWithinBox, pointWithinPolygon, segmentIntersectSegment} from '../../util/geometry_util';
|
|
4
4
|
import {lngFromMercatorX, latFromMercatorY} from '../../util/mercator';
|
|
5
5
|
import TinyQueue from "tinyqueue";
|
|
6
6
|
import EXTENT from '../../data/extent';
|
|
7
|
+
import Literal from './literal';
|
|
8
|
+
import {isGlobalPropertyConstant, isStateConstant} from '../is_constant';
|
|
7
9
|
|
|
8
10
|
// Geodesic scale factors (cheap-ruler math): meters per degree lon/lat at a given latitude.
|
|
9
11
|
// Only the three distance operations used in this file are implemented.
|
|
@@ -56,21 +58,21 @@ function rulerPointToSegmentDistance(p: [number, number], a: [number, number], b
|
|
|
56
58
|
|
|
57
59
|
function rulerPointOnLine(line: Array<[number, number]>, p: [number, number], kx: number, ky: number): [number, number] {
|
|
58
60
|
let minDist = Infinity;
|
|
59
|
-
let minX = line[0][0];
|
|
60
|
-
let minY = line[0][1];
|
|
61
|
+
let minX = line[0]![0];
|
|
62
|
+
let minY = line[0]![1];
|
|
61
63
|
|
|
62
64
|
for (let i = 0; i < line.length - 1; i++) {
|
|
63
|
-
let x = line[i][0];
|
|
64
|
-
let y = line[i][1];
|
|
65
|
-
let dx = wrapLng(line[i + 1][0] - x) * kx;
|
|
66
|
-
let dy = (line[i + 1][1] - y) * ky;
|
|
65
|
+
let x = line[i]![0];
|
|
66
|
+
let y = line[i]![1];
|
|
67
|
+
let dx = wrapLng(line[i + 1]![0] - x) * kx;
|
|
68
|
+
let dy = (line[i + 1]![1] - y) * ky;
|
|
67
69
|
let t = 0;
|
|
68
70
|
|
|
69
71
|
if (dx !== 0 || dy !== 0) {
|
|
70
72
|
t = (wrapLng(p[0] - x) * kx * dx + (p[1] - y) * ky * dy) / (dx * dx + dy * dy);
|
|
71
73
|
if (t > 1) {
|
|
72
|
-
x = line[i + 1][0];
|
|
73
|
-
y = line[i + 1][1];
|
|
74
|
+
x = line[i + 1]![0];
|
|
75
|
+
y = line[i + 1]![1];
|
|
74
76
|
} else if (t > 0) {
|
|
75
77
|
x += (dx / kx) * t;
|
|
76
78
|
y += (dy / ky) * t;
|
|
@@ -142,7 +144,7 @@ function isRangeSafe(range: IndexRange, threshold: number) {
|
|
|
142
144
|
// Split the point set(points or linestring) into two halves, using IndexRange to do in-place splitting.
|
|
143
145
|
// If geometry is a line, the last point(here is the second index) of range1 needs to be included as the first point(here is the first index) of range2.
|
|
144
146
|
// If geometry are points, just split the points equally(if possible) into two new point sets(here are two index ranges).
|
|
145
|
-
function splitRange(range: IndexRange, isLine: boolean) {
|
|
147
|
+
function splitRange(range: IndexRange, isLine: boolean): [IndexRange | null, IndexRange | null] {
|
|
146
148
|
if (range[0] > range[1]) return [null, null];
|
|
147
149
|
const size = getRangeSize(range);
|
|
148
150
|
if (isLine) {
|
|
@@ -168,7 +170,7 @@ function getBBox(pointSets: Array<[number, number]>, range: IndexRange) {
|
|
|
168
170
|
const bbox: BBox = [Infinity, Infinity, -Infinity, -Infinity];
|
|
169
171
|
if (!isRangeSafe(range, pointSets.length)) return bbox;
|
|
170
172
|
for (let i = range[0]; i <= range[1]; ++i) {
|
|
171
|
-
updateBBox(bbox, pointSets[i]);
|
|
173
|
+
updateBBox(bbox, pointSets[i]!);
|
|
172
174
|
}
|
|
173
175
|
return bbox;
|
|
174
176
|
}
|
|
@@ -176,8 +178,8 @@ function getBBox(pointSets: Array<[number, number]>, range: IndexRange) {
|
|
|
176
178
|
function getPolygonBBox(polygon: Array<Array<[number, number]>>) {
|
|
177
179
|
const bbox: BBox = [Infinity, Infinity, -Infinity, -Infinity];
|
|
178
180
|
for (let i = 0; i < polygon.length; ++i) {
|
|
179
|
-
for (let j = 0; j < polygon[i]
|
|
180
|
-
updateBBox(bbox, polygon[i][j]);
|
|
181
|
+
for (let j = 0; j < polygon[i]!.length; ++j) {
|
|
182
|
+
updateBBox(bbox, polygon[i]![j]!);
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
185
|
return bbox;
|
|
@@ -221,7 +223,7 @@ export function getLngLatPoint(coord: Point, canonical: CanonicalTileID, extent:
|
|
|
221
223
|
function getLngLatPoints(coordinates: Array<Point>, canonical: CanonicalTileID): Array<[number, number]> {
|
|
222
224
|
const coords: Array<[number, number]> = [];
|
|
223
225
|
for (let i = 0; i < coordinates.length; ++i) {
|
|
224
|
-
coords.push(getLngLatPoint(coordinates[i]
|
|
226
|
+
coords.push(getLngLatPoint(coordinates[i]!, canonical));
|
|
225
227
|
}
|
|
226
228
|
return coords;
|
|
227
229
|
}
|
|
@@ -235,7 +237,7 @@ function pointsToLineDistance(points: Array<[number, number]>, rangeA: IndexRang
|
|
|
235
237
|
const subLine = line.slice(rangeB[0], rangeB[1] + 1);
|
|
236
238
|
let dist = Infinity;
|
|
237
239
|
for (let i = rangeA[0]; i <= rangeA[1]; ++i) {
|
|
238
|
-
if ((dist = Math.min(dist, pointToLineDistance(points[i]
|
|
240
|
+
if ((dist = Math.min(dist, pointToLineDistance(points[i]!, subLine, kx, ky))) === 0.0) return 0.0;
|
|
239
241
|
}
|
|
240
242
|
return dist;
|
|
241
243
|
}
|
|
@@ -261,8 +263,8 @@ function lineToLineDistance(line1: Array<[number, number]>, range1: IndexRange,
|
|
|
261
263
|
let dist = Infinity;
|
|
262
264
|
for (let i = range1[0]; i < range1[1]; ++i) {
|
|
263
265
|
for (let j = range2[0]; j < range2[1]; ++j) {
|
|
264
|
-
if (segmentIntersectSegment(line1[i]
|
|
265
|
-
dist = Math.min(dist, segmentToSegmentDistance(line1[i]
|
|
266
|
+
if (segmentIntersectSegment(line1[i]!, line1[i + 1]!, line2[j]!, line2[j + 1]!)) return 0.0;
|
|
267
|
+
dist = Math.min(dist, segmentToSegmentDistance(line1[i]!, line1[i + 1]!, line2[j]!, line2[j + 1]!, kx, ky));
|
|
266
268
|
}
|
|
267
269
|
}
|
|
268
270
|
return dist;
|
|
@@ -275,7 +277,7 @@ function pointsToPointsDistance(pointSet1: Array<[number, number]>, range1: Inde
|
|
|
275
277
|
let dist = Infinity;
|
|
276
278
|
for (let i = range1[0]; i <= range1[1]; ++i) {
|
|
277
279
|
for (let j = range2[0]; j <= range2[1]; ++j) {
|
|
278
|
-
if ((dist = Math.min(dist, rulerDistance(pointSet1[i]
|
|
280
|
+
if ((dist = Math.min(dist, rulerDistance(pointSet1[i]!, pointSet2[j]!, kx, ky))) === 0.0) return dist;
|
|
279
281
|
}
|
|
280
282
|
}
|
|
281
283
|
return dist;
|
|
@@ -291,7 +293,7 @@ function pointToPolygonDistance(point: [number, number], polygon: Array<Array<[n
|
|
|
291
293
|
return NaN;
|
|
292
294
|
}
|
|
293
295
|
if (ring[0] !== ring[ringLen - 1]) {
|
|
294
|
-
if ((dist = Math.min(dist, rulerPointToSegmentDistance(point, ring[ringLen - 1]
|
|
296
|
+
if ((dist = Math.min(dist, rulerPointToSegmentDistance(point, ring[ringLen - 1]!, ring[0]!, kx, ky))) === 0.0) return dist;
|
|
295
297
|
}
|
|
296
298
|
if ((dist = Math.min(dist, pointToLineDistance(point, ring, kx, ky))) === 0.0) return dist;
|
|
297
299
|
}
|
|
@@ -303,14 +305,14 @@ function lineToPolygonDistance(line: Array<[number, number]>, range: IndexRange,
|
|
|
303
305
|
return NaN;
|
|
304
306
|
}
|
|
305
307
|
for (let i = range[0]; i <= range[1]; ++i) {
|
|
306
|
-
if (pointWithinPolygon(line[i]
|
|
308
|
+
if (pointWithinPolygon(line[i]!, polygon, true /*trueOnBoundary*/)) return 0.0;
|
|
307
309
|
}
|
|
308
310
|
let dist = Infinity;
|
|
309
311
|
for (let i = range[0]; i < range[1]; ++i) {
|
|
310
312
|
for (const ring of polygon) {
|
|
311
313
|
for (let j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
|
|
312
|
-
if (segmentIntersectSegment(line[i]
|
|
313
|
-
dist = Math.min(dist, segmentToSegmentDistance(line[i]
|
|
314
|
+
if (segmentIntersectSegment(line[i]!, line[i + 1]!, ring[k]!, ring[j]!)) return 0.0;
|
|
315
|
+
dist = Math.min(dist, segmentToSegmentDistance(line[i]!, line[i + 1]!, ring[k]!, ring[j]!, kx, ky));
|
|
314
316
|
}
|
|
315
317
|
}
|
|
316
318
|
}
|
|
@@ -320,7 +322,7 @@ function lineToPolygonDistance(line: Array<[number, number]>, range: IndexRange,
|
|
|
320
322
|
function polygonIntersect(polygon1: Array<Array<[number, number]>>, polygon2: Array<Array<[number, number]>>) {
|
|
321
323
|
for (const ring of polygon1) {
|
|
322
324
|
for (let i = 0; i <= ring.length - 1; ++i) {
|
|
323
|
-
if (pointWithinPolygon(ring[i]
|
|
325
|
+
if (pointWithinPolygon(ring[i]!, polygon2, true /*trueOnBoundary*/)) return true;
|
|
324
326
|
}
|
|
325
327
|
}
|
|
326
328
|
return false;
|
|
@@ -342,8 +344,8 @@ function polygonToPolygonDistance(polygon1: Array<Array<[number, number]>>, poly
|
|
|
342
344
|
for (let i = 0, len1 = ring1.length, l = len1 - 1; i < len1; l = i++) {
|
|
343
345
|
for (const ring2 of polygon2) {
|
|
344
346
|
for (let j = 0, len2 = ring2.length, k = len2 - 1; j < len2; k = j++) {
|
|
345
|
-
if (segmentIntersectSegment(ring1[l]
|
|
346
|
-
dist = Math.min(dist, segmentToSegmentDistance(ring1[l]
|
|
347
|
+
if (segmentIntersectSegment(ring1[l]!, ring1[i]!, ring2[k]!, ring2[j]!)) return 0.0;
|
|
348
|
+
dist = Math.min(dist, segmentToSegmentDistance(ring1[l]!, ring1[i]!, ring2[k]!, ring2[j]!, kx, ky));
|
|
347
349
|
}
|
|
348
350
|
}
|
|
349
351
|
}
|
|
@@ -361,7 +363,7 @@ function updateQueue(distQueue: TinyQueue<DistPair>, miniDist: number, kx: numbe
|
|
|
361
363
|
// Divide and conquer, the time complexity is O(n*lgn), faster than Brute force O(n*n)
|
|
362
364
|
// Most of the time, use index for in-place processing.
|
|
363
365
|
function pointSetToPolygonDistance(pointSets: Array<[number, number]>, isLine: boolean, polygon: Array<Array<[number, number]>>, kx: number, ky: number, currentMiniDist: number = Infinity) {
|
|
364
|
-
let miniDist = Math.min(rulerDistance(pointSets[0]
|
|
366
|
+
let miniDist = Math.min(rulerDistance(pointSets[0]!, polygon[0]![0]!, kx, ky), currentMiniDist);
|
|
365
367
|
if (miniDist === 0.0) return miniDist;
|
|
366
368
|
const initialDistPair: DistPair = {
|
|
367
369
|
dist: 0,
|
|
@@ -374,7 +376,7 @@ function pointSetToPolygonDistance(pointSets: Array<[number, number]>, isLine: b
|
|
|
374
376
|
const polyBBox = getPolygonBBox(polygon);
|
|
375
377
|
|
|
376
378
|
while (distQueue.length) {
|
|
377
|
-
const distPair = distQueue.pop()
|
|
379
|
+
const distPair = distQueue.pop()!;
|
|
378
380
|
if (distPair.dist >= miniDist) continue;
|
|
379
381
|
const range = distPair.range1;
|
|
380
382
|
// In case the set size are relatively small, we could use brute-force directly
|
|
@@ -385,7 +387,7 @@ function pointSetToPolygonDistance(pointSets: Array<[number, number]>, isLine: b
|
|
|
385
387
|
if ((miniDist = Math.min(miniDist, tempDist)) === 0.0) return miniDist;
|
|
386
388
|
} else {
|
|
387
389
|
for (let i = range[0]; i <= range[1]; ++i) {
|
|
388
|
-
const tempDist = pointToPolygonDistance(pointSets[i]
|
|
390
|
+
const tempDist = pointToPolygonDistance(pointSets[i]!, polygon, kx, ky);
|
|
389
391
|
if ((miniDist = Math.min(miniDist, tempDist)) === 0.0) return miniDist;
|
|
390
392
|
}
|
|
391
393
|
}
|
|
@@ -405,7 +407,7 @@ function pointSetToPolygonDistance(pointSets: Array<[number, number]>, isLine: b
|
|
|
405
407
|
}
|
|
406
408
|
|
|
407
409
|
function pointSetsDistance(pointSet1: Array<[number, number]>, isLine1: boolean, pointSet2: Array<[number, number]>, isLine2: boolean, kx: number, ky: number, currentMiniDist: number = Infinity) {
|
|
408
|
-
let miniDist = Math.min(currentMiniDist, rulerDistance(pointSet1[0]
|
|
410
|
+
let miniDist = Math.min(currentMiniDist, rulerDistance(pointSet1[0]!, pointSet2[0]!, kx, ky));
|
|
409
411
|
if (miniDist === 0.0) return miniDist;
|
|
410
412
|
const initialDistPair: DistPair = {
|
|
411
413
|
dist: 0,
|
|
@@ -418,7 +420,7 @@ function pointSetsDistance(pointSet1: Array<[number, number]>, isLine1: boolean,
|
|
|
418
420
|
const set2Threshold = isLine2 ? MIN_LINE_POINT_SIZE : MIN_POINT_SIZE;
|
|
419
421
|
|
|
420
422
|
while (distQueue.length) {
|
|
421
|
-
const distPair = distQueue.pop()
|
|
423
|
+
const distPair = distQueue.pop()!;
|
|
422
424
|
if (distPair.dist >= miniDist) continue;
|
|
423
425
|
const rangeA = distPair.range1;
|
|
424
426
|
const rangeB = distPair.range2;
|
|
@@ -491,7 +493,7 @@ function pointsToGeometryDistance(originGeometry: Array<Array<Point>>, canonical
|
|
|
491
493
|
lngLatPoints.push(getLngLatPoint(point, canonical));
|
|
492
494
|
}
|
|
493
495
|
}
|
|
494
|
-
const [kx, ky] = lngLatScale(lngLatPoints[0][1]);
|
|
496
|
+
const [kx, ky] = lngLatScale(lngLatPoints[0]![1]);
|
|
495
497
|
if (geometry.type === 'Point' || geometry.type === 'MultiPoint' || geometry.type === 'LineString') {
|
|
496
498
|
return pointSetsDistance(lngLatPoints, false /*isLine*/,
|
|
497
499
|
(geometry.type === 'Point' ? [geometry.coordinates] : geometry.coordinates) as Array<[number, number]>,
|
|
@@ -516,7 +518,7 @@ function linesToGeometryDistance(originGeometry: Array<Array<Point>>, canonical:
|
|
|
516
518
|
}
|
|
517
519
|
lngLatLines.push(lngLatLine);
|
|
518
520
|
}
|
|
519
|
-
const [kx, ky] = lngLatScale(lngLatLines[0][0][1]);
|
|
521
|
+
const [kx, ky] = lngLatScale(lngLatLines[0]![0]![1]);
|
|
520
522
|
if (geometry.type === 'Point' || geometry.type === 'MultiPoint' || geometry.type === 'LineString') {
|
|
521
523
|
return pointSetToLinesDistance(
|
|
522
524
|
(geometry.type === 'Point' ? [geometry.coordinates] : geometry.coordinates) as Array<[number, number]>,
|
|
@@ -525,7 +527,7 @@ function linesToGeometryDistance(originGeometry: Array<Array<Point>>, canonical:
|
|
|
525
527
|
if (geometry.type === 'MultiLineString') {
|
|
526
528
|
let dist = Infinity;
|
|
527
529
|
for (let i = 0; i < geometry.coordinates.length; i++) {
|
|
528
|
-
const tempDist = pointSetToLinesDistance(geometry.coordinates[i] as Array<[number, number]>, true /*isLine*/, lngLatLines, kx, ky, dist);
|
|
530
|
+
const tempDist = pointSetToLinesDistance(geometry.coordinates[i]! as Array<[number, number]>, true /*isLine*/, lngLatLines, kx, ky, dist);
|
|
529
531
|
if (isNaN(tempDist)) return tempDist;
|
|
530
532
|
if ((dist = Math.min(dist, tempDist)) === 0.0) return dist;
|
|
531
533
|
}
|
|
@@ -534,7 +536,7 @@ function linesToGeometryDistance(originGeometry: Array<Array<Point>>, canonical:
|
|
|
534
536
|
if (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
|
|
535
537
|
let dist = Infinity;
|
|
536
538
|
for (let i = 0; i < lngLatLines.length; i++) {
|
|
537
|
-
const tempDist = pointSetToPolygonsDistance(lngLatLines[i]
|
|
539
|
+
const tempDist = pointSetToPolygonsDistance(lngLatLines[i]!, true /*isLine*/,
|
|
538
540
|
(geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates) as Array<Array<Array<[number, number]>>>,
|
|
539
541
|
kx, ky, dist);
|
|
540
542
|
if (isNaN(tempDist)) return tempDist;
|
|
@@ -550,11 +552,11 @@ function polygonsToGeometryDistance(originGeometry: Array<Array<Point>>, canonic
|
|
|
550
552
|
for (const polygon of classifyRings(originGeometry, 0)) {
|
|
551
553
|
const lngLatPolygon: Array<Array<[number, number]>> = [];
|
|
552
554
|
for (let i = 0; i < polygon.length; ++i) {
|
|
553
|
-
lngLatPolygon.push(getLngLatPoints(polygon[i]
|
|
555
|
+
lngLatPolygon.push(getLngLatPoints(polygon[i]!, canonical));
|
|
554
556
|
}
|
|
555
557
|
lngLatPolygons.push(lngLatPolygon);
|
|
556
558
|
}
|
|
557
|
-
const [kx, ky] = lngLatScale(lngLatPolygons[0][0][0][1]);
|
|
559
|
+
const [kx, ky] = lngLatScale(lngLatPolygons[0]![0]![0]![1]);
|
|
558
560
|
if (geometry.type === 'Point' || geometry.type === 'MultiPoint' || geometry.type === 'LineString') {
|
|
559
561
|
return pointSetToPolygonsDistance(
|
|
560
562
|
(geometry.type === 'Point' ? [geometry.coordinates] : geometry.coordinates) as Array<[number, number]>,
|
|
@@ -563,7 +565,7 @@ function polygonsToGeometryDistance(originGeometry: Array<Array<Point>>, canonic
|
|
|
563
565
|
if (geometry.type === 'MultiLineString') {
|
|
564
566
|
let dist = Infinity;
|
|
565
567
|
for (let i = 0; i < geometry.coordinates.length; i++) {
|
|
566
|
-
const tempDist = pointSetToPolygonsDistance(geometry.coordinates[i] as Array<[number, number]>, true /*isLine*/, lngLatPolygons, kx, ky, dist);
|
|
568
|
+
const tempDist = pointSetToPolygonsDistance(geometry.coordinates[i]! as Array<[number, number]>, true /*isLine*/, lngLatPolygons, kx, ky, dist);
|
|
567
569
|
if (isNaN(tempDist)) return tempDist;
|
|
568
570
|
if ((dist = Math.min(dist, tempDist)) === 0.0) return dist;
|
|
569
571
|
}
|
|
@@ -587,70 +589,130 @@ function isTypeValid(type: string) {
|
|
|
587
589
|
type === "MultiPolygon"
|
|
588
590
|
);
|
|
589
591
|
}
|
|
592
|
+
|
|
593
|
+
// Resolves a GeoJSON value (Feature / FeatureCollection / bare geometry) down
|
|
594
|
+
// to the geometries `distance` measures against -- one per feature for a
|
|
595
|
+
// FeatureCollection, one otherwise. Shared by parse-time (literal argument)
|
|
596
|
+
// and evaluate-time (e.g. a `["config", ...]` argument, whose value isn't
|
|
597
|
+
// known until evaluation) resolution. Returns null if the value isn't valid
|
|
598
|
+
// GeoJSON, or if any feature has a geometry type other than
|
|
599
|
+
// Point/LineString/Polygon (and their Multi* variants).
|
|
600
|
+
function extractDistanceGeometry(value: unknown): Array<DistanceGeometry> | null {
|
|
601
|
+
if (!isValue(value) || typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
const geojson = value as GeoJSON.GeoJSON;
|
|
605
|
+
if (geojson.type === 'FeatureCollection') {
|
|
606
|
+
if (geojson.features.length === 0) return null;
|
|
607
|
+
const geometries: Array<DistanceGeometry> = [];
|
|
608
|
+
for (const feature of geojson.features) {
|
|
609
|
+
if (!isTypeValid(feature.geometry.type)) return null;
|
|
610
|
+
geometries.push(feature.geometry as DistanceGeometry);
|
|
611
|
+
}
|
|
612
|
+
return geometries;
|
|
613
|
+
}
|
|
614
|
+
if (geojson.type === 'Feature') {
|
|
615
|
+
return isTypeValid(geojson.geometry.type) ? [geojson.geometry as DistanceGeometry] : null;
|
|
616
|
+
}
|
|
617
|
+
if (isTypeValid(geojson.type)) {
|
|
618
|
+
return [geojson as DistanceGeometry];
|
|
619
|
+
}
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
590
623
|
class Distance implements Expression {
|
|
591
624
|
type: Type;
|
|
592
|
-
geojson:
|
|
593
|
-
geometries: DistanceGeometry;
|
|
625
|
+
geojson: Expression;
|
|
594
626
|
|
|
595
|
-
constructor(geojson:
|
|
627
|
+
constructor(geojson: Expression) {
|
|
596
628
|
this.type = NumberType;
|
|
597
629
|
this.geojson = geojson;
|
|
598
|
-
this.geometries = geometries;
|
|
599
630
|
}
|
|
600
631
|
|
|
601
632
|
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Distance | null | void {
|
|
602
633
|
if (args.length !== 2) {
|
|
603
634
|
return context.error(`'distance' expression requires either one argument, but found ' ${args.length - 1} instead.`);
|
|
604
635
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
636
|
+
|
|
637
|
+
const arg = args[1];
|
|
638
|
+
// A bare GeoJSON value (Feature / FeatureCollection / bare geometry)
|
|
639
|
+
// isn't valid expression syntax on its own, so wrap it as a literal
|
|
640
|
+
// like ["literal", {...}] would be. Anything else -- e.g.
|
|
641
|
+
// `["config", "key"]` -- is left as-is and parsed as a regular
|
|
642
|
+
// sub-expression, resolved to GeoJSON at evaluation time so a config
|
|
643
|
+
// value can change at runtime without re-parsing the style.
|
|
644
|
+
const isBareGeoJSON = isValue(arg) && !Array.isArray(arg);
|
|
645
|
+
const parsed = context.parse(isBareGeoJSON ? ['literal', arg] : arg, 1, ValueType);
|
|
646
|
+
if (!parsed) return null;
|
|
647
|
+
|
|
648
|
+
for (const [globalProperties, name] of [
|
|
649
|
+
[['measure-light'], 'brightness'],
|
|
650
|
+
[['pitch'], 'pitch'],
|
|
651
|
+
[['distance-from-center'], 'distance-from-center'],
|
|
652
|
+
] as Array<[Array<string>, string]>) {
|
|
653
|
+
if (!isGlobalPropertyConstant(parsed, globalProperties)) {
|
|
654
|
+
return context.error(`'distance' expression may not depend on ${name}.`);
|
|
619
655
|
}
|
|
620
656
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
657
|
+
if (!isStateConstant(parsed)) {
|
|
658
|
+
return context.error(`'distance' expression may not depend on feature-state.`);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Literal arguments can be validated eagerly; a config-driven
|
|
662
|
+
// argument can't be checked until it's evaluated.
|
|
663
|
+
if (parsed instanceof Literal && !extractDistanceGeometry(parsed.value)) {
|
|
664
|
+
return context.error(
|
|
665
|
+
"'distance' expression needs to be an array with format [\'Distance\', GeoJSONObj]."
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return new Distance(parsed);
|
|
624
670
|
}
|
|
625
671
|
|
|
626
672
|
evaluate(ctx: EvaluationContext): number | null {
|
|
673
|
+
const geometries = extractDistanceGeometry(this.geojson.evaluate(ctx));
|
|
674
|
+
if (!geometries) {
|
|
675
|
+
console.warn("Distance Expression: could not resolve a valid Point/LineString/Polygon GeoJSON geometry.");
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
|
|
627
679
|
const geometry = ctx.geometry();
|
|
628
680
|
const canonical = ctx.canonicalID();
|
|
629
|
-
if (geometry
|
|
630
|
-
if (ctx.geometryType() === 'Point') {
|
|
631
|
-
return pointsToGeometryDistance(geometry, canonical, this.geometries);
|
|
632
|
-
}
|
|
633
|
-
if (ctx.geometryType() === 'LineString') {
|
|
634
|
-
return linesToGeometryDistance(geometry, canonical, this.geometries);
|
|
635
|
-
}
|
|
636
|
-
if (ctx.geometryType() === 'Polygon') {
|
|
637
|
-
return polygonsToGeometryDistance(geometry, canonical, this.geometries);
|
|
638
|
-
}
|
|
639
|
-
console.warn("Distance Expression: currently only evaluates valid Point/LineString/Polygon geometries.");
|
|
640
|
-
} else {
|
|
681
|
+
if (geometry == null || canonical == null) {
|
|
641
682
|
console.warn("Distance Expression: requires valid feature and canonical information.");
|
|
683
|
+
return null;
|
|
642
684
|
}
|
|
643
|
-
|
|
685
|
+
|
|
686
|
+
const geometryType = ctx.geometryType();
|
|
687
|
+
const distanceTo = geometryType === 'Point' ? pointsToGeometryDistance :
|
|
688
|
+
geometryType === 'LineString' ? linesToGeometryDistance :
|
|
689
|
+
geometryType === 'Polygon' ? polygonsToGeometryDistance : null;
|
|
690
|
+
if (!distanceTo) {
|
|
691
|
+
console.warn("Distance Expression: currently only evaluates valid Point/LineString/Polygon geometries.");
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// A FeatureCollection reference resolves to one geometry per feature;
|
|
696
|
+
// report the distance to the closest one.
|
|
697
|
+
let dist = Infinity;
|
|
698
|
+
for (const g of geometries) {
|
|
699
|
+
const tempDist = distanceTo(geometry, canonical, g);
|
|
700
|
+
if (tempDist == null || isNaN(tempDist)) return tempDist;
|
|
701
|
+
if ((dist = Math.min(dist, tempDist)) === 0) break;
|
|
702
|
+
}
|
|
703
|
+
return dist;
|
|
644
704
|
}
|
|
645
705
|
|
|
646
|
-
eachChild() {
|
|
706
|
+
eachChild(fn: (_: Expression) => void) {
|
|
707
|
+
fn(this.geojson);
|
|
708
|
+
}
|
|
647
709
|
|
|
648
710
|
outputDefined(): boolean {
|
|
649
711
|
return true;
|
|
650
712
|
}
|
|
651
713
|
|
|
652
714
|
serialize(): Array<unknown> {
|
|
653
|
-
return ['distance', this.geojson];
|
|
715
|
+
return ['distance', this.geojson.serialize()];
|
|
654
716
|
}
|
|
655
717
|
}
|
|
656
718
|
|
|
@@ -47,35 +47,32 @@ export default class FormatExpression implements Expression {
|
|
|
47
47
|
const sections: Array<FormattedSectionExpression> = [];
|
|
48
48
|
let nextTokenMayBeObject = false;
|
|
49
49
|
for (let i = 1; i <= args.length - 1; ++i) {
|
|
50
|
-
const arg = args[i];
|
|
50
|
+
const arg = args[i] as Record<string, unknown> | unknown[] | string | number | boolean | null | undefined;
|
|
51
51
|
|
|
52
52
|
if (nextTokenMayBeObject && typeof arg === "object" && !Array.isArray(arg)) {
|
|
53
53
|
nextTokenMayBeObject = false;
|
|
54
54
|
|
|
55
55
|
let scale = null;
|
|
56
|
-
if (arg['font-scale']) {
|
|
57
|
-
scale = context.parseObjectValue(arg['font-scale'], i, 'font-scale', NumberType);
|
|
56
|
+
if (arg!['font-scale']) {
|
|
57
|
+
scale = context.parseObjectValue(arg!['font-scale'], i, 'font-scale', NumberType);
|
|
58
58
|
if (!scale) return null;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
let font = null;
|
|
62
|
-
if (arg['text-font']) {
|
|
63
|
-
font = context.parseObjectValue(arg['text-font'], i, 'text-font', array(StringType));
|
|
62
|
+
if (arg!['text-font']) {
|
|
63
|
+
font = context.parseObjectValue(arg!['text-font'], i, 'text-font', array(StringType));
|
|
64
64
|
if (!font) return null;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
let textColor = null;
|
|
68
|
-
if (arg['text-color']) {
|
|
69
|
-
textColor = context.parseObjectValue(arg['text-color'], i, 'text-color', ColorType);
|
|
68
|
+
if (arg!['text-color']) {
|
|
69
|
+
textColor = context.parseObjectValue(arg!['text-color'], i, 'text-color', ColorType);
|
|
70
70
|
if (!textColor) return null;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const lastExpression = sections.at(-1)
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
73
|
+
const lastExpression = sections.at(-1)!;
|
|
75
74
|
lastExpression.scale = scale;
|
|
76
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
77
75
|
lastExpression.font = font;
|
|
78
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
79
76
|
lastExpression.textColor = textColor;
|
|
80
77
|
} else {
|
|
81
78
|
const content = context.parse(args[i], i, ValueType);
|
|
@@ -140,10 +137,10 @@ export default class FormatExpression implements Expression {
|
|
|
140
137
|
}
|
|
141
138
|
|
|
142
139
|
serialize(): SerializedExpression {
|
|
143
|
-
const serialized:
|
|
140
|
+
const serialized: Array<unknown> = ["format"];
|
|
144
141
|
for (const section of this.sections) {
|
|
145
142
|
serialized.push(section.content.serialize());
|
|
146
|
-
const options = {}
|
|
143
|
+
const options: Record<string, SerializedExpression> = {};
|
|
147
144
|
if (section.scale) {
|
|
148
145
|
options['font-scale'] = section.scale.serialize();
|
|
149
146
|
}
|
|
@@ -32,7 +32,7 @@ export default class ImageExpression implements Expression {
|
|
|
32
32
|
paramsPrimary?: ImageParams;
|
|
33
33
|
iconsetIdPrimary?: string;
|
|
34
34
|
|
|
35
|
-
nameSecondary?: Expression;
|
|
35
|
+
nameSecondary?: Expression | null;
|
|
36
36
|
paramsSecondary?: ImageParams;
|
|
37
37
|
iconsetIdSecondary?: string;
|
|
38
38
|
|
|
@@ -121,7 +121,7 @@ export default class ImageExpression implements Expression {
|
|
|
121
121
|
parsedParams[key] = value;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
imageExpression.at(-1)
|
|
124
|
+
imageExpression.at(-1)!.options!.params = parsedParams;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// Validate the iconset image options
|
|
@@ -136,7 +136,7 @@ export default class ImageExpression implements Expression {
|
|
|
136
136
|
return false;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
imageExpression.at(-1)
|
|
139
|
+
imageExpression.at(-1)!.options!.iconset = iconset;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
nextArgId++;
|
|
@@ -154,14 +154,14 @@ export default class ImageExpression implements Expression {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
return new ImageExpression(
|
|
157
|
-
imageExpression[0]
|
|
157
|
+
imageExpression[0]!.image,
|
|
158
158
|
imageExpression[1] ? imageExpression[1].image : undefined,
|
|
159
|
-
imageExpression[0]
|
|
159
|
+
imageExpression[0]!.options,
|
|
160
160
|
imageExpression[1] ? imageExpression[1].options : undefined
|
|
161
161
|
);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
evaluateParams(ctx: EvaluationContext, params: Record<string, Expression> | undefined): {params: Record<string, Color>} {
|
|
164
|
+
evaluateParams(ctx: EvaluationContext, params: Record<string, Expression> | undefined): {params: Record<string, Color>} | undefined {
|
|
165
165
|
const result: Record<string, Color> = {};
|
|
166
166
|
if (params) {
|
|
167
167
|
for (const key in params) {
|
|
@@ -210,7 +210,7 @@ export default class ImageExpression implements Expression {
|
|
|
210
210
|
value.available = ctx.availableImages.some((id) => ImageId.isEqual(id, primaryId));
|
|
211
211
|
if (value.available) {
|
|
212
212
|
// If there's a secondary variant, only mark it available if both are present
|
|
213
|
-
const secondaryId = value.getSecondary() ? value.getSecondary()
|
|
213
|
+
const secondaryId = value.getSecondary() ? value.getSecondary()!.id : null;
|
|
214
214
|
if (secondaryId) value.available = ctx.availableImages.some((id) => ImageId.isEqual(id, secondaryId));
|
|
215
215
|
}
|
|
216
216
|
}
|
|
@@ -246,7 +246,7 @@ export default class ImageExpression implements Expression {
|
|
|
246
246
|
return false;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
serializeOptions(params
|
|
249
|
+
serializeOptions(params?: ImageParams, iconsetId?: string): SerializedImageOptions | undefined {
|
|
250
250
|
const result: SerializedImageOptions = {};
|
|
251
251
|
|
|
252
252
|
if (iconsetId) {
|