@mapbox/mapbox-gl-style-spec 14.12.0 → 14.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.
@@ -1,7 +1,8 @@
1
- import {ValueType} from '../types';
1
+ import {typeEquals, ValueType} from '../types';
2
2
  import {Color, typeOf, toString as valueToString} from '../values';
3
3
  import Formatted from '../types/formatted';
4
4
  import ResolvedImage from '../types/resolved_image';
5
+ import * as isConstant from '../is_constant';
5
6
  import Literal from './literal';
6
7
 
7
8
  import type {Type} from '../types';
@@ -9,6 +10,12 @@ import type {Expression, SerializedExpression} from '../expression';
9
10
  import type ParsingContext from '../parsing_context';
10
11
  import type EvaluationContext from '../evaluation_context';
11
12
 
13
+ const FQIDSeparator = '\u001F';
14
+
15
+ function makeConfigFQID(id: string, ownScope?: string | null, contextScope?: string | null): string {
16
+ return [id, ownScope, contextScope].filter(Boolean).join(FQIDSeparator);
17
+ }
18
+
12
19
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
20
  function coerceValue(type: string, value: any): any {
14
21
  switch (type) {
@@ -43,11 +50,13 @@ class Config implements Expression {
43
50
  type: Type;
44
51
  key: string;
45
52
  scope: string | null | undefined;
53
+ featureConstant: boolean;
46
54
 
47
- constructor(type: Type, key: string, scope?: string) {
55
+ constructor(type: Type, key: string, scope?: string, featureConstant: boolean = false) {
48
56
  this.type = type;
49
57
  this.key = key;
50
58
  this.scope = scope;
59
+ this.featureConstant = featureConstant;
51
60
  }
52
61
 
53
62
  static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Config | null | void {
@@ -64,23 +73,34 @@ class Config implements Expression {
64
73
  return context.error(`Key name of 'config' expression must be a string literal.`);
65
74
  }
66
75
 
76
+ let featureConstant = true;
77
+ let configScopeValue: string | undefined;
78
+ const configKeyValue = valueToString(configKey.value);
79
+
67
80
  if (args.length >= 3) {
68
81
  const configScope = context.parse(args[2], 2);
69
82
  if (!(configScope instanceof Literal)) {
70
83
  return context.error(`Scope of 'config' expression must be a string literal.`);
71
84
  }
72
- return new Config(type, valueToString(configKey.value), valueToString(configScope.value));
85
+
86
+ configScopeValue = valueToString(configScope.value);
73
87
  }
74
88
 
75
- return new Config(type, valueToString(configKey.value));
89
+ if (context.options) {
90
+ const fqid = makeConfigFQID(configKeyValue, configScopeValue, context._scope);
91
+ const config = context.options.get(fqid);
92
+ if (config) {
93
+ featureConstant = isConstant.isFeatureConstant(config.value || config.default);
94
+ }
95
+ }
96
+
97
+ return new Config(type, configKeyValue, configScopeValue, featureConstant);
76
98
  }
77
99
 
78
100
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
101
  evaluate(ctx: EvaluationContext): any {
80
- const FQIDSeparator = '\u001F';
81
- const configKey = [this.key, this.scope, ctx.scope].filter(Boolean).join(FQIDSeparator);
82
-
83
- const config = ctx.getConfig(configKey);
102
+ const fqid = makeConfigFQID(this.key, this.scope, ctx.scope);
103
+ const config = ctx.getConfig(fqid);
84
104
  if (!config) return null;
85
105
 
86
106
  const {type, value, values, minValue, maxValue, stepValue} = config;
@@ -117,7 +137,7 @@ class Config implements Expression {
117
137
  }
118
138
 
119
139
  // @ts-expect-error - TS2367 - This comparison appears to be unintentional because the types 'string' and 'Type' have no overlap.
120
- if ((type && type !== this.type) || (result !== undefined && typeOf(result) !== this.type)) {
140
+ if ((type && type !== this.type) || (result !== undefined && !typeEquals(typeOf(result), this.type))) {
121
141
  result = coerceValue(this.type.kind, result);
122
142
  }
123
143
 
@@ -133,7 +153,7 @@ class Config implements Expression {
133
153
  serialize(): SerializedExpression {
134
154
  const res = ["config", this.key];
135
155
  if (this.scope) {
136
- res.concat(this.key);
156
+ res.concat(this.scope);
137
157
  }
138
158
  return res;
139
159
  }
@@ -6,6 +6,7 @@ import {
6
6
  StringType,
7
7
  ColorType,
8
8
  ResolvedImageType,
9
+ typeEquals,
9
10
  } from '../types';
10
11
  import Formatted, {FormattedSection} from '../types/formatted';
11
12
  import {toString, typeOf} from '../values';
@@ -92,7 +93,7 @@ export default class FormatExpression implements Expression {
92
93
  evaluate(ctx: EvaluationContext): Formatted {
93
94
  const evaluateSection = (section: FormattedSectionExpression) => {
94
95
  const evaluatedContent = section.content.evaluate(ctx);
95
- if (typeOf(evaluatedContent) === ResolvedImageType) {
96
+ if (typeEquals(typeOf(evaluatedContent), ResolvedImageType)) {
96
97
  return new FormattedSection('', evaluatedContent, null, null, null);
97
98
  }
98
99
 
@@ -101,7 +101,7 @@ function rgba(ctx: EvaluationContext, [r, g, b, a]: Expression[]) {
101
101
  const alpha = a ? a.evaluate(ctx) : 1;
102
102
  const error = validateRGBA(r, g, b, alpha);
103
103
  if (error) throw new RuntimeError(error);
104
- return new Color(r as unknown as number / 255 * alpha, g as unknown as number / 255 * alpha, b as unknown as number / 255 * alpha, alpha);
104
+ return new Color(r as unknown as number / 255, g as unknown as number / 255, b as unknown as number / 255, alpha);
105
105
  }
106
106
 
107
107
  function hsla(ctx: EvaluationContext, [h, s, l, a]: Expression[]) {
@@ -187,7 +187,7 @@ CompoundExpression.register(expressions, {
187
187
  [ColorType],
188
188
  (ctx, [v]) => {
189
189
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
190
- return v.evaluate(ctx).toRenderColor(null).toArray();
190
+ return v.evaluate(ctx).toNonPremultipliedRenderColor(null).toArray();
191
191
  }
192
192
  ],
193
193
  'to-hsla': [
@@ -195,7 +195,7 @@ CompoundExpression.register(expressions, {
195
195
  [ColorType],
196
196
  (ctx, [v]) => {
197
197
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
198
- return v.evaluate(ctx).toRenderColor(null).toHslaArray();
198
+ return v.evaluate(ctx).toNonPremultipliedRenderColor(null).toHslaArray();
199
199
  }
200
200
  ],
201
201
  'rgb': [
@@ -260,6 +260,11 @@ CompoundExpression.register(expressions, {
260
260
  [],
261
261
  (ctx) => ctx.geometryType()
262
262
  ],
263
+ 'worldview': [
264
+ StringType,
265
+ [],
266
+ (ctx) => ctx.globals.worldview || ""
267
+ ],
263
268
  'id': [
264
269
  ValueType,
265
270
  [],
@@ -58,7 +58,7 @@ class Literal implements Expression {
58
58
  // Constant-folding can generate Literal expressions that you
59
59
  // couldn't actually generate with a "literal" expression,
60
60
  // so we have to implement an equivalent serialization here
61
- return ["rgba" as SerializedExpression].concat(this.value.toRenderColor(null).toArray());
61
+ return ["rgba" as SerializedExpression].concat(this.value.toNonPremultipliedRenderColor(null).toArray());
62
62
  } else if (this.value instanceof Formatted) {
63
63
  // Same as Color
64
64
  return this.value.serialize();
@@ -1,6 +1,6 @@
1
1
  import assert from 'assert';
2
2
  import {typeOf} from '../values';
3
- import {ValueType} from '../types';
3
+ import {typeEquals, ValueType} from '../types';
4
4
 
5
5
  import type {Type} from '../types';
6
6
  import type {Expression, SerializedExpression} from '../expression';
@@ -101,7 +101,7 @@ class Match implements Expression {
101
101
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
102
  evaluate(ctx: EvaluationContext): any {
103
103
  const input = (this.input.evaluate(ctx));
104
- const output = (typeOf(input) === this.inputType && this.outputs[this.cases[input]]) || this.otherwise;
104
+ const output = (typeEquals(typeOf(input), this.inputType) && this.outputs[this.cases[input]]) || this.otherwise;
105
105
  return output.evaluate(ctx);
106
106
  }
107
107
 
@@ -60,6 +60,7 @@ export interface GlobalProperties {
60
60
  readonly isSupportedScript?: (_: string) => boolean;
61
61
  accumulated?: Value;
62
62
  brightness?: number;
63
+ worldview?: string;
63
64
  }
64
65
 
65
66
  export class StyleExpression {
@@ -32,6 +32,10 @@ function isFeatureConstant(e: Expression): boolean {
32
32
  return false;
33
33
  }
34
34
 
35
+ if (e instanceof Config) {
36
+ return e.featureConstant;
37
+ }
38
+
35
39
  let result = true;
36
40
  e.eachChild(arg => {
37
41
  if (result && !isFeatureConstant(arg)) { result = false; }
@@ -271,5 +271,5 @@ function isConstant(expression: Expression) {
271
271
  }
272
272
 
273
273
  return isFeatureConstant(expression) &&
274
- isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'raster-value', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center', 'measure-light', 'raster-particle-speed']);
274
+ isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'worldview', 'line-progress', 'raster-value', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center', 'measure-light', 'raster-particle-speed']);
275
275
  }
@@ -70,7 +70,7 @@ export default class Formatted {
70
70
  options["font-scale"] = section.scale;
71
71
  }
72
72
  if (section.textColor) {
73
- options["text-color"] = (["rgba"] as Array<unknown>).concat(section.textColor.toRenderColor(null).toArray());
73
+ options["text-color"] = (["rgba"] as Array<unknown>).concat(section.textColor.toNonPremultipliedRenderColor(null).toArray());
74
74
  }
75
75
  serialized.push(options);
76
76
  }
@@ -134,3 +134,11 @@ export function isValidNativeType(provided: any, allowedTypes: Array<NativeType>
134
134
  }
135
135
  });
136
136
  }
137
+
138
+ export function typeEquals(a: Type, b: Type): boolean {
139
+ if (a.kind === 'array' && b.kind === 'array') {
140
+ return a.N === b.N && typeEquals(a.itemType, b.itemType);
141
+ } else {
142
+ return a.kind === b.kind;
143
+ }
144
+ }
@@ -135,9 +135,7 @@ export function toString(value: Value): string {
135
135
  return '';
136
136
  } else if (type === 'string' || type === 'number' || type === 'boolean') {
137
137
  return String(value as string | number | boolean);
138
- } else if (value instanceof Color) {
139
- return value.toStringPremultipliedAlpha();
140
- } else if (value instanceof Formatted || value instanceof ResolvedImage) {
138
+ } else if (value instanceof Formatted || value instanceof ResolvedImage || value instanceof Color) {
141
139
  return value.toString();
142
140
  } else {
143
141
  return JSON.stringify(value);
@@ -25,15 +25,7 @@ function stringify(obj: any) {
25
25
  function getKey(layer: LayerSpecification) {
26
26
  let key = '';
27
27
  for (const k of refProperties) {
28
- // Ignore minzoom and maxzoom for model layers so that multiple model layers
29
- // referencing the same source (but with different zoom ranges) produce the same
30
- // key. This ensures they get grouped into a single bucket, preventing a scenario
31
- // where shared node data is serialized twice and triggers an assert in struct_array.ts.
32
- if (layer.type === 'model' && (k === 'minzoom' || k === 'maxzoom')) {
33
- continue;
34
- } else {
35
- key += `/${stringify(layer[k])}`;
36
- }
28
+ key += `/${stringify(layer[k])}`;
37
29
  }
38
30
  return key;
39
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapbox/mapbox-gl-style-spec",
3
- "version": "14.12.0",
3
+ "version": "14.13.0",
4
4
  "description": "a specification for mapbox gl styles",
5
5
  "author": "Mapbox",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",