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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/bin/gl-style-composite.js +9 -4
  2. package/bin/gl-style-format.js +10 -4
  3. package/bin/gl-style-migrate.js +9 -4
  4. package/bin/gl-style-validate.js +16 -15
  5. package/deref.ts +8 -6
  6. package/dist/index.cjs +7771 -9448
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.ts +14 -11
  9. package/dist/index.es.js +7771 -9448
  10. package/dist/index.es.js.map +1 -1
  11. package/expression/compound_expression.ts +1 -1
  12. package/expression/definitions/assertion.ts +1 -1
  13. package/expression/definitions/at_interpolated.ts +2 -1
  14. package/expression/definitions/case.ts +1 -1
  15. package/expression/definitions/coalesce.ts +1 -1
  16. package/expression/definitions/coercion.ts +1 -1
  17. package/expression/definitions/distance.ts +1 -9
  18. package/expression/definitions/interpolate.ts +3 -3
  19. package/expression/definitions/literal.ts +1 -1
  20. package/expression/definitions/match.ts +1 -1
  21. package/expression/definitions/number_format.ts +8 -6
  22. package/expression/definitions/within.ts +1 -8
  23. package/expression/evaluation_context.ts +1 -10
  24. package/expression/index.ts +1 -1
  25. package/expression/values.ts +3 -4
  26. package/feature_filter/index.ts +1 -1
  27. package/function/convert.ts +1 -1
  28. package/group_by_layout.ts +8 -2
  29. package/package.json +4 -6
  30. package/read_style.ts +4 -4
  31. package/reference/v8.json +41 -7
  32. package/rollup.config.js +7 -4
  33. package/test.js +1 -1
  34. package/types/config_options.ts +7 -7
  35. package/types/lut.ts +5 -5
  36. package/types/tile_id.ts +3 -3
  37. package/types.ts +8 -1
  38. package/union-to-intersection.ts +2 -2
  39. package/util/assert.ts +3 -0
  40. package/util/color.ts +33 -18
  41. package/util/deep_equal.ts +6 -4
  42. package/util/geometry_util.ts +1 -1
  43. package/util/lerp.ts +3 -1
  44. package/util/mercator.ts +16 -0
  45. package/util/unbundle_jsonlint.ts +3 -2
  46. package/validate/validate.ts +2 -0
  47. package/validate/validate_appearance.ts +1 -1
  48. package/validate/validate_color.ts +5 -2
  49. package/validate/validate_expression.ts +16 -8
  50. package/validate/validate_function.ts +3 -2
  51. package/validate/validate_iconset.ts +1 -1
  52. package/validate/validate_import.ts +7 -0
  53. package/validate/validate_layer.ts +4 -1
  54. package/validate/validate_object.ts +20 -7
  55. package/validate/validate_option.ts +37 -0
  56. package/validate/validate_terrain.ts +1 -1
  57. package/validate_style.ts +3 -3
@@ -1,6 +1,6 @@
1
1
  import {toString} from './types';
2
2
  import ParsingContext from './parsing_context';
3
- import assert from 'assert';
3
+ import assert from '../util/assert';
4
4
 
5
5
  import type EvaluationContext from './evaluation_context';
6
6
  import type {Expression, ExpressionRegistry, SerializedExpression} from './expression';
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../../util/assert';
2
2
  import {
3
3
  ObjectType,
4
4
  ValueType,
@@ -1,5 +1,6 @@
1
1
  import {array, ValueType, NumberType} from '../types';
2
2
  import RuntimeError from '../runtime_error';
3
+ import {lerp} from '../../util/lerp';
3
4
 
4
5
  import type {Expression, SerializedExpression} from '../expression';
5
6
  import type ParsingContext from '../parsing_context';
@@ -60,7 +61,7 @@ class AtInterpolated implements Expression {
60
61
 
61
62
  // Linear interpolation
62
63
  const fraction = index - lowerIndex;
63
- return lowerValue * (1 - fraction) + upperValue * fraction;
64
+ return lerp(lowerValue, upperValue, fraction);
64
65
  }
65
66
 
66
67
  eachChild(fn: (_: Expression) => void) {
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../../util/assert';
2
2
  import {BooleanType} from '../types';
3
3
 
4
4
  import type {Expression, SerializedExpression} from '../expression';
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../../util/assert';
2
2
  import {checkSubtype, ValueType} from '../types';
3
3
  import ResolvedImage from '../types/resolved_image';
4
4
 
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../../util/assert';
2
2
  import {BooleanType, ColorType, NumberType, StringType, ValueType, array, NullType} from '../types';
3
3
  import {Color, isValue, toString as valueToString, typeOf, validateRGBA} from '../values';
4
4
  import RuntimeError from '../runtime_error';
@@ -1,6 +1,7 @@
1
1
  import {isValue} from '../values';
2
2
  import {NumberType} from '../types';
3
3
  import {classifyRings, updateBBox, boxWithinBox, pointWithinPolygon, segmentIntersectSegment} from '../../util/geometry_util';
4
+ import {lngFromMercatorX, latFromMercatorY} from '../../util/mercator';
4
5
  import CheapRuler from "cheap-ruler";
5
6
  import TinyQueue from "tinyqueue";
6
7
  import EXTENT from '../../data/extent';
@@ -126,15 +127,6 @@ function bboxToBBoxDistance(bbox1: BBox, bbox2: BBox, ruler: CheapRuler) {
126
127
  return ruler.distance([0.0, 0.0], [dx, dy]);
127
128
  }
128
129
 
129
- function lngFromMercatorX(x: number): number {
130
- return x * 360 - 180;
131
- }
132
-
133
- function latFromMercatorY(y: number): number {
134
- const y2 = 180 - y * 360;
135
- return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
136
- }
137
-
138
130
  export function getLngLatPoint(coord: Point, canonical: CanonicalTileID, extent: number = EXTENT): [number, number] {
139
131
  const tilesAtZoom = Math.pow(2, canonical.z);
140
132
  const x = (coord.x / extent + canonical.x) / tilesAtZoom;
@@ -1,4 +1,4 @@
1
- import UnitBezier from '@mapbox/unitbezier';
1
+ import unitBezier from '@mapbox/unitbezier';
2
2
  import * as interpolate from '../../util/interpolate';
3
3
  import {toString, NumberType, ColorType} from '../types';
4
4
  import {findStopLessThanOrEqualTo} from '../stops';
@@ -57,8 +57,8 @@ class Interpolate implements Expression {
57
57
  t = exponentialInterpolation(input, 1, lower, upper);
58
58
  } else if (interpolation.name === 'cubic-bezier') {
59
59
  const c = interpolation.controlPoints;
60
- const ub = new UnitBezier(c[0], c[1], c[2], c[3]);
61
- t = ub.solve(exponentialInterpolation(input, 1, lower, upper));
60
+ const ub = unitBezier(c[0], c[1], c[2], c[3]);
61
+ t = ub(exponentialInterpolation(input, 1, lower, upper));
62
62
  }
63
63
  return t;
64
64
  }
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../../util/assert';
2
2
  import {isValue, typeOf, Color} from '../values';
3
3
  import Formatted from '../types/formatted';
4
4
 
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../../util/assert';
2
2
  import {typeOf} from '../values';
3
3
  import {typeEquals, ValueType} from '../types';
4
4
 
@@ -14,12 +14,14 @@ export default class NumberFormat implements Expression {
14
14
  minFractionDigits: Expression | null; // Default 0
15
15
  maxFractionDigits: Expression | null; // Default 3
16
16
 
17
- constructor(number: Expression,
18
- locale: Expression | null,
19
- currency: Expression | null,
20
- unit: Expression | null,
21
- minFractionDigits: Expression | null,
22
- maxFractionDigits: Expression | null) {
17
+ constructor(
18
+ number: Expression,
19
+ locale: Expression | null,
20
+ currency: Expression | null,
21
+ unit: Expression | null,
22
+ minFractionDigits: Expression | null,
23
+ maxFractionDigits: Expression | null
24
+ ) {
23
25
  this.type = StringType;
24
26
  this.number = number;
25
27
  this.locale = locale;
@@ -1,6 +1,7 @@
1
1
  import {isValue} from '../values';
2
2
  import {BooleanType} from '../types';
3
3
  import {updateBBox, boxWithinBox, pointWithinPolygon, segmentIntersectSegment} from '../../util/geometry_util';
4
+ import {mercatorXfromLng, mercatorYfromLat} from '../../util/mercator';
4
5
 
5
6
  import type Point from '@mapbox/point-geometry';
6
7
  import type {Type} from '../types';
@@ -14,14 +15,6 @@ type GeoJSONPolygons = GeoJSON.Polygon | GeoJSON.MultiPolygon;
14
15
 
15
16
  const EXTENT = 8192;
16
17
 
17
- function mercatorXfromLng(lng: number) {
18
- return (180 + lng) / 360;
19
- }
20
-
21
- function mercatorYfromLat(lat: number) {
22
- return (180 - (180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)))) / 360;
23
- }
24
-
25
18
  function getTileCoordinates(p: GeoJSON.Position, canonical: CanonicalTileID) {
26
19
  const x = mercatorXfromLng(p[0]);
27
20
  const y = mercatorYfromLat(p[1]);
@@ -23,16 +23,11 @@ class EvaluationContext {
23
23
  options: ConfigOptions | null | undefined;
24
24
  iconImageUseTheme: string | null | undefined;
25
25
 
26
- _parseColorCache: {
27
- [_: string]: Color | null | undefined;
28
- };
29
-
30
26
  constructor(scope?: string | null, options?: ConfigOptions | null, iconImageUseTheme?: string) {
31
27
  this.globals = null;
32
28
  this.feature = null;
33
29
  this.featureState = null;
34
30
  this.formattedSection = null;
35
- this._parseColorCache = {};
36
31
  this.availableImages = null;
37
32
  this.canonical = null;
38
33
  this.featureTileCoord = null;
@@ -90,11 +85,7 @@ class EvaluationContext {
90
85
  }
91
86
 
92
87
  parseColor(input: string): Color | undefined {
93
- let cached = this._parseColorCache[input];
94
- if (!cached) {
95
- cached = this._parseColorCache[input] = Color.parse(input);
96
- }
97
- return cached;
88
+ return Color.parse(input);
98
89
  }
99
90
 
100
91
  getConfig(id: string): ConfigOptionValue | null | undefined {
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../util/assert';
2
2
  import ParsingError from './parsing_error';
3
3
  import ParsingContext from './parsing_context';
4
4
  import EvaluationContext from './evaluation_context';
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../util/assert';
2
2
  import Color from '../util/color';
3
3
  import Collator from './types/collator';
4
4
  import Formatted from './types/formatted';
@@ -131,11 +131,10 @@ export function typeOf(value: Value): Type {
131
131
  }
132
132
 
133
133
  export function toString(value: Value): string {
134
- const type = typeof value;
135
134
  if (value === null) {
136
135
  return '';
137
- } else if (type === 'string' || type === 'number' || type === 'boolean') {
138
- return String(value as string | number | boolean);
136
+ } else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
137
+ return String(value);
139
138
  } else if (value instanceof Formatted || value instanceof ResolvedImage || value instanceof Color) {
140
139
  return value.toString();
141
140
  } else {
@@ -2,7 +2,7 @@ import latest from '../reference/latest';
2
2
  import {deepUnbundle} from '../util/unbundle_jsonlint';
3
3
  import {createExpression} from '../expression/index';
4
4
  import {isFeatureConstant} from '../expression/is_constant';
5
- import assert from 'assert';
5
+ import assert from '../util/assert';
6
6
 
7
7
  import type Point from '@mapbox/point-geometry';
8
8
  import type {CanonicalTileID} from '../types/tile_id';
@@ -1,4 +1,4 @@
1
- import assert from 'assert';
1
+ import assert from '../util/assert';
2
2
 
3
3
  import type {StylePropertySpecification} from '../style-spec';
4
4
  import type {
@@ -81,13 +81,19 @@ export default function groupByLayout(
81
81
  k = layer.id;
82
82
  } else {
83
83
  k = getKey(layer);
84
- // The usage of "line-progress" inside "line-width" makes the property act like a layout property.
85
- // We need to split it from the group to avoid conflicts in the bucket creation.
84
+ // The usage of "line-progress" inside "line-width" or "line-emissive-strength"
85
+ // makes the property act like a layout property: the bucket needs to evaluate it
86
+ // per-vertex against the first layer's paint, so layers that disagree on whether
87
+ // line-progress is used can't share a bucket.
86
88
  if (layer.type === 'line' && layer["paint"]) {
87
89
  const lineWidth = layer["paint"]['line-width'];
88
90
  if (containsKey(lineWidth, 'line-progress')) {
89
91
  k += `/${stringify(layer["paint"]['line-width'])}`;
90
92
  }
93
+ const lineEmissiveStrength = layer["paint"]['line-emissive-strength'];
94
+ if (containsKey(lineEmissiveStrength, 'line-progress')) {
95
+ k += `/${stringify(layer["paint"]['line-emissive-strength'])}`;
96
+ }
91
97
  }
92
98
  }
93
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapbox/mapbox-gl-style-spec",
3
- "version": "14.24.0",
3
+ "version": "14.25.0-rc.1",
4
4
  "description": "a specification for mapbox gl styles",
5
5
  "author": "Mapbox",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",
@@ -32,7 +32,7 @@
32
32
  "pretest": "npm run build",
33
33
  "test": "node ./test.js",
34
34
  "build": "npm run build-spec && npm run build-dts",
35
- "build-dts": "dts-bundle-generator --no-banner --export-referenced-types=false -o ./dist/index.d.ts ./style-spec.ts",
35
+ "build-dts": "dts-bundle-generator --no-banner --export-referenced-types=false --project ../../tsconfig.browser.json -o ./dist/index.d.ts ./style-spec.ts",
36
36
  "build-spec": "rollup -c && rollup -c --environment esm",
37
37
  "prepublishOnly": "npm run build",
38
38
  "postpublish": "rm dist/index.cjs dist/index.d.ts"
@@ -45,13 +45,11 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@mapbox/point-geometry": "^1.1.0",
48
- "@mapbox/unitbezier": "^0.0.1",
48
+ "@mapbox/unitbezier": "^1.0.0",
49
49
  "cheap-ruler": "^4.0.0",
50
- "csscolorparser": "~1.0.2",
50
+ "csscolorparser": "~1.0.3",
51
51
  "json-stringify-pretty-compact": "^4.0.0",
52
- "minimist": "^1.2.6",
53
52
  "quickselect": "^3.0.0",
54
- "rw": "^1.3.3",
55
53
  "tinyqueue": "^3.0.0"
56
54
  }
57
55
  }
package/read_style.ts CHANGED
@@ -2,10 +2,10 @@ import ParsingError from './error/parsing_error';
2
2
 
3
3
  import type {StyleSpecification} from './types';
4
4
 
5
- export default function readStyle(style: string | Buffer | StyleSpecification): StyleSpecification {
5
+ export default function readStyle(style: string | Uint8Array | StyleSpecification): StyleSpecification {
6
6
  if (style instanceof String || typeof style === 'string' || ArrayBuffer.isView(style)) {
7
7
  try {
8
- const str = style.toString();
8
+ const str = ArrayBuffer.isView(style) ? new TextDecoder().decode(style) : style.toString();
9
9
  JSON.parse(str); // first try full parsing to catch malformed JSON
10
10
  return parse(str) as StyleSpecification; // then our custom parser
11
11
  } catch (e) {
@@ -111,7 +111,7 @@ function parseTokens(s: string, tokens: number[]) {
111
111
  while (tokens[i] !== RBRACE) {
112
112
  const key: string = JSON.parse(s.slice(tokens[i + 1], tokens[i + 2])) as string;
113
113
  i += 3;
114
- obj[key] = parseValue();
114
+ Object.defineProperty(obj, key, {value: parseValue(), enumerable: true, configurable: true, writable: true});
115
115
  }
116
116
  i += 3;
117
117
  return obj;
@@ -122,7 +122,7 @@ function parseTokens(s: string, tokens: number[]) {
122
122
  i += 3;
123
123
  return arr;
124
124
  }
125
- /* eslint-disable no-new-wrappers */
125
+
126
126
  if (type === STRING) return setLine(new String(JSON.parse(s.slice(start, end))), line);
127
127
  if (type === NUMBER) return setLine(new Number(+s.slice(start, end)), line);
128
128
  if (type === TRUE) return setLine(new Boolean(true), line);
package/reference/v8.json CHANGED
@@ -8874,7 +8874,8 @@
8874
8874
  "interpolated": true,
8875
8875
  "parameters": [
8876
8876
  "zoom",
8877
- "measure-light"
8877
+ "measure-light",
8878
+ "line-progress"
8878
8879
  ]
8879
8880
  },
8880
8881
  "property-type": "data-driven"
@@ -11415,6 +11416,27 @@
11415
11416
  },
11416
11417
  "transition": true
11417
11418
  },
11419
+ "model-lightmap-intensity": {
11420
+ "type": "number",
11421
+ "default": 0.0,
11422
+ "minimum": 0,
11423
+ "doc": "Controls the intensity of the model lightmap contribution. Values less than or equal to 0.001 are treated as off.",
11424
+ "private": true,
11425
+ "experimental": true,
11426
+ "expression": {
11427
+ "interpolated": true,
11428
+ "parameters": ["zoom"]
11429
+ },
11430
+ "sdk-support": {
11431
+ "basic functionality": {
11432
+ "js": "3.25.0",
11433
+ "android": "11.25.0",
11434
+ "ios": "11.25.0"
11435
+ }
11436
+ },
11437
+ "property-type": "data-constant",
11438
+ "transition": true
11439
+ },
11418
11440
  "model-type": {
11419
11441
  "type": "enum",
11420
11442
  "values": {
@@ -11634,10 +11656,21 @@
11634
11656
  }
11635
11657
  }
11636
11658
  },
11637
- "model-ignore-line-cutout": {
11638
- "type": "boolean",
11639
- "doc": "When set to true, the layer is not going to be affected by the line cutout effect.",
11640
- "default": false,
11659
+ "model-line-cutout-mode": {
11660
+ "type": "enum",
11661
+ "doc": "Selects the way the line cutout effect is applied to the model.",
11662
+ "default": "enabled",
11663
+ "values": {
11664
+ "enabled": {
11665
+ "doc": "The model receives the cutout without any special handling."
11666
+ },
11667
+ "disabled": {
11668
+ "doc": "The cutout effect is disabled for the model."
11669
+ },
11670
+ "enabled-above-cutout": {
11671
+ "doc": "Only enabled if the model is above the cutout plane, such as for models above cut tunnels."
11672
+ }
11673
+ },
11641
11674
  "transition": false,
11642
11675
  "expression": {
11643
11676
  "interpolated": false
@@ -11647,8 +11680,9 @@
11647
11680
  "private": true,
11648
11681
  "sdk-support": {
11649
11682
  "basic functionality": {
11650
- "android": "11.21.0",
11651
- "ios": "11.21.0"
11683
+ "js": "3.26.0",
11684
+ "android": "11.26.0",
11685
+ "ios": "11.26.0"
11652
11686
  }
11653
11687
  }
11654
11688
  }
package/rollup.config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import resolve from '@rollup/plugin-node-resolve';
2
2
  import commonjs from '@rollup/plugin-commonjs';
3
- import unassert from 'rollup-plugin-unassert';
3
+ import strip from '@rollup/plugin-strip';
4
4
  import json from '@rollup/plugin-json';
5
5
  import esbuild from 'rollup-plugin-esbuild';
6
6
  import {fileURLToPath} from 'url';
@@ -19,10 +19,13 @@ const config = [{
19
19
  sourcemap: true
20
20
  },
21
21
  plugins: [
22
- esbuild({tsconfig: `${__dirname}/../../tsconfig.json`}),
22
+ esbuild({tsconfig: `${__dirname}/../../tsconfig.browser.json`}),
23
23
  json(),
24
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
25
- unassert({include: ['*.js', '**/*.js', '*.ts', '**/*.ts']}),
24
+ strip({
25
+ sourceMap: true,
26
+ functions: ['assert', 'assert.*'],
27
+ include: ['**/*.ts']
28
+ }),
26
29
  resolve({
27
30
  browser: true,
28
31
  preferBuiltins: false
package/test.js CHANGED
@@ -15,7 +15,7 @@ process.on('unhandledRejection', (/** @type {Error} */ error) => {
15
15
  });
16
16
 
17
17
  const require = createRequire(import.meta.url);
18
- const stylePath = require.resolve('mapbox-gl-styles/styles/basic-v9.json');
18
+ const stylePath = require.resolve('@mapbox/mapbox-gl-styles/styles/basic-v9.json');
19
19
 
20
20
  try {
21
21
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -1,13 +1,13 @@
1
1
  import type {Expression} from '../expression/expression';
2
2
 
3
3
  export type ConfigOptionValue = {
4
- default: Expression;
5
- value?: Expression;
6
- values?: Array<unknown>;
7
- minValue?: number;
8
- maxValue?: number;
9
- stepValue?: number;
10
- type?: 'string' | 'number' | 'boolean' | 'color';
4
+ default: Expression;
5
+ value?: Expression;
6
+ values?: Array<unknown>;
7
+ minValue?: number;
8
+ maxValue?: number;
9
+ stepValue?: number;
10
+ type?: 'string' | 'number' | 'boolean' | 'color';
11
11
  };
12
12
 
13
13
  export type ConfigOptions = Map<string, ConfigOptionValue>;
package/types/lut.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type LUT = {
2
- image: {
3
- width: number;
4
- height: number;
5
- data: Uint8Array;
6
- };
2
+ image: {
3
+ width: number;
4
+ height: number;
5
+ data: Uint8Array;
6
+ };
7
7
  };
package/types/tile_id.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type CanonicalTileID = {
2
- z: number;
3
- x: number;
4
- y: number;
2
+ z: number;
3
+ x: number;
4
+ y: number;
5
5
  };
package/types.ts CHANGED
@@ -1405,6 +1405,11 @@ export type ModelLayerSpecification = {
1405
1405
  "model-color-use-theme"?: PropertyValueSpecification<string>,
1406
1406
  "model-color-mix-intensity"?: DataDrivenPropertyValueSpecification<number>,
1407
1407
  "model-color-mix-intensity-transition"?: TransitionSpecification,
1408
+ /**
1409
+ * @experimental This property is experimental and subject to change in future versions.
1410
+ */
1411
+ "model-lightmap-intensity"?: PropertyValueSpecification<number>,
1412
+ "model-lightmap-intensity-transition"?: TransitionSpecification,
1408
1413
  "model-type"?: "common-3d" | "location-indicator",
1409
1414
  "model-cast-shadows"?: boolean,
1410
1415
  "model-receive-shadows"?: boolean,
@@ -1422,7 +1427,7 @@ export type ModelLayerSpecification = {
1422
1427
  /**
1423
1428
  * @experimental This property is experimental and subject to change in future versions.
1424
1429
  */
1425
- "model-ignore-line-cutout"?: boolean | ExpressionSpecification
1430
+ "model-line-cutout-mode"?: "enabled" | "disabled" | "enabled-above-cutout" | ExpressionSpecification
1426
1431
  },
1427
1432
  /**
1428
1433
  * @experimental This property is experimental and subject to change in future versions.
@@ -1593,6 +1598,8 @@ export type LayoutSpecification = UnionToIntersection<NonNullable<LayerSpecifica
1593
1598
 
1594
1599
  export type PaintSpecification = UnionToIntersection<NonNullable<LayerSpecification['paint']>>;
1595
1600
 
1601
+ export type LayerBaseSpecification = Pick<LayerSpecification, "slot" | "minzoom" | "maxzoom" | "filter" | "appearances">;
1602
+
1596
1603
  // Aliases for easier migration from @types/mapbox-gl
1597
1604
 
1598
1605
  export type Layer = Pick<
@@ -1,4 +1,4 @@
1
1
  export type UnionToIntersection<U> =
2
2
  (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ?
3
- {[K in keyof I]: I[K]} :
4
- never;
3
+ {[K in keyof I]: I[K]} :
4
+ never;
package/util/assert.ts ADDED
@@ -0,0 +1,3 @@
1
+ export default function assert(condition: unknown, message?: string): asserts condition {
2
+ if (!condition) throw new Error(message !== undefined ? message : 'Assertion failed');
3
+ }
package/util/color.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  import {parseCSSColor} from 'csscolorparser';
2
- import {number as lerp} from './lerp';
2
+ import {lerp} from './lerp';
3
3
 
4
4
  import type {LUT} from '../types/lut';
5
5
 
6
+ // Color strings are parsed repeatedly during style load and evaluation; since Color
7
+ // is immutable, parsed instances can be safely shared and cached by input string.
8
+ const colorCache: Map<string, Color> = new Map();
9
+
6
10
  /**
7
11
  * An RGBA color value. Create instances from color strings using the static
8
12
  * method `Color.parse`. The constructor accepts RGB channel values in the range
@@ -15,10 +19,10 @@ import type {LUT} from '../types/lut';
15
19
  * @private
16
20
  */
17
21
  class Color {
18
- r: number;
19
- g: number;
20
- b: number;
21
- a: number;
22
+ readonly r: number;
23
+ readonly g: number;
24
+ readonly b: number;
25
+ readonly a: number;
22
26
 
23
27
  constructor(r: number, g: number, b: number, a: number = 1) {
24
28
  this.r = r;
@@ -50,17 +54,24 @@ class Color {
50
54
  return undefined;
51
55
  }
52
56
 
57
+ const cached = colorCache.get(input);
58
+ if (cached) {
59
+ return cached;
60
+ }
61
+
53
62
  const rgba = parseCSSColor(input);
54
63
  if (!rgba) {
55
64
  return undefined;
56
65
  }
57
66
 
58
- return new Color(
67
+ const color = new Color(
59
68
  rgba[0] / 255,
60
69
  rgba[1] / 255,
61
70
  rgba[2] / 255,
62
71
  rgba[3]
63
72
  );
73
+ colorCache.set(input, color);
74
+ return color;
64
75
  }
65
76
 
66
77
  /**
@@ -152,28 +163,32 @@ export abstract class RenderColor {
152
163
  const i6 = (r1 + g1 * N2 + b0 * N) * 4;
153
164
  const i7 = (r1 + g1 * N2 + b1 * N) * 4;
154
165
 
166
+ // r/g/b are clamped to [0, N-1] above, so every index below is within bounds.
167
+ // The `as number` casts only suppress the `number | undefined` that
168
+ // `noUncheckedIndexedAccess` infers for typed-array reads; they are erased at
169
+ // runtime, so unlike a helper closure they add no per-construction allocation.
155
170
  // Trilinear interpolation.
156
171
  this.r = lerp(
157
172
  lerp(
158
- lerp(data[i0], data[i1], bw),
159
- lerp(data[i2], data[i3], bw), gw),
173
+ lerp(data[i0] as number, data[i1] as number, bw),
174
+ lerp(data[i2] as number, data[i3] as number, bw), gw),
160
175
  lerp(
161
- lerp(data[i4], data[i5], bw),
162
- lerp(data[i6], data[i7], bw), gw), rw) / 255 * (this.premultiplied ? a : 1);
176
+ lerp(data[i4] as number, data[i5] as number, bw),
177
+ lerp(data[i6] as number, data[i7] as number, bw), gw), rw) / 255 * (this.premultiplied ? a : 1);
163
178
  this.g = lerp(
164
179
  lerp(
165
- lerp(data[i0 + 1], data[i1 + 1], bw),
166
- lerp(data[i2 + 1], data[i3 + 1], bw), gw),
180
+ lerp(data[i0 + 1] as number, data[i1 + 1] as number, bw),
181
+ lerp(data[i2 + 1] as number, data[i3 + 1] as number, bw), gw),
167
182
  lerp(
168
- lerp(data[i4 + 1], data[i5 + 1], bw),
169
- lerp(data[i6 + 1], data[i7 + 1], bw), gw), rw) / 255 * (this.premultiplied ? a : 1);
183
+ lerp(data[i4 + 1] as number, data[i5 + 1] as number, bw),
184
+ lerp(data[i6 + 1] as number, data[i7 + 1] as number, bw), gw), rw) / 255 * (this.premultiplied ? a : 1);
170
185
  this.b = lerp(
171
186
  lerp(
172
- lerp(data[i0 + 2], data[i1 + 2], bw),
173
- lerp(data[i2 + 2], data[i3 + 2], bw), gw),
187
+ lerp(data[i0 + 2] as number, data[i1 + 2] as number, bw),
188
+ lerp(data[i2 + 2] as number, data[i3 + 2] as number, bw), gw),
174
189
  lerp(
175
- lerp(data[i4 + 2], data[i5 + 2], bw),
176
- lerp(data[i6 + 2], data[i7 + 2], bw), gw), rw) / 255 * (this.premultiplied ? a : 1);
190
+ lerp(data[i4 + 2] as number, data[i5 + 2] as number, bw),
191
+ lerp(data[i6 + 2] as number, data[i7 + 2] as number, bw), gw), rw) / 255 * (this.premultiplied ? a : 1);
177
192
  this.a = a;
178
193
  }
179
194
  }
@@ -13,10 +13,12 @@ function deepEqual(a?: unknown, b?: unknown): boolean {
13
13
  }
14
14
  if (typeof a === 'object' && a !== null && b !== null) {
15
15
  if (!(typeof b === 'object')) return false;
16
- const keys = Object.keys(a);
17
- if (keys.length !== Object.keys(b).length) return false;
18
- for (const key in a) {
19
- if (!deepEqual(a[key], b[key])) return false;
16
+ const aRec = a as Record<string, unknown>;
17
+ const bRec = b as Record<string, unknown>;
18
+ const keys = Object.keys(aRec);
19
+ if (keys.length !== Object.keys(bRec).length) return false;
20
+ for (const key in aRec) {
21
+ if (!deepEqual(aRec[key], bRec[key])) return false;
20
22
  }
21
23
  return true;
22
24
  }