@mapbox/mapbox-gl-style-spec 14.25.0 → 14.26.0-rc.1

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.
@@ -1,8 +1,8 @@
1
1
  import {toString} from './types';
2
- import ParsingContext from './parsing_context';
3
2
  import assert from '../util/assert';
4
3
 
5
4
  import type EvaluationContext from './evaluation_context';
5
+ import type ParsingContext from './parsing_context';
6
6
  import type {Expression, ExpressionRegistry, SerializedExpression} from './expression';
7
7
  import type {Type} from './types';
8
8
  import type {Value} from './values';
@@ -89,7 +89,7 @@ class CompoundExpression implements Expression {
89
89
  // errors between overload attempts; the other fields are
90
90
  // identical across attempts.
91
91
  if (signatureContext === null) {
92
- signatureContext = new ParsingContext(context.registry, context.path, null, context.scope, [], context._scope, context.options, context.iconImageUseTheme);
92
+ signatureContext = context._forkForSignature();
93
93
  } else {
94
94
  signatureContext.errors.length = 0;
95
95
  }
@@ -102,12 +102,12 @@ function makeComparison(
102
102
  let lhs = context.parse(args[1], 1, ValueType);
103
103
  if (!lhs) return null;
104
104
  if (!isComparableType(op, lhs.type)) {
105
- return context.concat(1).error(`"${op}" comparisons are not supported for type '${toString(lhs.type)}'.`);
105
+ return context.error(`"${op}" comparisons are not supported for type '${toString(lhs.type)}'.`, 1);
106
106
  }
107
107
  let rhs = context.parse(args[2], 2, ValueType);
108
108
  if (!rhs) return null;
109
109
  if (!isComparableType(op, rhs.type)) {
110
- return context.concat(2).error(`"${op}" comparisons are not supported for type '${toString(rhs.type)}'.`);
110
+ return context.error(`"${op}" comparisons are not supported for type '${toString(rhs.type)}'.`, 2);
111
111
  }
112
112
 
113
113
  if (
@@ -2,10 +2,94 @@ import {isValue} from '../values';
2
2
  import {NumberType} from '../types';
3
3
  import {classifyRings, updateBBox, boxWithinBox, pointWithinPolygon, segmentIntersectSegment} from '../../util/geometry_util';
4
4
  import {lngFromMercatorX, latFromMercatorY} from '../../util/mercator';
5
- import CheapRuler from "cheap-ruler";
6
5
  import TinyQueue from "tinyqueue";
7
6
  import EXTENT from '../../data/extent';
8
7
 
8
+ // Geodesic scale factors (cheap-ruler math): meters per degree lon/lat at a given latitude.
9
+ // Only the three distance operations used in this file are implemented.
10
+ const RE = 6378.137; // WGS84 equatorial radius (km)
11
+ const FE = 1 / 298.257223563; // WGS84 flattening
12
+ const E2 = FE * (2 - FE);
13
+ const RAD = Math.PI / 180;
14
+
15
+ function lngLatScale(lat: number): [number, number] {
16
+ const m = RAD * RE * 1000;
17
+ const coslat = Math.cos(lat * RAD);
18
+ const w2 = 1 / (1 - E2 * (1 - coslat * coslat));
19
+ const w = Math.sqrt(w2);
20
+ return [m * w * coslat, m * w * w2 * (1 - E2)]; // [kx, ky]
21
+ }
22
+
23
+ function wrapLng(deg: number): number {
24
+ while (deg < -180) deg += 360;
25
+ while (deg > 180) deg -= 360;
26
+ return deg;
27
+ }
28
+
29
+ function rulerDistance(a: [number, number], b: [number, number], kx: number, ky: number): number {
30
+ const dx = wrapLng(a[0] - b[0]) * kx;
31
+ const dy = (a[1] - b[1]) * ky;
32
+ return Math.sqrt(dx * dx + dy * dy);
33
+ }
34
+
35
+ function rulerPointToSegmentDistance(p: [number, number], a: [number, number], b: [number, number], kx: number, ky: number): number {
36
+ let x = a[0];
37
+ let y = a[1];
38
+ let dx = wrapLng(b[0] - x) * kx;
39
+ let dy = (b[1] - y) * ky;
40
+
41
+ if (dx !== 0 || dy !== 0) {
42
+ const t = (wrapLng(p[0] - x) * kx * dx + (p[1] - y) * ky * dy) / (dx * dx + dy * dy);
43
+ if (t > 1) {
44
+ x = b[0];
45
+ y = b[1];
46
+ } else if (t > 0) {
47
+ x += (dx / kx) * t;
48
+ y += (dy / ky) * t;
49
+ }
50
+ }
51
+
52
+ dx = wrapLng(p[0] - x) * kx;
53
+ dy = (p[1] - y) * ky;
54
+ return Math.sqrt(dx * dx + dy * dy);
55
+ }
56
+
57
+ function rulerPointOnLine(line: Array<[number, number]>, p: [number, number], kx: number, ky: number): [number, number] {
58
+ let minDist = Infinity;
59
+ let minX = line[0][0];
60
+ let minY = line[0][1];
61
+
62
+ 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;
67
+ let t = 0;
68
+
69
+ if (dx !== 0 || dy !== 0) {
70
+ t = (wrapLng(p[0] - x) * kx * dx + (p[1] - y) * ky * dy) / (dx * dx + dy * dy);
71
+ if (t > 1) {
72
+ x = line[i + 1][0];
73
+ y = line[i + 1][1];
74
+ } else if (t > 0) {
75
+ x += (dx / kx) * t;
76
+ y += (dy / ky) * t;
77
+ }
78
+ }
79
+
80
+ dx = wrapLng(p[0] - x) * kx;
81
+ dy = (p[1] - y) * ky;
82
+ const sqDist = dx * dx + dy * dy;
83
+ if (sqDist < minDist) {
84
+ minDist = sqDist;
85
+ minX = x;
86
+ minY = y;
87
+ }
88
+ }
89
+
90
+ return [minX, minY];
91
+ }
92
+
9
93
  import type Point from "@mapbox/point-geometry";
10
94
  import type ParsingContext from '../parsing_context';
11
95
  import type {BBox} from '../../util/geometry_util';
@@ -102,7 +186,7 @@ function getPolygonBBox(polygon: Array<Array<[number, number]>>) {
102
186
  // Calculate the distance between two bounding boxes.
103
187
  // Calculate the delta in x and y direction, and use two fake points {0.0, 0.0} and {dx, dy} to calculate the distance.
104
188
  // Distance will be 0.0 if bounding box are overlapping.
105
- function bboxToBBoxDistance(bbox1: BBox, bbox2: BBox, ruler: CheapRuler) {
189
+ function bboxToBBoxDistance(bbox1: BBox, bbox2: BBox, kx: number, ky: number) {
106
190
  if (isDefaultBBOX(bbox1) || isDefaultBBOX(bbox2)) {
107
191
  return NaN;
108
192
  }
@@ -124,7 +208,7 @@ function bboxToBBoxDistance(bbox1: BBox, bbox2: BBox, ruler: CheapRuler) {
124
208
  if (bbox1[3] < bbox2[1]) {
125
209
  dy = bbox2[1] - bbox1[3];
126
210
  }
127
- return ruler.distance([0.0, 0.0], [dx, dy]);
211
+ return rulerDistance([0.0, 0.0], [dx, dy], kx, ky);
128
212
  }
129
213
 
130
214
  export function getLngLatPoint(coord: Point, canonical: CanonicalTileID, extent: number = EXTENT): [number, number] {
@@ -142,35 +226,35 @@ function getLngLatPoints(coordinates: Array<Point>, canonical: CanonicalTileID):
142
226
  return coords;
143
227
  }
144
228
 
145
- function pointToLineDistance(point: [number, number], line: Array<[number, number]>, ruler: CheapRuler) {
146
- const nearestPoint = ruler.pointOnLine(line, point).point;
147
- return ruler.distance(point, nearestPoint);
229
+ function pointToLineDistance(point: [number, number], line: Array<[number, number]>, kx: number, ky: number) {
230
+ const nearestPoint = rulerPointOnLine(line, point, kx, ky);
231
+ return rulerDistance(point, nearestPoint, kx, ky);
148
232
  }
149
233
 
150
- function pointsToLineDistance(points: Array<[number, number]>, rangeA: IndexRange, line: Array<[number, number]>, rangeB: IndexRange, ruler: CheapRuler) {
234
+ function pointsToLineDistance(points: Array<[number, number]>, rangeA: IndexRange, line: Array<[number, number]>, rangeB: IndexRange, kx: number, ky: number) {
151
235
  const subLine = line.slice(rangeB[0], rangeB[1] + 1);
152
236
  let dist = Infinity;
153
237
  for (let i = rangeA[0]; i <= rangeA[1]; ++i) {
154
- if ((dist = Math.min(dist, pointToLineDistance(points[i], subLine, ruler))) === 0.0) return 0.0;
238
+ if ((dist = Math.min(dist, pointToLineDistance(points[i], subLine, kx, ky))) === 0.0) return 0.0;
155
239
  }
156
240
  return dist;
157
241
  }
158
242
 
159
243
  // precondition is two segments are not intersecting with each other
160
- function segmentToSegmentDistance(p1: [number, number], p2: [number, number], q1: [number, number], q2: [number, number], ruler: CheapRuler) {
244
+ function segmentToSegmentDistance(p1: [number, number], p2: [number, number], q1: [number, number], q2: [number, number], kx: number, ky: number) {
161
245
  const dist1 = Math.min(
162
- ruler.pointToSegmentDistance(p1, q1, q2),
163
- ruler.pointToSegmentDistance(p2, q1, q2)
246
+ rulerPointToSegmentDistance(p1, q1, q2, kx, ky),
247
+ rulerPointToSegmentDistance(p2, q1, q2, kx, ky)
164
248
  );
165
249
  const dist2 = Math.min(
166
- ruler.pointToSegmentDistance(q1, p1, p2),
167
- ruler.pointToSegmentDistance(q2, p1, p2)
250
+ rulerPointToSegmentDistance(q1, p1, p2, kx, ky),
251
+ rulerPointToSegmentDistance(q2, p1, p2, kx, ky)
168
252
  );
169
253
 
170
254
  return Math.min(dist1, dist2);
171
255
  }
172
256
 
173
- function lineToLineDistance(line1: Array<[number, number]>, range1: IndexRange, line2: Array<[number, number]>, range2: IndexRange, ruler: CheapRuler) {
257
+ function lineToLineDistance(line1: Array<[number, number]>, range1: IndexRange, line2: Array<[number, number]>, range2: IndexRange, kx: number, ky: number) {
174
258
  if (!isRangeSafe(range1, line1.length) || !isRangeSafe(range2, line2.length)) {
175
259
  return NaN;
176
260
  }
@@ -178,26 +262,26 @@ function lineToLineDistance(line1: Array<[number, number]>, range1: IndexRange,
178
262
  for (let i = range1[0]; i < range1[1]; ++i) {
179
263
  for (let j = range2[0]; j < range2[1]; ++j) {
180
264
  if (segmentIntersectSegment(line1[i], line1[i + 1], line2[j], line2[j + 1])) return 0.0;
181
- dist = Math.min(dist, segmentToSegmentDistance(line1[i], line1[i + 1], line2[j], line2[j + 1], ruler));
265
+ dist = Math.min(dist, segmentToSegmentDistance(line1[i], line1[i + 1], line2[j], line2[j + 1], kx, ky));
182
266
  }
183
267
  }
184
268
  return dist;
185
269
  }
186
270
 
187
- function pointsToPointsDistance(pointSet1: Array<[number, number]>, range1: IndexRange, pointSet2: Array<[number, number]>, range2: IndexRange, ruler: CheapRuler) {
271
+ function pointsToPointsDistance(pointSet1: Array<[number, number]>, range1: IndexRange, pointSet2: Array<[number, number]>, range2: IndexRange, kx: number, ky: number) {
188
272
  if (!isRangeSafe(range1, pointSet1.length) || !isRangeSafe(range2, pointSet2.length)) {
189
273
  return NaN;
190
274
  }
191
275
  let dist = Infinity;
192
276
  for (let i = range1[0]; i <= range1[1]; ++i) {
193
277
  for (let j = range2[0]; j <= range2[1]; ++j) {
194
- if ((dist = Math.min(dist, ruler.distance(pointSet1[i], pointSet2[j]))) === 0.0) return dist;
278
+ if ((dist = Math.min(dist, rulerDistance(pointSet1[i], pointSet2[j], kx, ky))) === 0.0) return dist;
195
279
  }
196
280
  }
197
281
  return dist;
198
282
  }
199
283
 
200
- function pointToPolygonDistance(point: [number, number], polygon: Array<Array<[number, number]>>, ruler: CheapRuler) {
284
+ function pointToPolygonDistance(point: [number, number], polygon: Array<Array<[number, number]>>, kx: number, ky: number) {
201
285
  if (pointWithinPolygon(point, polygon, true /*trueOnBoundary*/)) return 0.0;
202
286
  let dist = Infinity;
203
287
  for (const ring of polygon) {
@@ -207,14 +291,14 @@ function pointToPolygonDistance(point: [number, number], polygon: Array<Array<[n
207
291
  return NaN;
208
292
  }
209
293
  if (ring[0] !== ring[ringLen - 1]) {
210
- if ((dist = Math.min(dist, ruler.pointToSegmentDistance(point, ring[ringLen - 1], ring[0]))) === 0.0) return dist;
294
+ if ((dist = Math.min(dist, rulerPointToSegmentDistance(point, ring[ringLen - 1], ring[0], kx, ky))) === 0.0) return dist;
211
295
  }
212
- if ((dist = Math.min(dist, pointToLineDistance(point, ring, ruler))) === 0.0) return dist;
296
+ if ((dist = Math.min(dist, pointToLineDistance(point, ring, kx, ky))) === 0.0) return dist;
213
297
  }
214
298
  return dist;
215
299
  }
216
300
 
217
- function lineToPolygonDistance(line: Array<[number, number]>, range: IndexRange, polygon: Array<Array<[number, number]>>, ruler: CheapRuler) {
301
+ function lineToPolygonDistance(line: Array<[number, number]>, range: IndexRange, polygon: Array<Array<[number, number]>>, kx: number, ky: number) {
218
302
  if (!isRangeSafe(range, line.length)) {
219
303
  return NaN;
220
304
  }
@@ -226,7 +310,7 @@ function lineToPolygonDistance(line: Array<[number, number]>, range: IndexRange,
226
310
  for (const ring of polygon) {
227
311
  for (let j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
228
312
  if (segmentIntersectSegment(line[i], line[i + 1], ring[k], ring[j])) return 0.0;
229
- dist = Math.min(dist, segmentToSegmentDistance(line[i], line[i + 1], ring[k], ring[j], ruler));
313
+ dist = Math.min(dist, segmentToSegmentDistance(line[i], line[i + 1], ring[k], ring[j], kx, ky));
230
314
  }
231
315
  }
232
316
  }
@@ -242,10 +326,10 @@ function polygonIntersect(polygon1: Array<Array<[number, number]>>, polygon2: Ar
242
326
  return false;
243
327
  }
244
328
 
245
- function polygonToPolygonDistance(polygon1: Array<Array<[number, number]>>, polygon2: Array<Array<[number, number]>>, ruler: CheapRuler, currentMiniDist: number = Infinity) {
329
+ function polygonToPolygonDistance(polygon1: Array<Array<[number, number]>>, polygon2: Array<Array<[number, number]>>, kx: number, ky: number, currentMiniDist: number = Infinity) {
246
330
  const bbox1 = getPolygonBBox(polygon1);
247
331
  const bbox2 = getPolygonBBox(polygon2);
248
- if (currentMiniDist !== Infinity && bboxToBBoxDistance(bbox1, bbox2, ruler) >= currentMiniDist) {
332
+ if (currentMiniDist !== Infinity && bboxToBBoxDistance(bbox1, bbox2, kx, ky) >= currentMiniDist) {
249
333
  return currentMiniDist;
250
334
  }
251
335
  if (boxWithinBox(bbox1, bbox2)) {
@@ -259,7 +343,7 @@ function polygonToPolygonDistance(polygon1: Array<Array<[number, number]>>, poly
259
343
  for (const ring2 of polygon2) {
260
344
  for (let j = 0, len2 = ring2.length, k = len2 - 1; j < len2; k = j++) {
261
345
  if (segmentIntersectSegment(ring1[l], ring1[i], ring2[k], ring2[j])) return 0.0;
262
- dist = Math.min(dist, segmentToSegmentDistance(ring1[l], ring1[i], ring2[k], ring2[j], ruler));
346
+ dist = Math.min(dist, segmentToSegmentDistance(ring1[l], ring1[i], ring2[k], ring2[j], kx, ky));
263
347
  }
264
348
  }
265
349
  }
@@ -267,17 +351,17 @@ function polygonToPolygonDistance(polygon1: Array<Array<[number, number]>>, poly
267
351
  return dist;
268
352
  }
269
353
 
270
- function updateQueue(distQueue: TinyQueue<DistPair>, miniDist: number, ruler: CheapRuler, pointSet1: Array<[number, number]>, pointSet2: Array<[number, number]>, r1: IndexRange | null, r2: IndexRange | null) {
354
+ function updateQueue(distQueue: TinyQueue<DistPair>, miniDist: number, kx: number, ky: number, pointSet1: Array<[number, number]>, pointSet2: Array<[number, number]>, r1: IndexRange | null, r2: IndexRange | null) {
271
355
  if (r1 === null || r2 === null) return;
272
- const tempDist = bboxToBBoxDistance(getBBox(pointSet1, r1), getBBox(pointSet2, r2), ruler);
356
+ const tempDist = bboxToBBoxDistance(getBBox(pointSet1, r1), getBBox(pointSet2, r2), kx, ky);
273
357
  // Insert new pair to the queue if the bbox distance is less than miniDist, the pair with biggest distance will be at the top
274
358
  if (tempDist < miniDist) distQueue.push({dist: tempDist, range1: r1, range2: r2});
275
359
  }
276
360
 
277
361
  // Divide and conquer, the time complexity is O(n*lgn), faster than Brute force O(n*n)
278
362
  // Most of the time, use index for in-place processing.
279
- function pointSetToPolygonDistance(pointSets: Array<[number, number]>, isLine: boolean, polygon: Array<Array<[number, number]>>, ruler: CheapRuler, currentMiniDist: number = Infinity) {
280
- let miniDist = Math.min(ruler.distance(pointSets[0], polygon[0][0]), currentMiniDist);
363
+ 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], polygon[0][0], kx, ky), currentMiniDist);
281
365
  if (miniDist === 0.0) return miniDist;
282
366
  const initialDistPair: DistPair = {
283
367
  dist: 0,
@@ -297,22 +381,22 @@ function pointSetToPolygonDistance(pointSets: Array<[number, number]>, isLine: b
297
381
  if (getRangeSize(range) <= setThreshold) {
298
382
  if (!isRangeSafe(range, pointSets.length)) return NaN;
299
383
  if (isLine) {
300
- const tempDist = lineToPolygonDistance(pointSets, range, polygon, ruler);
384
+ const tempDist = lineToPolygonDistance(pointSets, range, polygon, kx, ky);
301
385
  if ((miniDist = Math.min(miniDist, tempDist)) === 0.0) return miniDist;
302
386
  } else {
303
387
  for (let i = range[0]; i <= range[1]; ++i) {
304
- const tempDist = pointToPolygonDistance(pointSets[i], polygon, ruler);
388
+ const tempDist = pointToPolygonDistance(pointSets[i], polygon, kx, ky);
305
389
  if ((miniDist = Math.min(miniDist, tempDist)) === 0.0) return miniDist;
306
390
  }
307
391
  }
308
392
  } else {
309
393
  const newRanges = splitRange(range, isLine);
310
394
  if (newRanges[0] !== null) {
311
- const tempDist = bboxToBBoxDistance(getBBox(pointSets, newRanges[0]), polyBBox, ruler);
395
+ const tempDist = bboxToBBoxDistance(getBBox(pointSets, newRanges[0]), polyBBox, kx, ky);
312
396
  if (tempDist < miniDist) distQueue.push({dist: tempDist, range1: newRanges[0], range2: [0, 0]});
313
397
  }
314
398
  if (newRanges[1] !== null) {
315
- const tempDist = bboxToBBoxDistance(getBBox(pointSets, newRanges[1]), polyBBox, ruler);
399
+ const tempDist = bboxToBBoxDistance(getBBox(pointSets, newRanges[1]), polyBBox, kx, ky);
316
400
  if (tempDist < miniDist) distQueue.push({dist: tempDist, range1: newRanges[1], range2: [0, 0]});
317
401
  }
318
402
  }
@@ -320,8 +404,8 @@ function pointSetToPolygonDistance(pointSets: Array<[number, number]>, isLine: b
320
404
  return miniDist;
321
405
  }
322
406
 
323
- function pointSetsDistance(pointSet1: Array<[number, number]>, isLine1: boolean, pointSet2: Array<[number, number]>, isLine2: boolean, ruler: CheapRuler, currentMiniDist: number = Infinity) {
324
- let miniDist = Math.min(currentMiniDist, ruler.distance(pointSet1[0], pointSet2[0]));
407
+ 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], pointSet2[0], kx, ky));
325
409
  if (miniDist === 0.0) return miniDist;
326
410
  const initialDistPair: DistPair = {
327
411
  dist: 0,
@@ -344,55 +428,55 @@ function pointSetsDistance(pointSet1: Array<[number, number]>, isLine1: boolean,
344
428
  return NaN;
345
429
  }
346
430
  if (isLine1 && isLine2) {
347
- miniDist = Math.min(miniDist, lineToLineDistance(pointSet1, rangeA, pointSet2, rangeB, ruler));
431
+ miniDist = Math.min(miniDist, lineToLineDistance(pointSet1, rangeA, pointSet2, rangeB, kx, ky));
348
432
  } else if (!isLine1 && !isLine2) {
349
- miniDist = Math.min(miniDist, pointsToPointsDistance(pointSet1, rangeA, pointSet2, rangeB, ruler));
433
+ miniDist = Math.min(miniDist, pointsToPointsDistance(pointSet1, rangeA, pointSet2, rangeB, kx, ky));
350
434
  } else if (isLine1 && !isLine2) {
351
- miniDist = Math.min(miniDist, pointsToLineDistance(pointSet2, rangeB, pointSet1, rangeA, ruler));
435
+ miniDist = Math.min(miniDist, pointsToLineDistance(pointSet2, rangeB, pointSet1, rangeA, kx, ky));
352
436
  } else if (!isLine1 && isLine2) {
353
- miniDist = Math.min(miniDist, pointsToLineDistance(pointSet1, rangeA, pointSet2, rangeB, ruler));
437
+ miniDist = Math.min(miniDist, pointsToLineDistance(pointSet1, rangeA, pointSet2, rangeB, kx, ky));
354
438
  }
355
439
  if (miniDist === 0.0) return miniDist;
356
440
  } else {
357
441
  const newRangesA = splitRange(rangeA, isLine1);
358
442
  const newRangesB = splitRange(rangeB, isLine2);
359
- updateQueue(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[0], newRangesB[0]);
360
- updateQueue(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[0], newRangesB[1]);
361
- updateQueue(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[1], newRangesB[0]);
362
- updateQueue(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[1], newRangesB[1]);
443
+ updateQueue(distQueue, miniDist, kx, ky, pointSet1, pointSet2, newRangesA[0], newRangesB[0]);
444
+ updateQueue(distQueue, miniDist, kx, ky, pointSet1, pointSet2, newRangesA[0], newRangesB[1]);
445
+ updateQueue(distQueue, miniDist, kx, ky, pointSet1, pointSet2, newRangesA[1], newRangesB[0]);
446
+ updateQueue(distQueue, miniDist, kx, ky, pointSet1, pointSet2, newRangesA[1], newRangesB[1]);
363
447
  }
364
448
  }
365
449
  return miniDist;
366
450
  }
367
451
 
368
- function pointSetToLinesDistance(pointSet: Array<[number, number]>, isLine: boolean, lines: Array<Array<[number, number]>>, ruler: CheapRuler, currentMiniDist: number = Infinity) {
452
+ function pointSetToLinesDistance(pointSet: Array<[number, number]>, isLine: boolean, lines: Array<Array<[number, number]>>, kx: number, ky: number, currentMiniDist: number = Infinity) {
369
453
  let dist = currentMiniDist;
370
454
  const bbox1 = getBBox(pointSet, [0, pointSet.length - 1]);
371
455
  for (const line of lines) {
372
- if (dist !== Infinity && bboxToBBoxDistance(bbox1, getBBox(line, [0, line.length - 1]), ruler) >= dist) continue;
373
- dist = Math.min(dist, pointSetsDistance(pointSet, isLine, line, true /*isLine*/, ruler, dist));
456
+ if (dist !== Infinity && bboxToBBoxDistance(bbox1, getBBox(line, [0, line.length - 1]), kx, ky) >= dist) continue;
457
+ dist = Math.min(dist, pointSetsDistance(pointSet, isLine, line, true /*isLine*/, kx, ky, dist));
374
458
  if (dist === 0.0) return dist;
375
459
  }
376
460
  return dist;
377
461
  }
378
462
 
379
- function pointSetToPolygonsDistance(points: Array<[number, number]>, isLine: boolean, polygons: Array<Array<Array<[number, number]>>>, ruler: CheapRuler, currentMiniDist: number = Infinity) {
463
+ function pointSetToPolygonsDistance(points: Array<[number, number]>, isLine: boolean, polygons: Array<Array<Array<[number, number]>>>, kx: number, ky: number, currentMiniDist: number = Infinity) {
380
464
  let dist = currentMiniDist;
381
465
  const bbox1 = getBBox(points, [0, points.length - 1]);
382
466
  for (const polygon of polygons) {
383
- if (dist !== Infinity && bboxToBBoxDistance(bbox1, getPolygonBBox(polygon), ruler) >= dist) continue;
384
- const tempDist = pointSetToPolygonDistance(points, isLine, polygon, ruler, dist);
467
+ if (dist !== Infinity && bboxToBBoxDistance(bbox1, getPolygonBBox(polygon), kx, ky) >= dist) continue;
468
+ const tempDist = pointSetToPolygonDistance(points, isLine, polygon, kx, ky, dist);
385
469
  if (isNaN(tempDist)) return tempDist;
386
470
  if ((dist = Math.min(dist, tempDist)) === 0.0) return dist;
387
471
  }
388
472
  return dist;
389
473
  }
390
474
 
391
- function polygonsToPolygonsDistance(polygons1: Array<Array<Array<[number, number]>>>, polygons2: Array<Array<Array<[number, number]>>>, ruler: CheapRuler) {
475
+ function polygonsToPolygonsDistance(polygons1: Array<Array<Array<[number, number]>>>, polygons2: Array<Array<Array<[number, number]>>>, kx: number, ky: number) {
392
476
  let dist = Infinity;
393
477
  for (const polygon1 of polygons1) {
394
478
  for (const polygon2 of polygons2) {
395
- const tempDist = polygonToPolygonDistance(polygon1, polygon2, ruler, dist);
479
+ const tempDist = polygonToPolygonDistance(polygon1, polygon2, kx, ky, dist);
396
480
  if (isNaN(tempDist)) return tempDist;
397
481
  if ((dist = Math.min(dist, tempDist)) === 0.0) return dist;
398
482
  }
@@ -407,18 +491,18 @@ function pointsToGeometryDistance(originGeometry: Array<Array<Point>>, canonical
407
491
  lngLatPoints.push(getLngLatPoint(point, canonical));
408
492
  }
409
493
  }
410
- const ruler = new CheapRuler(lngLatPoints[0][1], 'meters');
494
+ const [kx, ky] = lngLatScale(lngLatPoints[0][1]);
411
495
  if (geometry.type === 'Point' || geometry.type === 'MultiPoint' || geometry.type === 'LineString') {
412
496
  return pointSetsDistance(lngLatPoints, false /*isLine*/,
413
497
  (geometry.type === 'Point' ? [geometry.coordinates] : geometry.coordinates) as Array<[number, number]>,
414
- geometry.type === 'LineString' /*isLine*/, ruler);
498
+ geometry.type === 'LineString' /*isLine*/, kx, ky);
415
499
  }
416
500
  if (geometry.type === 'MultiLineString') {
417
- return pointSetToLinesDistance(lngLatPoints, false /*isLine*/, geometry.coordinates as Array<Array<[number, number]>>, ruler);
501
+ return pointSetToLinesDistance(lngLatPoints, false /*isLine*/, geometry.coordinates as Array<Array<[number, number]>>, kx, ky);
418
502
  }
419
503
  if (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
420
504
  return pointSetToPolygonsDistance(lngLatPoints, false /*isLine*/,
421
- (geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates) as Array<Array<Array<[number, number]>>>, ruler);
505
+ (geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates) as Array<Array<Array<[number, number]>>>, kx, ky);
422
506
  }
423
507
  return null;
424
508
  }
@@ -432,16 +516,16 @@ function linesToGeometryDistance(originGeometry: Array<Array<Point>>, canonical:
432
516
  }
433
517
  lngLatLines.push(lngLatLine);
434
518
  }
435
- const ruler = new CheapRuler(lngLatLines[0][0][1], 'meters');
519
+ const [kx, ky] = lngLatScale(lngLatLines[0][0][1]);
436
520
  if (geometry.type === 'Point' || geometry.type === 'MultiPoint' || geometry.type === 'LineString') {
437
521
  return pointSetToLinesDistance(
438
522
  (geometry.type === 'Point' ? [geometry.coordinates] : geometry.coordinates) as Array<[number, number]>,
439
- geometry.type === 'LineString' /*isLine*/, lngLatLines, ruler);
523
+ geometry.type === 'LineString' /*isLine*/, lngLatLines, kx, ky);
440
524
  }
441
525
  if (geometry.type === 'MultiLineString') {
442
526
  let dist = Infinity;
443
527
  for (let i = 0; i < geometry.coordinates.length; i++) {
444
- const tempDist = pointSetToLinesDistance(geometry.coordinates[i] as Array<[number, number]>, true /*isLine*/, lngLatLines, ruler, dist);
528
+ const tempDist = pointSetToLinesDistance(geometry.coordinates[i] as Array<[number, number]>, true /*isLine*/, lngLatLines, kx, ky, dist);
445
529
  if (isNaN(tempDist)) return tempDist;
446
530
  if ((dist = Math.min(dist, tempDist)) === 0.0) return dist;
447
531
  }
@@ -452,7 +536,7 @@ function linesToGeometryDistance(originGeometry: Array<Array<Point>>, canonical:
452
536
  for (let i = 0; i < lngLatLines.length; i++) {
453
537
  const tempDist = pointSetToPolygonsDistance(lngLatLines[i], true /*isLine*/,
454
538
  (geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates) as Array<Array<Array<[number, number]>>>,
455
- ruler, dist);
539
+ kx, ky, dist);
456
540
  if (isNaN(tempDist)) return tempDist;
457
541
  if ((dist = Math.min(dist, tempDist)) === 0.0) return dist;
458
542
  }
@@ -470,16 +554,16 @@ function polygonsToGeometryDistance(originGeometry: Array<Array<Point>>, canonic
470
554
  }
471
555
  lngLatPolygons.push(lngLatPolygon);
472
556
  }
473
- const ruler = new CheapRuler(lngLatPolygons[0][0][0][1], 'meters');
557
+ const [kx, ky] = lngLatScale(lngLatPolygons[0][0][0][1]);
474
558
  if (geometry.type === 'Point' || geometry.type === 'MultiPoint' || geometry.type === 'LineString') {
475
559
  return pointSetToPolygonsDistance(
476
560
  (geometry.type === 'Point' ? [geometry.coordinates] : geometry.coordinates) as Array<[number, number]>,
477
- geometry.type === 'LineString' /*isLine*/, lngLatPolygons, ruler);
561
+ geometry.type === 'LineString' /*isLine*/, lngLatPolygons, kx, ky);
478
562
  }
479
563
  if (geometry.type === 'MultiLineString') {
480
564
  let dist = Infinity;
481
565
  for (let i = 0; i < geometry.coordinates.length; i++) {
482
- const tempDist = pointSetToPolygonsDistance(geometry.coordinates[i] as Array<[number, number]>, true /*isLine*/, lngLatPolygons, ruler, dist);
566
+ const tempDist = pointSetToPolygonsDistance(geometry.coordinates[i] as Array<[number, number]>, true /*isLine*/, lngLatPolygons, kx, ky, dist);
483
567
  if (isNaN(tempDist)) return tempDist;
484
568
  if ((dist = Math.min(dist, tempDist)) === 0.0) return dist;
485
569
  }
@@ -488,7 +572,7 @@ function polygonsToGeometryDistance(originGeometry: Array<Array<Point>>, canonic
488
572
  if (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
489
573
  return polygonsToPolygonsDistance(
490
574
  (geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates) as Array<Array<Array<[number, number]>>>,
491
- lngLatPolygons, ruler);
575
+ lngLatPolygons, kx, ky);
492
576
  }
493
577
  return null;
494
578
  }
@@ -50,28 +50,27 @@ class Match implements Expression {
50
50
  labels = [labels];
51
51
  }
52
52
 
53
- const labelContext = context.concat(i);
54
53
  if ((labels as unknown[]).length === 0) {
55
- return labelContext.error('Expected at least one branch label.');
54
+ return context.error('Expected at least one branch label.', i);
56
55
  }
57
56
 
58
57
  for (const label of (labels as unknown[])) {
59
58
  if (typeof label !== 'number' && typeof label !== 'string') {
60
- return labelContext.error(`Branch labels must be numbers or strings.`);
59
+ return context.error(`Branch labels must be numbers or strings.`, i);
61
60
  } else if (typeof label === 'number' && Math.abs(label) > Number.MAX_SAFE_INTEGER) {
62
- return labelContext.error(`Branch labels must be integers no larger than ${Number.MAX_SAFE_INTEGER}.`);
61
+ return context.error(`Branch labels must be integers no larger than ${Number.MAX_SAFE_INTEGER}.`, i);
63
62
 
64
63
  } else if (typeof label === 'number' && Math.floor(label) !== label) {
65
- return labelContext.error(`Numeric branch labels must be integer values.`);
64
+ return context.error(`Numeric branch labels must be integer values.`, i);
66
65
 
67
66
  } else if (!inputType) {
68
67
  inputType = typeOf(label);
69
- } else if (labelContext.checkSubtype(inputType, typeOf(label))) {
68
+ } else if (context.checkSubtype(inputType, typeOf(label), i)) {
70
69
  return null;
71
70
  }
72
71
 
73
72
  if (typeof cases[String(label)] !== 'undefined') {
74
- return labelContext.error('Branch labels must be unique.');
73
+ return context.error('Branch labels must be unique.', i);
75
74
  }
76
75
 
77
76
  cases[String(label)] = outputs.length;
@@ -91,7 +90,7 @@ class Match implements Expression {
91
90
 
92
91
  assert(inputType && outputType);
93
92
 
94
- if (input.type.kind !== 'value' && context.concat(1).checkSubtype((inputType), input.type)) {
93
+ if (input.type.kind !== 'value' && context.checkSubtype((inputType), input.type, 1)) {
95
94
  return null;
96
95
  }
97
96
 
@@ -56,13 +56,17 @@ function isStateConstant(e: Expression): boolean {
56
56
  return result;
57
57
  }
58
58
 
59
- function isGlobalPropertyConstant(e: Expression, properties: Array<string>): boolean {
60
- if (e instanceof CompoundExpression && properties.includes(e.name)) { return false; }
59
+ function isGlobalPropertyConstantSet(e: Expression, properties: ReadonlySet<string>): boolean {
60
+ if (e instanceof CompoundExpression && properties.has(e.name)) { return false; }
61
61
  let result = true;
62
62
  e.eachChild((arg) => {
63
- if (result && !isGlobalPropertyConstant(arg, properties)) { result = false; }
63
+ if (result && !isGlobalPropertyConstantSet(arg, properties)) { result = false; }
64
64
  });
65
65
  return result;
66
66
  }
67
67
 
68
- export {isFeatureConstant, isGlobalPropertyConstant, isStateConstant};
68
+ function isGlobalPropertyConstant(e: Expression, properties: Array<string>): boolean {
69
+ return isGlobalPropertyConstantSet(e, new Set(properties));
70
+ }
71
+
72
+ export {isFeatureConstant, isGlobalPropertyConstant, isGlobalPropertyConstantSet, isStateConstant};