@mapbox/mapbox-gl-style-spec 13.10.1 → 13.13.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +32 -4
  2. package/dist/index.es.js +2244 -2406
  3. package/dist/index.es.js.map +1 -1
  4. package/dist/index.js +2244 -2405
  5. package/dist/index.js.map +1 -1
  6. package/empty.js +29 -0
  7. package/error/parsing_error.js +5 -2
  8. package/error/validation_error.js +6 -3
  9. package/expression/compound_expression.js +5 -5
  10. package/expression/definitions/assertion.js +3 -4
  11. package/expression/definitions/at.js +3 -3
  12. package/expression/definitions/case.js +3 -6
  13. package/expression/definitions/coalesce.js +3 -4
  14. package/expression/definitions/coercion.js +3 -4
  15. package/expression/definitions/collator.js +5 -5
  16. package/expression/definitions/comparison.js +3 -3
  17. package/expression/definitions/format.js +3 -3
  18. package/expression/definitions/format_section_override.js +3 -4
  19. package/expression/definitions/image.js +6 -8
  20. package/expression/definitions/in.js +4 -4
  21. package/expression/definitions/index.js +4 -2
  22. package/expression/definitions/interpolate.js +3 -4
  23. package/expression/definitions/length.js +3 -3
  24. package/expression/definitions/let.js +3 -3
  25. package/expression/definitions/literal.js +2 -2
  26. package/expression/definitions/match.js +3 -6
  27. package/expression/definitions/number_format.js +3 -3
  28. package/expression/definitions/step.js +3 -4
  29. package/expression/definitions/var.js +2 -2
  30. package/expression/definitions/within.js +297 -0
  31. package/expression/evaluation_context.js +12 -1
  32. package/expression/expression.js +3 -5
  33. package/expression/index.js +24 -19
  34. package/expression/is_constant.js +5 -1
  35. package/expression/parsing_context.js +3 -0
  36. package/expression/scope.js +1 -1
  37. package/expression/types/resolved_image.js +2 -1
  38. package/feature_filter/convert.js +1 -1
  39. package/feature_filter/index.js +10 -5
  40. package/flow-typed/offscreen-canvas.js +9 -0
  41. package/flow-typed/vector-tile.js +2 -2
  42. package/function/convert.js +8 -2
  43. package/package.json +2 -1
  44. package/reference/v8.json +166 -168
  45. package/style-spec.js +3 -1
  46. package/types.js +7 -3
  47. package/validate/validate_expression.js +1 -1
  48. package/validate/validate_source.js +22 -2
  49. package/visit.js +2 -2
@@ -0,0 +1,297 @@
1
+ // @flow
2
+
3
+ import {isValue} from '../values';
4
+ import type {Type} from '../types';
5
+ import {BooleanType} from '../types';
6
+ import type {Expression} from '../expression';
7
+ import type ParsingContext from '../parsing_context';
8
+ import type EvaluationContext from '../evaluation_context';
9
+ import type {GeoJSON, GeoJSONPolygon, GeoJSONMultiPolygon} from '@mapbox/geojson-types';
10
+ import MercatorCoordinate from '../../../geo/mercator_coordinate';
11
+ import EXTENT from '../../../data/extent';
12
+ import Point from '@mapbox/point-geometry';
13
+ import type {CanonicalTileID} from '../../../source/tile_id';
14
+
15
+ type GeoJSONPolygons =| GeoJSONPolygon | GeoJSONMultiPolygon;
16
+
17
+ // minX, minY, maxX, maxY
18
+ type BBox = [number, number, number, number];
19
+ function updateBBox(bbox: BBox, coord: Point) {
20
+ bbox[0] = Math.min(bbox[0], coord[0]);
21
+ bbox[1] = Math.min(bbox[1], coord[1]);
22
+ bbox[2] = Math.max(bbox[2], coord[0]);
23
+ bbox[3] = Math.max(bbox[3], coord[1]);
24
+ }
25
+
26
+ function boxWithinBox(bbox1: BBox, bbox2: BBox) {
27
+ if (bbox1[0] <= bbox2[0]) return false;
28
+ if (bbox1[2] >= bbox2[2]) return false;
29
+ if (bbox1[1] <= bbox2[1]) return false;
30
+ if (bbox1[3] >= bbox2[3]) return false;
31
+ return true;
32
+ }
33
+
34
+ function getTileCoordinates(p, canonical: CanonicalTileID) {
35
+ const coord = MercatorCoordinate.fromLngLat({lng: p[0], lat: p[1]}, 0);
36
+ const tilesAtZoom = Math.pow(2, canonical.z);
37
+ return [Math.round(coord.x * tilesAtZoom * EXTENT), Math.round(coord.y * tilesAtZoom * EXTENT)];
38
+ }
39
+
40
+ function onBoundary(p, p1, p2) {
41
+ const x1 = p[0] - p1[0];
42
+ const y1 = p[1] - p1[1];
43
+ const x2 = p[0] - p2[0];
44
+ const y2 = p[1] - p2[1];
45
+ return (x1 * y2 - x2 * y1 === 0) && (x1 * x2 <= 0) && (y1 * y2 <= 0);
46
+ }
47
+
48
+ function rayIntersect(p, p1, p2) {
49
+ return ((p1[1] > p[1]) !== (p2[1] > p[1])) && (p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0]);
50
+ }
51
+
52
+ // ray casting algorithm for detecting if point is in polygon
53
+ function pointWithinPolygon(point, rings) {
54
+ let inside = false;
55
+ for (let i = 0, len = rings.length; i < len; i++) {
56
+ const ring = rings[i];
57
+ for (let j = 0, len2 = ring.length; j < len2 - 1; j++) {
58
+ if (onBoundary(point, ring[j], ring[j + 1])) return false;
59
+ if (rayIntersect(point, ring[j], ring[j + 1])) inside = !inside;
60
+ }
61
+ }
62
+ return inside;
63
+ }
64
+
65
+ function pointWithinPolygons(point, polygons) {
66
+ for (let i = 0; i < polygons.length; i++) {
67
+ if (pointWithinPolygon(point, polygons[i])) return true;
68
+ }
69
+ return false;
70
+ }
71
+
72
+ function perp(v1, v2) {
73
+ return (v1[0] * v2[1] - v1[1] * v2[0]);
74
+ }
75
+
76
+ // check if p1 and p2 are in different sides of line segment q1->q2
77
+ function twoSided(p1, p2, q1, q2) {
78
+ // q1->p1 (x1, y1), q1->p2 (x2, y2), q1->q2 (x3, y3)
79
+ const x1 = p1[0] - q1[0];
80
+ const y1 = p1[1] - q1[1];
81
+ const x2 = p2[0] - q1[0];
82
+ const y2 = p2[1] - q1[1];
83
+ const x3 = q2[0] - q1[0];
84
+ const y3 = q2[1] - q1[1];
85
+ if ((x1 * y3 - x3 * y1) * (x2 * y3 - x3 * y2) < 0) return true;
86
+ return false;
87
+ }
88
+ // a, b are end points for line segment1, c and d are end points for line segment2
89
+ function lineIntersectLine(a, b, c, d) {
90
+ // check if two segments are parallel or not
91
+ // precondition is end point a, b is inside polygon, if line a->b is
92
+ // parallel to polygon edge c->d, then a->b won't intersect with c->d
93
+ const vectorP = [b[0] - a[0], b[1] - a[1]];
94
+ const vectorQ = [d[0] - c[0], d[1] - c[1]];
95
+ if (perp(vectorQ, vectorP) === 0) return false;
96
+
97
+ // If lines are intersecting with each other, the relative location should be:
98
+ // a and b lie in different sides of segment c->d
99
+ // c and d lie in different sides of segment a->b
100
+ if (twoSided(a, b, c, d) && twoSided(c, d, a, b)) return true;
101
+ return false;
102
+ }
103
+
104
+ function lineIntersectPolygon(p1, p2, polygon) {
105
+ for (const ring of polygon) {
106
+ // loop through every edge of the ring
107
+ for (let j = 0; j < ring.length - 1; ++j) {
108
+ if (lineIntersectLine(p1, p2, ring[j], ring[j + 1])) {
109
+ return true;
110
+ }
111
+ }
112
+ }
113
+ return false;
114
+ }
115
+
116
+ function lineStringWithinPolygon(line, polygon) {
117
+ // First, check if geometry points of line segments are all inside polygon
118
+ for (let i = 0; i < line.length; ++i) {
119
+ if (!pointWithinPolygon(line[i], polygon)) {
120
+ return false;
121
+ }
122
+ }
123
+
124
+ // Second, check if there is line segment intersecting polygon edge
125
+ for (let i = 0; i < line.length - 1; ++i) {
126
+ if (lineIntersectPolygon(line[i], line[i + 1], polygon)) {
127
+ return false;
128
+ }
129
+ }
130
+ return true;
131
+ }
132
+
133
+ function lineStringWithinPolygons(line, polygons) {
134
+ for (let i = 0; i < polygons.length; i++) {
135
+ if (lineStringWithinPolygon(line, polygons[i])) return true;
136
+ }
137
+ return false;
138
+ }
139
+
140
+ function getTilePolygon(coordinates, bbox, canonical) {
141
+ const polygon = [];
142
+ for (let i = 0; i < coordinates.length; i++) {
143
+ const ring = [];
144
+ for (let j = 0; j < coordinates[i].length; j++) {
145
+ const coord = getTileCoordinates(coordinates[i][j], canonical);
146
+ updateBBox(bbox, coord);
147
+ ring.push(coord);
148
+ }
149
+ polygon.push(ring);
150
+ }
151
+ return polygon;
152
+ }
153
+
154
+ function getTilePolygons(coordinates, bbox, canonical) {
155
+ const polygons = [];
156
+ for (let i = 0; i < coordinates.length; i++) {
157
+ const polygon = getTilePolygon(coordinates[i], bbox, canonical);
158
+ polygons.push(polygon);
159
+ }
160
+ return polygons;
161
+ }
162
+
163
+ function pointsWithinPolygons(ctx: EvaluationContext, polygonGeometry: GeoJSONPolygons) {
164
+ const pointBBox = [Infinity, Infinity, -Infinity, -Infinity];
165
+ const polyBBox = [Infinity, Infinity, -Infinity, -Infinity];
166
+ const canonical = ctx.canonicalID();
167
+ const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];
168
+ const tilePoints = [];
169
+
170
+ for (const points of ctx.geometry()) {
171
+ for (const point of points) {
172
+ const p = [point.x + shifts[0], point.y + shifts[1]];
173
+ updateBBox(pointBBox, p);
174
+ tilePoints.push(p);
175
+ }
176
+ }
177
+
178
+ if (polygonGeometry.type === 'Polygon') {
179
+ const tilePolygon = getTilePolygon(polygonGeometry.coordinates, polyBBox, canonical);
180
+ if (!boxWithinBox(pointBBox, polyBBox)) return false;
181
+
182
+ for (const point of tilePoints) {
183
+ if (!pointWithinPolygon(point, tilePolygon)) return false;
184
+ }
185
+ }
186
+
187
+ if (polygonGeometry.type === 'MultiPolygon') {
188
+ const tilePolygons = getTilePolygons(polygonGeometry.coordinates, polyBBox, canonical);
189
+ if (!boxWithinBox(pointBBox, polyBBox)) return false;
190
+
191
+ for (const point of tilePoints) {
192
+ if (!pointWithinPolygons(point, tilePolygons)) return false;
193
+ }
194
+ }
195
+
196
+ return true;
197
+ }
198
+
199
+ function linesWithinPolygons(ctx: EvaluationContext, polygonGeometry: GeoJSONPolygons) {
200
+ const lineBBox = [Infinity, Infinity, -Infinity, -Infinity];
201
+ const polyBBox = [Infinity, Infinity, -Infinity, -Infinity];
202
+
203
+ const canonical = ctx.canonicalID();
204
+ const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];
205
+ const tileLines = [];
206
+
207
+ for (const line of ctx.geometry()) {
208
+ const tileLine = [];
209
+ for (const point of line) {
210
+ const p = [point.x + shifts[0], point.y + shifts[1]];
211
+ updateBBox(lineBBox, p);
212
+ tileLine.push(p);
213
+ }
214
+ tileLines.push(tileLine);
215
+ }
216
+
217
+ if (polygonGeometry.type === 'Polygon') {
218
+ const tilePolygon = getTilePolygon(polygonGeometry.coordinates, polyBBox, canonical);
219
+ if (!boxWithinBox(lineBBox, polyBBox)) return false;
220
+
221
+ for (const line of tileLines) {
222
+ if (!lineStringWithinPolygon(line, tilePolygon)) return false;
223
+ }
224
+ }
225
+
226
+ if (polygonGeometry.type === 'MultiPolygon') {
227
+ const tilePolygons = getTilePolygons(polygonGeometry.coordinates, polyBBox, canonical);
228
+
229
+ if (!boxWithinBox(lineBBox, polyBBox)) return false;
230
+
231
+ for (const line of tileLines) {
232
+ if (!lineStringWithinPolygons(line, tilePolygons)) return false;
233
+ }
234
+ }
235
+ return true;
236
+
237
+ }
238
+
239
+ class Within implements Expression {
240
+ type: Type;
241
+ geojson: GeoJSON
242
+ geometries: GeoJSONPolygons;
243
+
244
+ constructor(geojson: GeoJSON, geometries: GeoJSONPolygons) {
245
+ this.type = BooleanType;
246
+ this.geojson = geojson;
247
+ this.geometries = geometries;
248
+ }
249
+
250
+ static parse(args: $ReadOnlyArray<mixed>, context: ParsingContext) {
251
+ if (args.length !== 2)
252
+ return context.error(`'within' expression requires exactly one argument, but found ${args.length - 1} instead.`);
253
+ if (isValue(args[1])) {
254
+ const geojson = (args[1]: Object);
255
+ if (geojson.type === 'FeatureCollection') {
256
+ for (let i = 0; i < geojson.features.length; ++i) {
257
+ const type = geojson.features[i].geometry.type;
258
+ if (type === 'Polygon' || type === 'MultiPolygon') {
259
+ return new Within(geojson, geojson.features[i].geometry);
260
+ }
261
+ }
262
+ } else if (geojson.type === 'Feature') {
263
+ const type = geojson.geometry.type;
264
+ if (type === 'Polygon' || type === 'MultiPolygon') {
265
+ return new Within(geojson, geojson.geometry);
266
+ }
267
+ } else if (geojson.type === 'Polygon' || geojson.type === 'MultiPolygon') {
268
+ return new Within(geojson, geojson);
269
+ }
270
+ }
271
+ return context.error(`'within' expression requires valid geojson object that contains polygon geometry type.`);
272
+ }
273
+
274
+ evaluate(ctx: EvaluationContext) {
275
+ if (ctx.geometry() != null && ctx.canonicalID() != null) {
276
+ if (ctx.geometryType() === 'Point') {
277
+ return pointsWithinPolygons(ctx, this.geometries);
278
+ } else if (ctx.geometryType() === 'LineString') {
279
+ return linesWithinPolygons(ctx, this.geometries);
280
+ }
281
+ }
282
+ return false;
283
+ }
284
+
285
+ eachChild() {}
286
+
287
+ outputDefined(): boolean {
288
+ return true;
289
+ }
290
+
291
+ serialize(): Array<mixed> {
292
+ return ["within", this.geojson];
293
+ }
294
+
295
+ }
296
+
297
+ export default Within;
@@ -3,6 +3,7 @@
3
3
  import {Color} from './values';
4
4
  import type {FormattedSection} from './types/formatted';
5
5
  import type {GlobalProperties, Feature, FeatureState} from './index';
6
+ import type {CanonicalTileID} from '../../source/tile_id';
6
7
 
7
8
  const geometryTypes = ['Unknown', 'Point', 'LineString', 'Polygon'];
8
9
 
@@ -12,8 +13,9 @@ class EvaluationContext {
12
13
  featureState: ?FeatureState;
13
14
  formattedSection: ?FormattedSection;
14
15
  availableImages: ?Array<string>;
16
+ canonical: ?CanonicalTileID;
15
17
 
16
- _parseColorCache: {[string]: ?Color};
18
+ _parseColorCache: {[_: string]: ?Color};
17
19
 
18
20
  constructor() {
19
21
  this.globals = (null: any);
@@ -22,6 +24,7 @@ class EvaluationContext {
22
24
  this.formattedSection = null;
23
25
  this._parseColorCache = {};
24
26
  this.availableImages = null;
27
+ this.canonical = null;
25
28
  }
26
29
 
27
30
  id() {
@@ -32,6 +35,14 @@ class EvaluationContext {
32
35
  return this.feature ? typeof this.feature.type === 'number' ? geometryTypes[this.feature.type] : this.feature.type : null;
33
36
  }
34
37
 
38
+ geometry() {
39
+ return this.feature && 'geometry' in this.feature ? this.feature.geometry : null;
40
+ }
41
+
42
+ canonicalID() {
43
+ return this.canonical;
44
+ }
45
+
35
46
  properties() {
36
47
  return this.feature && this.feature.properties || {};
37
48
  }
@@ -1,7 +1,6 @@
1
1
  // @flow
2
2
 
3
3
  import type {Type} from './types';
4
- import type {Value} from './values';
5
4
  import type ParsingContext from './parsing_context';
6
5
  import type EvaluationContext from './evaluation_context';
7
6
 
@@ -16,14 +15,13 @@ export interface Expression {
16
15
 
17
16
  /**
18
17
  * Statically analyze the expression, attempting to enumerate possible outputs. Returns
19
- * an array of values plus the sentinel value `undefined`, used to indicate that the
20
- * complete set of outputs is statically undecidable.
18
+ * false if the complete set of outputs is statically undecidable, otherwise true.
21
19
  */
22
- possibleOutputs(): Array<Value | void>;
20
+ outputDefined(): boolean;
23
21
 
24
22
  serialize(): SerializedExpression;
25
23
  }
26
24
 
27
25
  export type ExpressionParser = (args: $ReadOnlyArray<mixed>, context: ParsingContext) => ?Expression;
28
26
  export type ExpressionRegistration = Class<Expression> & { +parse: ExpressionParser };
29
- export type ExpressionRegistry = {[string]: ExpressionRegistration};
27
+ export type ExpressionRegistry = {[_: string]: ExpressionRegistration};
@@ -25,21 +25,24 @@ import type {Result} from '../util/result';
25
25
  import type {InterpolationType} from './definitions/interpolate';
26
26
  import type {PropertyValueSpecification} from '../types';
27
27
  import type {FormattedSection} from './types/formatted';
28
+ import type Point from '@mapbox/point-geometry';
29
+ import type {CanonicalTileID} from '../../source/tile_id';
28
30
 
29
31
  export type Feature = {
30
32
  +type: 1 | 2 | 3 | 'Unknown' | 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon',
31
33
  +id?: any,
32
- +properties: {[string]: any},
33
- +patterns?: {[string]: {"min": string, "mid": string, "max": string}}
34
+ +properties: {[_: string]: any},
35
+ +patterns?: {[_: string]: {"min": string, "mid": string, "max": string}},
36
+ +geometry?: Array<Array<Point>>
34
37
  };
35
38
 
36
- export type FeatureState = {[string]: any};
39
+ export type FeatureState = {[_: string]: any};
37
40
 
38
41
  export type GlobalProperties = $ReadOnly<{
39
42
  zoom: number,
40
43
  heatmapDensity?: number,
41
44
  lineProgress?: number,
42
- isSupportedScript?: (string) => boolean,
45
+ isSupportedScript?: (_: string) => boolean,
43
46
  accumulated?: Value
44
47
  }>;
45
48
 
@@ -49,7 +52,7 @@ export class StyleExpression {
49
52
  _evaluator: EvaluationContext;
50
53
  _defaultValue: Value;
51
54
  _warningHistory: {[key: string]: boolean};
52
- _enumValues: ?{[string]: any};
55
+ _enumValues: ?{[_: string]: any};
53
56
 
54
57
  constructor(expression: Expression, propertySpec: ?StylePropertySpecification) {
55
58
  this.expression = expression;
@@ -59,20 +62,22 @@ export class StyleExpression {
59
62
  this._enumValues = propertySpec && propertySpec.type === 'enum' ? propertySpec.values : null;
60
63
  }
61
64
 
62
- evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
65
+ evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
63
66
  this._evaluator.globals = globals;
64
67
  this._evaluator.feature = feature;
65
68
  this._evaluator.featureState = featureState;
69
+ this._evaluator.canonical = canonical;
66
70
  this._evaluator.availableImages = availableImages || null;
67
71
  this._evaluator.formattedSection = formattedSection;
68
72
 
69
73
  return this.expression.evaluate(this._evaluator);
70
74
  }
71
75
 
72
- evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
76
+ evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
73
77
  this._evaluator.globals = globals;
74
78
  this._evaluator.feature = feature || null;
75
79
  this._evaluator.featureState = featureState || null;
80
+ this._evaluator.canonical = canonical;
76
81
  this._evaluator.availableImages = availableImages || null;
77
82
  this._evaluator.formattedSection = formattedSection || null;
78
83
 
@@ -138,12 +143,12 @@ export class ZoomConstantExpression<Kind: EvaluationKind> {
138
143
  this.isStateDependent = kind !== ('constant': EvaluationKind) && !isConstant.isStateConstant(expression.expression);
139
144
  }
140
145
 
141
- evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
142
- return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, availableImages, formattedSection);
146
+ evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
147
+ return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
143
148
  }
144
149
 
145
- evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
146
- return this._styleExpression.evaluate(globals, feature, featureState, availableImages, formattedSection);
150
+ evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
151
+ return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
147
152
  }
148
153
  }
149
154
 
@@ -163,12 +168,12 @@ export class ZoomDependentExpression<Kind: EvaluationKind> {
163
168
  this.interpolationType = interpolationType;
164
169
  }
165
170
 
166
- evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
167
- return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, availableImages, formattedSection);
171
+ evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
172
+ return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
168
173
  }
169
174
 
170
- evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
171
- return this._styleExpression.evaluate(globals, feature, featureState, availableImages, formattedSection);
175
+ evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
176
+ return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
172
177
  }
173
178
 
174
179
  interpolationFactor(input: number, lower: number, upper: number): number {
@@ -182,18 +187,18 @@ export class ZoomDependentExpression<Kind: EvaluationKind> {
182
187
 
183
188
  export type ConstantExpression = {
184
189
  kind: 'constant',
185
- +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>) => any,
190
+ +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>) => any,
186
191
  }
187
192
 
188
193
  export type SourceExpression = {
189
194
  kind: 'source',
190
195
  isStateDependent: boolean,
191
- +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
196
+ +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
192
197
  };
193
198
 
194
199
  export type CameraExpression = {
195
200
  kind: 'camera',
196
- +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>) => any,
201
+ +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>) => any,
197
202
  +interpolationFactor: (input: number, lower: number, upper: number) => number,
198
203
  zoomStops: Array<number>,
199
204
  interpolationType: ?InterpolationType
@@ -202,7 +207,7 @@ export type CameraExpression = {
202
207
  export type CompositeExpression = {
203
208
  kind: 'composite',
204
209
  isStateDependent: boolean,
205
- +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
210
+ +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
206
211
  +interpolationFactor: (input: number, lower: number, upper: number) => number,
207
212
  zoomStops: Array<number>,
208
213
  interpolationType: ?InterpolationType
@@ -1,7 +1,7 @@
1
1
  // @flow
2
2
 
3
3
  import CompoundExpression from './compound_expression';
4
-
4
+ import Within from './definitions/within';
5
5
  import type {Expression} from './expression.js';
6
6
 
7
7
  function isFeatureConstant(e: Expression) {
@@ -23,6 +23,10 @@ function isFeatureConstant(e: Expression) {
23
23
  }
24
24
  }
25
25
 
26
+ if (e instanceof Within) {
27
+ return false;
28
+ }
29
+
26
30
  let result = true;
27
31
  e.eachChild(arg => {
28
32
  if (result && !isFeatureConstant(arg)) { result = false; }
@@ -9,6 +9,7 @@ import Coercion from './definitions/coercion';
9
9
  import EvaluationContext from './evaluation_context';
10
10
  import CompoundExpression from './compound_expression';
11
11
  import CollatorExpression from './definitions/collator';
12
+ import Within from './definitions/within';
12
13
  import {isGlobalPropertyConstant, isFeatureConstant} from './is_constant';
13
14
  import Var from './definitions/var';
14
15
 
@@ -201,6 +202,8 @@ function isConstant(expression: Expression) {
201
202
  // generally shouldn't change between executions, we can't serialize them
202
203
  // as constant expressions because results change based on environment.
203
204
  return false;
205
+ } else if (expression instanceof Within) {
206
+ return false;
204
207
  }
205
208
 
206
209
  const isTypeAnnotation = expression instanceof Coercion ||
@@ -8,7 +8,7 @@ import type {Expression} from './expression';
8
8
  */
9
9
  class Scope {
10
10
  parent: ?Scope;
11
- bindings: {[string]: Expression};
11
+ bindings: {[_: string]: Expression};
12
12
  constructor(parent?: Scope, bindings: Array<[string, Expression]> = []) {
13
13
  this.parent = parent;
14
14
  this.bindings = {};
@@ -18,7 +18,8 @@ export default class ResolvedImage {
18
18
  return this.name;
19
19
  }
20
20
 
21
- static fromString(name: string): ResolvedImage {
21
+ static fromString(name: string): ResolvedImage | null {
22
+ if (!name) return null; // treat empty values as no image
22
23
  return new ResolvedImage({name, available: false});
23
24
  }
24
25
 
@@ -4,7 +4,7 @@ import {isExpressionFilter} from './index';
4
4
 
5
5
  import type {FilterSpecification} from '../types';
6
6
 
7
- type ExpectedTypes = {[string]: 'string' | 'number' | 'boolean'};
7
+ type ExpectedTypes = {[_: string]: 'string' | 'number' | 'boolean'};
8
8
 
9
9
  /**
10
10
  * Convert the given legacy filter to (the JSON representation of) an
@@ -1,9 +1,11 @@
1
1
  // @flow
2
2
 
3
3
  import {createExpression} from '../expression';
4
+ import type {GlobalProperties, Feature} from '../expression';
5
+ import type {CanonicalTileID} from '../../source/tile_id';
4
6
 
5
- import type {GlobalProperties} from '../expression';
6
- export type FeatureFilter = (globalProperties: GlobalProperties, feature: VectorTileFeature) => boolean;
7
+ type FilterExpression = (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => boolean;
8
+ export type FeatureFilter ={filter: FilterExpression, needGeometry: boolean};
7
9
 
8
10
  export default createFilter;
9
11
  export {isExpressionFilter};
@@ -21,7 +23,8 @@ function isExpressionFilter(filter: any) {
21
23
  return filter.length >= 2 && filter[1] !== '$id' && filter[1] !== '$type';
22
24
 
23
25
  case 'in':
24
- return filter.length >= 3 && Array.isArray(filter[2]);
26
+ return filter.length >= 3 && (typeof filter[1] !== 'string' || Array.isArray(filter[2]));
27
+
25
28
  case '!in':
26
29
  case '!has':
27
30
  case 'none':
@@ -71,7 +74,7 @@ const filterSpec = {
71
74
  */
72
75
  function createFilter(filter: any): FeatureFilter {
73
76
  if (filter === null || filter === undefined) {
74
- return () => true;
77
+ return {filter: () => true, needGeometry: false};
75
78
  }
76
79
 
77
80
  if (!isExpressionFilter(filter)) {
@@ -82,7 +85,9 @@ function createFilter(filter: any): FeatureFilter {
82
85
  if (compiled.result === 'error') {
83
86
  throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', '));
84
87
  } else {
85
- return (globalProperties: GlobalProperties, feature: VectorTileFeature) => compiled.value.evaluate(globalProperties, feature);
88
+ const needGeometry = Array.isArray(filter) && filter.length !== 0 && filter[0] === 'within';
89
+ return {filter: (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => compiled.value.evaluate(globalProperties, feature, {}, canonical),
90
+ needGeometry};
86
91
  }
87
92
  }
88
93
 
@@ -0,0 +1,9 @@
1
+ // @flow strict
2
+
3
+ declare class OffscreenCanvas {
4
+ width: number;
5
+ height: number;
6
+
7
+ constructor(width: number, height: number): OffscreenCanvas;
8
+ getContext(contextType: '2d'): CanvasRenderingContext2D;
9
+ }
@@ -3,7 +3,7 @@ import type Point from '@mapbox/point-geometry';
3
3
  import type { GeoJSONFeature } from '@mapbox/geojson-types';
4
4
 
5
5
  declare interface VectorTile {
6
- layers: {[string]: VectorTileLayer};
6
+ layers: {[_: string]: VectorTileLayer};
7
7
  }
8
8
 
9
9
  declare interface VectorTileLayer {
@@ -18,7 +18,7 @@ declare interface VectorTileFeature {
18
18
  extent: number;
19
19
  type: 1 | 2 | 3;
20
20
  id: number;
21
- properties: {[string]: string | number | boolean};
21
+ properties: {[_: string]: string | number | boolean};
22
22
 
23
23
  loadGeometry(): Array<Array<Point>>;
24
24
  toGeoJSON(x: number, y: number, z: number): GeoJSONFeature;
@@ -153,7 +153,12 @@ function convertPropertyFunction(parameters, propertySpec, stops) {
153
153
  ];
154
154
  } else if (type === 'exponential') {
155
155
  const base = parameters.base !== undefined ? parameters.base : 1;
156
- const expression = [getInterpolateOperator(parameters), ['exponential', base], ['number', get]];
156
+ const expression = [
157
+ getInterpolateOperator(parameters),
158
+ base === 1 ? ["linear"] : ["exponential", base],
159
+ ["number", get]
160
+ ];
161
+
157
162
  for (const stop of stops) {
158
163
  appendStopPair(expression, stop[0], stop[1], false);
159
164
  }
@@ -177,7 +182,8 @@ function convertZoomFunction(parameters, propertySpec, stops, input = ['zoom'])
177
182
  isStep = true;
178
183
  } else if (type === 'exponential') {
179
184
  const base = parameters.base !== undefined ? parameters.base : 1;
180
- expression = [getInterpolateOperator(parameters), ['exponential', base], input];
185
+ expression = [getInterpolateOperator(parameters), base === 1 ? ["linear"] : ["exponential", base], input];
186
+
181
187
  } else {
182
188
  throw new Error(`Unknown zoom function type "${type}"`);
183
189
  }