@mapbox/mapbox-gl-style-spec 13.12.0 → 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.
- package/CHANGELOG.md +13 -0
- package/dist/index.es.js +1201 -297
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1201 -297
- package/dist/index.js.map +1 -1
- package/expression/compound_expression.js +5 -5
- package/expression/definitions/assertion.js +3 -4
- package/expression/definitions/at.js +3 -3
- package/expression/definitions/case.js +3 -6
- package/expression/definitions/coalesce.js +3 -4
- package/expression/definitions/coercion.js +3 -4
- package/expression/definitions/collator.js +5 -5
- package/expression/definitions/comparison.js +3 -3
- package/expression/definitions/format.js +3 -3
- package/expression/definitions/format_section_override.js +3 -4
- package/expression/definitions/image.js +6 -8
- package/expression/definitions/in.js +4 -4
- package/expression/definitions/index.js +4 -2
- package/expression/definitions/interpolate.js +3 -4
- package/expression/definitions/length.js +3 -3
- package/expression/definitions/let.js +3 -3
- package/expression/definitions/literal.js +2 -2
- package/expression/definitions/match.js +3 -6
- package/expression/definitions/number_format.js +3 -3
- package/expression/definitions/step.js +3 -4
- package/expression/definitions/var.js +2 -2
- package/expression/definitions/within.js +297 -0
- package/expression/evaluation_context.js +12 -1
- package/expression/expression.js +3 -5
- package/expression/index.js +24 -19
- package/expression/is_constant.js +5 -1
- package/expression/parsing_context.js +3 -0
- package/expression/scope.js +1 -1
- package/expression/types/resolved_image.js +2 -1
- package/feature_filter/convert.js +1 -1
- package/feature_filter/index.js +10 -5
- package/flow-typed/vector-tile.js +2 -2
- package/package.json +2 -1
- package/reference/v8.json +10 -1
- package/style-spec.js +1 -1
- package/types.js +2 -2
- package/validate/validate_expression.js +1 -1
- 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
|
}
|
package/expression/expression.js
CHANGED
|
@@ -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
|
-
*
|
|
20
|
-
* complete set of outputs is statically undecidable.
|
|
18
|
+
* false if the complete set of outputs is statically undecidable, otherwise true.
|
|
21
19
|
*/
|
|
22
|
-
|
|
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};
|
package/expression/index.js
CHANGED
|
@@ -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 ||
|
package/expression/scope.js
CHANGED
|
@@ -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
|
package/feature_filter/index.js
CHANGED
|
@@ -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
|
-
|
|
6
|
-
export type FeatureFilter =
|
|
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
|
-
|
|
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
|
|
|
@@ -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;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapbox/mapbox-gl-style-spec",
|
|
3
3
|
"description": "a specification for mapbox gl styles",
|
|
4
|
-
"version": "13.
|
|
4
|
+
"version": "13.13.0",
|
|
5
5
|
"author": "Mapbox",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mapbox",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
|
32
32
|
"@mapbox/unitbezier": "^0.0.0",
|
|
33
|
+
"@mapbox/point-geometry": "^0.1.0",
|
|
33
34
|
"csscolorparser": "~1.0.2",
|
|
34
35
|
"json-stringify-pretty-compact": "^2.0.0",
|
|
35
36
|
"minimist": "0.0.8",
|
package/reference/v8.json
CHANGED
|
@@ -1025,7 +1025,7 @@
|
|
|
1025
1025
|
},
|
|
1026
1026
|
"symbol-sort-key": {
|
|
1027
1027
|
"type": "number",
|
|
1028
|
-
"doc": "Sorts features in ascending order based on this value. Features with
|
|
1028
|
+
"doc": "Sorts features in ascending order based on this value. Features with lower sort keys are drawn and placed first. When `icon-allow-overlap` or `text-allow-overlap` is `false`, features with a lower sort key will have priority during placement. When `icon-allow-overlap` or `text-allow-overlap` is set to `true`, features with a higher sort key will overlap over features with a lower sort key.",
|
|
1029
1029
|
"sdk-support": {
|
|
1030
1030
|
"basic functionality": {
|
|
1031
1031
|
"js": "0.53.0",
|
|
@@ -3454,6 +3454,15 @@
|
|
|
3454
3454
|
}
|
|
3455
3455
|
}
|
|
3456
3456
|
},
|
|
3457
|
+
"within": {
|
|
3458
|
+
"doc": "Returns `true` if the feature being evaluated is inside the pre-defined geometry boundary, `false` otherwise. The expression has one argument which must be a valid GeoJSON Polygon/Multi-Polygon object. The expression only evaluates on `Point` or `LineString` feature. For `Point` feature, The expression will return false if any point of the feature is on the boundary or outside the boundary. For `LineString` feature, the expression will return false if the line is fully outside the boundary, or the line is partially intersecting the boundary, which means either part of the line is outside of the boundary, or end point of the line lies on the boundary.",
|
|
3459
|
+
"group": "Decision",
|
|
3460
|
+
"sdk-support": {
|
|
3461
|
+
"basic functionality": {
|
|
3462
|
+
"js": "1.9.0"
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
},
|
|
3457
3466
|
"is-supported-script": {
|
|
3458
3467
|
"doc": "Returns `true` if the input string is expected to render legibly. Returns `false` if the input string contains sections that cannot be rendered without potential loss of meaning (e.g. Indic scripts that require complex text shaping, or right-to-left scripts if the the `mapbox-gl-rtl-text` plugin is not in use in Mapbox GL JS).",
|
|
3459
3468
|
"group": "String",
|