@mapbox/mapbox-gl-style-spec 13.28.0 → 14.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE.txt +47 -0
  2. package/README.md +7 -0
  3. package/data/extent.js +18 -0
  4. package/deref.js +1 -1
  5. package/diff.js +36 -15
  6. package/dist/index.cjs +5839 -2015
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.es.js +5837 -2011
  9. package/dist/index.es.js.map +1 -1
  10. package/expression/compound_expression.js +2 -1
  11. package/expression/definitions/assertion.js +2 -2
  12. package/expression/definitions/coercion.js +54 -15
  13. package/expression/definitions/comparison.js +2 -0
  14. package/expression/definitions/distance.js +597 -0
  15. package/expression/definitions/format.js +2 -2
  16. package/expression/definitions/image.js +34 -14
  17. package/expression/definitions/index.js +122 -8
  18. package/expression/definitions/interpolate.js +1 -1
  19. package/expression/definitions/match.js +2 -2
  20. package/expression/definitions/within.js +21 -92
  21. package/expression/evaluation_context.js +12 -1
  22. package/expression/index.js +74 -43
  23. package/expression/is_constant.js +19 -1
  24. package/expression/parsing_context.js +20 -16
  25. package/expression/types/formatted.js +2 -2
  26. package/expression/types/resolved_image.js +19 -8
  27. package/expression/types.js +2 -1
  28. package/expression/values.js +25 -0
  29. package/feature_filter/convert.js +1 -1
  30. package/feature_filter/index.js +4 -4
  31. package/flow-typed/cheap-ruler.js +25 -0
  32. package/flow-typed/geojson.js +8 -7
  33. package/flow-typed/gl-matrix.js +4 -2
  34. package/flow-typed/kdbush.js +9 -0
  35. package/function/convert.js +23 -12
  36. package/group_by_layout.js +2 -2
  37. package/migrate/expressions.js +3 -0
  38. package/package.json +6 -3
  39. package/reference/v8.json +1771 -114
  40. package/style-spec.js +4 -3
  41. package/types.js +190 -24
  42. package/util/color.js +31 -0
  43. package/util/geometry_util.js +145 -0
  44. package/util/properties.js +10 -2
  45. package/util/random.js +12 -0
  46. package/validate/validate.js +17 -7
  47. package/validate/validate_array.js +1 -1
  48. package/validate/validate_filter.js +4 -12
  49. package/validate/validate_function.js +2 -2
  50. package/validate/validate_import.js +31 -0
  51. package/validate/validate_layer.js +3 -2
  52. package/validate/validate_lights.js +84 -0
  53. package/validate/validate_model.js +38 -0
  54. package/validate/validate_property.js +18 -4
  55. package/validate/validate_source.js +3 -2
  56. package/validate/validate_style.js +29 -0
  57. package/validate_mapbox_api_supported.js +55 -11
  58. package/validate_style.js +4 -0
  59. package/validate_style.min.js +11 -19
  60. package/visit.js +3 -2
@@ -15,7 +15,12 @@ import definitions from './definitions/index.js';
15
15
  import * as isConstant from './is_constant.js';
16
16
  import RuntimeError from './runtime_error.js';
17
17
  import {success, error} from '../util/result.js';
18
- import {supportsPropertyExpression, supportsZoomExpression, supportsInterpolation} from '../util/properties.js';
18
+ import {
19
+ supportsPropertyExpression,
20
+ supportsZoomExpression,
21
+ supportsLightExpression,
22
+ supportsInterpolation
23
+ } from '../util/properties.js';
19
24
 
20
25
  import type {Type, EvaluationKind} from './types.js';
21
26
  import type {Value} from './values.js';
@@ -29,25 +34,27 @@ import type Point from '@mapbox/point-geometry';
29
34
  import type {CanonicalTileID} from '../../source/tile_id.js';
30
35
  import type {FeatureDistanceData} from '../feature_filter/index.js';
31
36
 
32
- export type Feature = {
33
- +type: 1 | 2 | 3 | 'Unknown' | 'Point' | 'LineString' | 'Polygon',
34
- +id?: number | null,
35
- +properties: {[_: string]: any},
36
- +patterns?: {[_: string]: string},
37
- +geometry?: Array<Array<Point>>
38
- };
37
+ export interface Feature {
38
+ +type: 1 | 2 | 3 | 'Unknown' | 'Point' | 'LineString' | 'Polygon';
39
+ +id?: number | null;
40
+ +properties: {[_: string]: any};
41
+ +patterns?: {[_: string]: string};
42
+ +geometry?: Array<Array<Point>>;
43
+ }
39
44
 
40
45
  export type FeatureState = {[_: string]: any};
41
46
 
42
- export type GlobalProperties = $ReadOnly<{
47
+ export interface GlobalProperties {
43
48
  zoom: number,
44
49
  pitch?: number,
45
50
  heatmapDensity?: number,
46
51
  lineProgress?: number,
52
+ rasterValue?: number,
47
53
  skyRadialProgress?: number,
48
- isSupportedScript?: (_: string) => boolean,
49
- accumulated?: Value
50
- }>;
54
+ +isSupportedScript?: (_: string) => boolean,
55
+ accumulated?: Value,
56
+ brightness?: number
57
+ }
51
58
 
52
59
  export class StyleExpression {
53
60
  expression: Expression;
@@ -57,10 +64,10 @@ export class StyleExpression {
57
64
  _warningHistory: {[key: string]: boolean};
58
65
  _enumValues: ?{[_: string]: any};
59
66
 
60
- constructor(expression: Expression, propertySpec: ?StylePropertySpecification) {
67
+ constructor(expression: Expression, propertySpec: ?StylePropertySpecification, options?: ?Map<string, Expression>) {
61
68
  this.expression = expression;
62
69
  this._warningHistory = {};
63
- this._evaluator = new EvaluationContext();
70
+ this._evaluator = new EvaluationContext(options);
64
71
  this._defaultValue = propertySpec ? getDefaultValue(propertySpec) : null;
65
72
  this._enumValues = propertySpec && propertySpec.type === 'enum' ? propertySpec.values : null;
66
73
  }
@@ -124,8 +131,8 @@ export function isExpression(expression: mixed): boolean {
124
131
  *
125
132
  * @private
126
133
  */
127
- export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification): Result<StyleExpression, Array<ParsingError>> {
128
- const parser = new ParsingContext(definitions, [], propertySpec ? getExpectedType(propertySpec) : undefined);
134
+ export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification, options?: ?Map<string, Expression>): Result<StyleExpression, Array<ParsingError>> {
135
+ const parser = new ParsingContext(definitions, [], propertySpec ? getExpectedType(propertySpec) : undefined, undefined, undefined, options);
129
136
 
130
137
  // For string-valued properties, coerce to string at the top level rather than asserting.
131
138
  const parsed = parser.parse(expression, undefined, undefined, undefined,
@@ -136,18 +143,22 @@ export function createExpression(expression: mixed, propertySpec: ?StyleProperty
136
143
  return error(parser.errors);
137
144
  }
138
145
 
139
- return success(new StyleExpression(parsed, propertySpec));
146
+ return success(new StyleExpression(parsed, propertySpec, options));
140
147
  }
141
148
 
142
149
  export class ZoomConstantExpression<Kind: EvaluationKind> {
143
150
  kind: Kind;
144
151
  isStateDependent: boolean;
152
+ isConfigDependent: boolean;
145
153
  _styleExpression: StyleExpression;
154
+ isLightConstant: ?boolean;
146
155
 
147
- constructor(kind: Kind, expression: StyleExpression) {
156
+ constructor(kind: Kind, expression: StyleExpression, isLightConstant: ?boolean) {
148
157
  this.kind = kind;
149
158
  this._styleExpression = expression;
159
+ this.isLightConstant = isLightConstant;
150
160
  this.isStateDependent = kind !== ('constant': EvaluationKind) && !isConstant.isStateConstant(expression.expression);
161
+ this.isConfigDependent = !isConstant.isConfigConstant(expression.expression);
151
162
  }
152
163
 
153
164
  evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
@@ -163,15 +174,19 @@ export class ZoomDependentExpression<Kind: EvaluationKind> {
163
174
  kind: Kind;
164
175
  zoomStops: Array<number>;
165
176
  isStateDependent: boolean;
177
+ isLightConstant: ?boolean;
178
+ isConfigDependent: boolean;
166
179
 
167
180
  _styleExpression: StyleExpression;
168
181
  interpolationType: ?InterpolationType;
169
182
 
170
- constructor(kind: Kind, expression: StyleExpression, zoomStops: Array<number>, interpolationType?: InterpolationType) {
183
+ constructor(kind: Kind, expression: StyleExpression, zoomStops: Array<number>, interpolationType?: InterpolationType, isLightConstant: ?boolean) {
171
184
  this.kind = kind;
172
185
  this.zoomStops = zoomStops;
173
186
  this._styleExpression = expression;
174
187
  this.isStateDependent = kind !== ('camera': EvaluationKind) && !isConstant.isStateConstant(expression.expression);
188
+ this.isLightConstant = isLightConstant;
189
+ this.isConfigDependent = !isConstant.isConfigConstant(expression.expression);
175
190
  this.interpolationType = interpolationType;
176
191
  }
177
192
 
@@ -192,33 +207,40 @@ export class ZoomDependentExpression<Kind: EvaluationKind> {
192
207
  }
193
208
  }
194
209
 
195
- export type ConstantExpression = {
210
+ export type ConstantExpression = interface {
196
211
  kind: 'constant',
212
+ isConfigDependent: boolean,
197
213
  +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>) => any,
198
214
  }
199
215
 
200
- export type SourceExpression = {
216
+ export type SourceExpression = interface {
201
217
  kind: 'source',
202
218
  isStateDependent: boolean,
219
+ isLightConstant: ?boolean;
220
+ isConfigDependent: boolean,
203
221
  +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
204
222
  };
205
223
 
206
- export type CameraExpression = {
224
+ export type CameraExpression = interface {
207
225
  kind: 'camera',
226
+ isStateDependent: boolean,
227
+ isConfigDependent: boolean,
208
228
  +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>) => any,
209
229
  +interpolationFactor: (input: number, lower: number, upper: number) => number,
210
230
  zoomStops: Array<number>,
211
231
  interpolationType: ?InterpolationType
212
232
  };
213
233
 
214
- export type CompositeExpression = {
215
- kind: 'composite',
216
- isStateDependent: boolean,
217
- +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
218
- +interpolationFactor: (input: number, lower: number, upper: number) => number,
219
- zoomStops: Array<number>,
220
- interpolationType: ?InterpolationType
221
- };
234
+ export interface CompositeExpression {
235
+ kind: 'composite';
236
+ isStateDependent: boolean;
237
+ isLightConstant: ?boolean;
238
+ isConfigDependent: boolean;
239
+ +evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any;
240
+ +interpolationFactor: (input: number, lower: number, upper: number) => number;
241
+ zoomStops: Array<number>;
242
+ interpolationType: ?InterpolationType;
243
+ }
222
244
 
223
245
  export type StylePropertyExpression =
224
246
  | ConstantExpression
@@ -226,8 +248,8 @@ export type StylePropertyExpression =
226
248
  | CameraExpression
227
249
  | CompositeExpression;
228
250
 
229
- export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification): Result<StylePropertyExpression, Array<ParsingError>> {
230
- expression = createExpression(expression, propertySpec);
251
+ export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification, options?: ?Map<string, Expression>): Result<StylePropertyExpression, Array<ParsingError>> {
252
+ expression = createExpression(expression, propertySpec, options);
231
253
  if (expression.result === 'error') {
232
254
  return expression;
233
255
  }
@@ -244,9 +266,15 @@ export function createPropertyExpression(expression: mixed, propertySpec: StyleP
244
266
  return error([new ParsingError('', 'zoom expressions not supported')]);
245
267
  }
246
268
 
269
+ const isLightConstant = isConstant.isGlobalPropertyConstant(parsed, ['measure-light']);
270
+ if (!isLightConstant && !supportsLightExpression(propertySpec)) {
271
+ return error([new ParsingError('', 'measure-light expression not supported')]);
272
+ }
273
+
274
+ const canRelaxZoomRestriction = propertySpec.expression && propertySpec.expression.relaxZoomRestriction;
247
275
  const zoomCurve = findZoomCurve(parsed);
248
- if (!zoomCurve && !isZoomConstant) {
249
- return error([new ParsingError('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.')]);
276
+ if (!zoomCurve && !isZoomConstant && !canRelaxZoomRestriction) {
277
+ return error([new ParsingError('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression, or in the properties of atmosphere.')]);
250
278
  } else if (zoomCurve instanceof ParsingError) {
251
279
  return error([zoomCurve]);
252
280
  } else if (zoomCurve instanceof Interpolate && !supportsInterpolation(propertySpec)) {
@@ -255,15 +283,19 @@ export function createPropertyExpression(expression: mixed, propertySpec: StyleP
255
283
 
256
284
  if (!zoomCurve) {
257
285
  return success(isFeatureConstant ?
258
- (new ZoomConstantExpression('constant', expression.value): ConstantExpression) :
259
- (new ZoomConstantExpression('source', expression.value): SourceExpression));
286
+ // $FlowFixMe[method-unbinding]
287
+ (new ZoomConstantExpression('constant', expression.value, isLightConstant): ConstantExpression) :
288
+ // $FlowFixMe[method-unbinding]
289
+ (new ZoomConstantExpression('source', expression.value, isLightConstant): SourceExpression));
260
290
  }
261
291
 
262
292
  const interpolationType = zoomCurve instanceof Interpolate ? zoomCurve.interpolation : undefined;
263
293
 
264
294
  return success(isFeatureConstant ?
265
- (new ZoomDependentExpression('camera', expression.value, zoomCurve.labels, interpolationType): CameraExpression) :
266
- (new ZoomDependentExpression('composite', expression.value, zoomCurve.labels, interpolationType): CompositeExpression));
295
+ // $FlowFixMe[method-unbinding]
296
+ (new ZoomDependentExpression('camera', expression.value, zoomCurve.labels, interpolationType, isLightConstant): CameraExpression) :
297
+ // $FlowFixMe[method-unbinding]
298
+ (new ZoomDependentExpression('composite', expression.value, zoomCurve.labels, interpolationType, isLightConstant): CompositeExpression));
267
299
  }
268
300
 
269
301
  import {isFunction, createFunction} from '../function/index.js';
@@ -298,12 +330,12 @@ export class StylePropertyFunction<T> {
298
330
  }
299
331
  }
300
332
 
301
- export function normalizePropertyExpression<T>(value: PropertyValueSpecification<T>, specification: StylePropertySpecification): StylePropertyExpression {
333
+ export function normalizePropertyExpression<T>(value: PropertyValueSpecification<T>, specification: StylePropertySpecification, options?: ?Map<string, Expression>): StylePropertyExpression {
302
334
  if (isFunction(value)) {
303
335
  return (new StylePropertyFunction(value, specification): any);
304
336
 
305
- } else if (isExpression(value)) {
306
- const expression = createPropertyExpression(value, specification);
337
+ } else if (isExpression(value) || (Array.isArray(value) && value.length > 0)) {
338
+ const expression = createPropertyExpression(value, specification, options);
307
339
  if (expression.result === 'error') {
308
340
  // this should have been caught in validation
309
341
  throw new Error(expression.value.map(err => `${err.key}: ${err.message}`).join(', '));
@@ -317,6 +349,7 @@ export function normalizePropertyExpression<T>(value: PropertyValueSpecification
317
349
  }
318
350
  return {
319
351
  kind: 'constant',
352
+ isConfigDependent: false,
320
353
  evaluate: () => constant
321
354
  };
322
355
  }
@@ -353,8 +386,6 @@ function findZoomCurve(expression: Expression): Step | Interpolate | ParsingErro
353
386
  const childResult = findZoomCurve(child);
354
387
  if (childResult instanceof ParsingError) {
355
388
  result = childResult;
356
- } else if (!result && childResult) {
357
- result = new ParsingError('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.');
358
389
  } else if (result && childResult && result !== childResult) {
359
390
  result = new ParsingError('', 'Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.');
360
391
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import CompoundExpression from './compound_expression.js';
4
4
  import Within from './definitions/within.js';
5
+ import Distance from './definitions/distance.js';
5
6
  import type {Expression} from './expression.js';
6
7
 
7
8
  function isFeatureConstant(e: Expression): boolean {
@@ -27,6 +28,10 @@ function isFeatureConstant(e: Expression): boolean {
27
28
  return false;
28
29
  }
29
30
 
31
+ if (e instanceof Distance) {
32
+ return false;
33
+ }
34
+
30
35
  let result = true;
31
36
  e.eachChild(arg => {
32
37
  if (result && !isFeatureConstant(arg)) { result = false; }
@@ -47,6 +52,19 @@ function isStateConstant(e: Expression): boolean {
47
52
  return result;
48
53
  }
49
54
 
55
+ function isConfigConstant(e: Expression): boolean {
56
+ if (e instanceof CompoundExpression) {
57
+ if (e.name === 'config') {
58
+ return false;
59
+ }
60
+ }
61
+ let result = true;
62
+ e.eachChild(arg => {
63
+ if (result && !isConfigConstant(arg)) { result = false; }
64
+ });
65
+ return result;
66
+ }
67
+
50
68
  function isGlobalPropertyConstant(e: Expression, properties: Array<string>): boolean {
51
69
  if (e instanceof CompoundExpression && properties.indexOf(e.name) >= 0) { return false; }
52
70
  let result = true;
@@ -56,4 +74,4 @@ function isGlobalPropertyConstant(e: Expression, properties: Array<string>): boo
56
74
  return result;
57
75
  }
58
76
 
59
- export {isFeatureConstant, isGlobalPropertyConstant, isStateConstant};
77
+ export {isFeatureConstant, isGlobalPropertyConstant, isStateConstant, isConfigConstant};
@@ -10,6 +10,7 @@ import EvaluationContext from './evaluation_context.js';
10
10
  import CompoundExpression from './compound_expression.js';
11
11
  import CollatorExpression from './definitions/collator.js';
12
12
  import Within from './definitions/within.js';
13
+ import Distance from './definitions/distance.js';
13
14
  import {isGlobalPropertyConstant, isFeatureConstant} from './is_constant.js';
14
15
  import Var from './definitions/var.js';
15
16
 
@@ -26,6 +27,7 @@ class ParsingContext {
26
27
  key: string;
27
28
  scope: Scope;
28
29
  errors: Array<ParsingError>;
30
+ options: ?Map<string, Expression>;
29
31
 
30
32
  // The expected type of this expression. Provided only to allow Expression
31
33
  // implementations to infer argument types: Expression#parse() need not
@@ -38,7 +40,8 @@ class ParsingContext {
38
40
  path: Array<number> = [],
39
41
  expectedType: ?Type,
40
42
  scope: Scope = new Scope(),
41
- errors: Array<ParsingError> = []
43
+ errors: Array<ParsingError> = [],
44
+ options?: ?Map<string, Expression>
42
45
  ) {
43
46
  this.registry = registry;
44
47
  this.path = path;
@@ -46,6 +49,7 @@ class ParsingContext {
46
49
  this.scope = scope;
47
50
  this.errors = errors;
48
51
  this.expectedType = expectedType;
52
+ this.options = options;
49
53
  }
50
54
 
51
55
  /**
@@ -62,7 +66,7 @@ class ParsingContext {
62
66
  bindings?: Array<[string, Expression]>,
63
67
  options: {typeAnnotation?: 'assert' | 'coerce' | 'omit'} = {}
64
68
  ): ?Expression {
65
- if (index) {
69
+ if (index || expectedType) {
66
70
  return this.concat(index, expectedType, bindings)._parse(expr, options);
67
71
  }
68
72
  return this._parse(expr, options);
@@ -73,7 +77,7 @@ class ParsingContext {
73
77
  expr = ['literal', expr];
74
78
  }
75
79
 
76
- function annotate(parsed, type, typeAnnotation: 'assert' | 'coerce' | 'omit') {
80
+ function annotate(parsed: Expression, type: Type, typeAnnotation: 'assert' | 'coerce' | 'omit') {
77
81
  if (typeAnnotation === 'assert') {
78
82
  return new Assertion(type, [parsed]);
79
83
  } else if (typeAnnotation === 'coerce') {
@@ -88,13 +92,7 @@ class ParsingContext {
88
92
  return this.error(`Expected an array with at least one element. If you wanted a literal array, use ["literal", []].`);
89
93
  }
90
94
 
91
- const op = expr[0];
92
- if (typeof op !== 'string') {
93
- this.error(`Expression name must be a string, but found ${typeof op} instead. If you wanted a literal array, use ["literal", [...]].`, 0);
94
- return null;
95
- }
96
-
97
- const Expr = this.registry[op];
95
+ const Expr = typeof expr[0] === 'string' ? this.registry[expr[0]] : undefined;
98
96
  if (Expr) {
99
97
  let parsed = Expr.parse(expr, this);
100
98
  if (!parsed) return null;
@@ -125,7 +123,7 @@ class ParsingContext {
125
123
  // parsed/compiled result. Expressions that expect an image should
126
124
  // not be resolved here so we can later get the available images.
127
125
  if (!(parsed instanceof Literal) && (parsed.type.kind !== 'resolvedImage') && isConstant(parsed)) {
128
- const ec = new EvaluationContext();
126
+ const ec = new EvaluationContext(this.options);
129
127
  try {
130
128
  parsed = new Literal(parsed.type, parsed.evaluate(ec));
131
129
  } catch (e) {
@@ -137,7 +135,8 @@ class ParsingContext {
137
135
  return parsed;
138
136
  }
139
137
 
140
- return this.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
138
+ // Try to parse as array
139
+ return Coercion.parse(['to-array', expr], this);
141
140
  } else if (typeof expr === 'undefined') {
142
141
  return this.error(`'undefined' value invalid. Use null instead.`);
143
142
  } else if (typeof expr === 'object') {
@@ -155,7 +154,7 @@ class ParsingContext {
155
154
  * parsing, is copied by reference rather than cloned.
156
155
  * @private
157
156
  */
158
- concat(index: number, expectedType?: ?Type, bindings?: Array<[string, Expression]>): ParsingContext {
157
+ concat(index: ?number, expectedType?: ?Type, bindings?: Array<[string, Expression]>): ParsingContext {
159
158
  const path = typeof index === 'number' ? this.path.concat(index) : this.path;
160
159
  const scope = bindings ? this.scope.concat(bindings) : this.scope;
161
160
  return new ParsingContext(
@@ -163,7 +162,8 @@ class ParsingContext {
163
162
  path,
164
163
  expectedType || null,
165
164
  scope,
166
- this.errors
165
+ this.errors,
166
+ this.options
167
167
  );
168
168
  }
169
169
 
@@ -197,12 +197,16 @@ function isConstant(expression: Expression) {
197
197
  return isConstant(expression.boundExpression);
198
198
  } else if (expression instanceof CompoundExpression && expression.name === 'error') {
199
199
  return false;
200
+ } else if (expression instanceof CompoundExpression && expression.name === 'config') {
201
+ return false;
200
202
  } else if (expression instanceof CollatorExpression) {
201
203
  // Although the results of a Collator expression with fixed arguments
202
204
  // generally shouldn't change between executions, we can't serialize them
203
205
  // as constant expressions because results change based on environment.
204
206
  return false;
205
- } else if (expression instanceof Within) {
207
+ } else if (expression instanceof Within) {
208
+ return false;
209
+ } else if (expression instanceof Distance) {
206
210
  return false;
207
211
  }
208
212
 
@@ -229,5 +233,5 @@ function isConstant(expression: Expression) {
229
233
  }
230
234
 
231
235
  return isFeatureConstant(expression) &&
232
- isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center']);
236
+ isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'raster-value', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center', 'measure-light']);
233
237
  }
@@ -33,7 +33,7 @@ export default class Formatted {
33
33
  isEmpty(): boolean {
34
34
  if (this.sections.length === 0) return true;
35
35
  return !this.sections.some(section => section.text.length !== 0 ||
36
- (section.image && section.image.name.length !== 0));
36
+ (section.image && section.image.namePrimary.length !== 0));
37
37
  }
38
38
 
39
39
  static factory(text: Formatted | string): Formatted {
@@ -53,7 +53,7 @@ export default class Formatted {
53
53
  const serialized: Array<mixed> = ["format"];
54
54
  for (const section of this.sections) {
55
55
  if (section.image) {
56
- serialized.push(["image", section.image.name]);
56
+ serialized.push(["image", section.image.namePrimary]);
57
57
  continue;
58
58
  }
59
59
  serialized.push(section.text);
@@ -1,29 +1,40 @@
1
1
  // @flow
2
2
 
3
3
  export type ResolvedImageOptions = {
4
- name: string,
4
+ namePrimary: string,
5
+ nameSecondary: ?string,
5
6
  available: boolean
6
7
  };
7
8
 
8
9
  export default class ResolvedImage {
9
- name: string;
10
+ namePrimary: string;
11
+ nameSecondary: ?string;
10
12
  available: boolean;
11
13
 
12
14
  constructor(options: ResolvedImageOptions) {
13
- this.name = options.name;
15
+ this.namePrimary = options.namePrimary;
16
+ if (options.nameSecondary) {
17
+ this.nameSecondary = options.nameSecondary;
18
+ }
14
19
  this.available = options.available;
15
20
  }
16
21
 
17
22
  toString(): string {
18
- return this.name;
23
+ if (this.nameSecondary) {
24
+ return `[${this.namePrimary},${this.nameSecondary}]`;
25
+ }
26
+ return this.namePrimary;
19
27
  }
20
28
 
21
- static fromString(name: string): ResolvedImage | null {
22
- if (!name) return null; // treat empty values as no image
23
- return new ResolvedImage({name, available: false});
29
+ static fromString(namePrimary: string, nameSecondary: ?string): ResolvedImage | null {
30
+ if (!namePrimary) return null; // treat empty values as no image
31
+ return new ResolvedImage({namePrimary, nameSecondary, available: false});
24
32
  }
25
33
 
26
34
  serialize(): Array<string> {
27
- return ["image", this.name];
35
+ if (this.nameSecondary) {
36
+ return ["image", this.namePrimary, this.nameSecondary];
37
+ }
38
+ return ["image", this.namePrimary];
28
39
  }
29
40
  }
@@ -26,7 +26,8 @@ export type Type =
26
26
  ErrorTypeT |
27
27
  CollatorTypeT |
28
28
  FormattedTypeT |
29
- ResolvedImageTypeT
29
+ ResolvedImageTypeT |
30
+ ArrayType;
30
31
 
31
32
  export type ArrayType = {
32
33
  kind: 'array',
@@ -29,6 +29,31 @@ export function validateRGBA(r: mixed, g: mixed, b: mixed, a?: mixed): string |
29
29
  return null;
30
30
  }
31
31
 
32
+ export function validateHSLA(h: mixed, s: mixed, l: mixed, a?: mixed): string | null {
33
+ if (!(
34
+ typeof h === 'number' && h >= 0 && h <= 360
35
+ )) {
36
+ const value = typeof a === 'number' ? [h, s, l, a] : [h, s, l];
37
+ return `Invalid hsla value [${value.join(', ')}]: 'h' must be between 0 and 360.`;
38
+ }
39
+
40
+ if (!(
41
+ typeof s === 'number' && s >= 0 && s <= 100 &&
42
+ typeof l === 'number' && l >= 0 && l <= 100
43
+ )) {
44
+ const value = typeof a === 'number' ? [h, s, l, a] : [h, s, l];
45
+ return `Invalid hsla value [${value.join(', ')}]: 's', and 'l' must be between 0 and 100.`;
46
+ }
47
+
48
+ if (!(
49
+ typeof a === 'undefined' || (typeof a === 'number' && a >= 0 && a <= 1)
50
+ )) {
51
+ return `Invalid hsla value [${[h, s, l, a].join(', ')}]: 'a' must be between 0 and 1.`;
52
+ }
53
+
54
+ return null;
55
+ }
56
+
32
57
  export type Value = null | string | boolean | number | Color | Collator | Formatted | ResolvedImage | $ReadOnlyArray<Value> | { +[string]: Value }
33
58
 
34
59
  export function isValue(mixed: mixed): boolean {
@@ -161,7 +161,7 @@ function convertComparisonOp(property: string, value: any, op: string, expectedT
161
161
  return [op, get, value];
162
162
  }
163
163
 
164
- function convertInOp(property: string, values: Array<any>, negate = false) {
164
+ function convertInOp(property: string, values: Array<any>, negate: boolean = false) {
165
165
  if (values.length === 0) return negate;
166
166
 
167
167
  let get;
@@ -73,6 +73,7 @@ function createFilter(filter: any, layerType?: string = 'fill'): FeatureFilter {
73
73
  }
74
74
 
75
75
  if (!isExpressionFilter(filter)) {
76
+ // $FlowFixMe[incompatible-call]
76
77
  filter = convertFilter(filter);
77
78
  }
78
79
  const filterExp = ((filter: any): string[] | string | boolean);
@@ -253,13 +254,13 @@ function collapsedExpression(expression: any): any {
253
254
  }
254
255
 
255
256
  // Comparison function to sort numbers and strings
256
- function compare(a, b) {
257
+ function compare(a: number, b: number) {
257
258
  return a < b ? -1 : a > b ? 1 : 0;
258
259
  }
259
260
 
260
- function geometryNeeded(filter) {
261
+ function geometryNeeded(filter: Array<any> | boolean) {
261
262
  if (!Array.isArray(filter)) return false;
262
- if (filter[0] === 'within') return true;
263
+ if (filter[0] === 'within' || filter[0] === 'distance') return true;
263
264
  for (let index = 1; index < filter.length; index++) {
264
265
  if (geometryNeeded(filter[index])) return true;
265
266
  }
@@ -284,7 +285,6 @@ function convertFilter(filter: ?Array<any>): mixed {
284
285
  op === '!in' ? convertNegation(convertInOp(filter[1], filter.slice(2))) :
285
286
  op === 'has' ? convertHasOp(filter[1]) :
286
287
  op === '!has' ? convertNegation(convertHasOp(filter[1])) :
287
- op === 'within' ? filter :
288
288
  true;
289
289
  return converted;
290
290
  }
@@ -0,0 +1,25 @@
1
+ // @flow strict
2
+
3
+ declare module 'cheap-ruler' {
4
+ import type { GeoJSONPosition} from '@mapbox/geojson-types';
5
+ declare export default class CheapRuler {
6
+ kx: number;
7
+ ky: number;
8
+ constructor(lat: number, units: string): CheapRuler;
9
+ distance(a: GeoJSONPosition, b: GeoJSONPosition): number;
10
+ bearing(a: GeoJSONPosition, b: GeoJSONPosition): number;
11
+ destination(p: GeoJSONPosition, dist: number , bearing: number): GeoJSONPosition;
12
+ offset(p: GeoJSONPosition, dx: number, dy: number): GeoJSONPosition;
13
+ lineDistance(points: Array<GeoJSONPosition>): number;
14
+ area(polygon: Array<Array<GeoJSONPosition>>): number;
15
+ along(line: Array<GeoJSONPosition>, dist: number): GeoJSONPosition;
16
+ pointToSegmentDistance(p: GeoJSONPosition, a: GeoJSONPosition, b: GeoJSONPosition): number;
17
+ pointOnLine(line: Array<GeoJSONPosition>, p: GeoJSONPosition): Object;
18
+ lineSlice(start: GeoJSONPosition, stop: GeoJSONPosition, line: Array<GeoJSONPosition>): Array<GeoJSONPosition>;
19
+ lineSliceAlong(start: GeoJSONPosition, stop: GeoJSONPosition, line: Array<GeoJSONPosition>): Array<GeoJSONPosition>;
20
+ bufferPoint(p: GeoJSONPosition, buffer: number): GeoJSONPosition;
21
+ bufferBBox(bbox: GeoJSONPosition, buffer: number): GeoJSONPosition;
22
+ insideBBox(p: GeoJSONPosition, bbox: GeoJSONPosition): boolean;
23
+
24
+ }
25
+ }
@@ -1,9 +1,10 @@
1
1
  // @flow strict
2
2
 
3
- type GeoJSONPosition = [number, number] | [number, number, number];
4
3
  type Geometry<T, C> = { type: T, coordinates: C }
5
4
 
6
5
  declare module "@mapbox/geojson-types" {
6
+ declare export type GeoJSONPosition = [number, number] | [number, number, number];
7
+
7
8
  declare export type GeoJSONPoint = Geometry<'Point', GeoJSONPosition>;
8
9
  declare export type GeoJSONMultiPoint = Geometry<'MultiPoint', Array<GeoJSONPosition>>;
9
10
 
@@ -27,12 +28,12 @@ declare module "@mapbox/geojson-types" {
27
28
  geometries: Array<GeoJSONGeometry>
28
29
  };
29
30
 
30
- declare export type GeoJSONFeature = {
31
- type: 'Feature',
32
- geometry: ?GeoJSONGeometry,
33
- properties: ?{},
34
- id?: number | string
35
- };
31
+ declare export interface GeoJSONFeature {
32
+ type: 'Feature';
33
+ geometry: ?GeoJSONGeometry;
34
+ properties: ?{};
35
+ id?: number | string;
36
+ }
36
37
 
37
38
  declare export type GeoJSONFeatureCollection = {
38
39
  type: 'FeatureCollection',