@versatiles/png-renderer 2.5.0 → 2.6.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/dist/index.js CHANGED
@@ -9830,6 +9830,21 @@ var v8_default = {
9830
9830
  },
9831
9831
  "property-type": "data-constant"
9832
9832
  },
9833
+ "raster-layer-blend": {
9834
+ "type": "enum",
9835
+ "values": {
9836
+ "normal": {},
9837
+ "multiply": {},
9838
+ "color": {},
9839
+ "luminosity": {},
9840
+ "plus-lighter": {},
9841
+ "screen": {},
9842
+ "overlay": {}
9843
+ },
9844
+ "default": "normal",
9845
+ "property-type": "constant",
9846
+ "transition": false
9847
+ },
9833
9848
  "raster-hue-rotate": {
9834
9849
  "type": "number",
9835
9850
  "default": 0,
@@ -10193,6 +10208,40 @@ var v8_default = {
10193
10208
  // src/reference/latest.ts
10194
10209
  const latest = v8_default;
10195
10210
 
10211
+ // src/expression/parsing_error.ts
10212
+ var ExpressionParsingError = class extends Error {
10213
+ constructor(key, message) {
10214
+ super(message);
10215
+ this.message = message;
10216
+ this.key = key;
10217
+ }
10218
+ };
10219
+
10220
+ // src/expression/scope.ts
10221
+ /**
10222
+ * Tracks `let` bindings during expression parsing.
10223
+ * @private
10224
+ */
10225
+ var Scope = class Scope {
10226
+ constructor(parent, bindings = []) {
10227
+ this.parent = parent;
10228
+ this.bindings = {};
10229
+ for (const [name, expression] of bindings) this.bindings[name] = expression;
10230
+ }
10231
+ concat(bindings) {
10232
+ return new Scope(this, bindings);
10233
+ }
10234
+ get(name) {
10235
+ if (this.bindings[name]) return this.bindings[name];
10236
+ if (this.parent) return this.parent.get(name);
10237
+ throw new Error(`${name} not found in scope.`);
10238
+ }
10239
+ has(name) {
10240
+ if (this.bindings[name]) return true;
10241
+ return this.parent ? this.parent.has(name) : false;
10242
+ }
10243
+ };
10244
+
10196
10245
  // src/expression/types.ts
10197
10246
  const NullType = { kind: "null" };
10198
10247
  const NumberType = { kind: "number" };
@@ -11464,6 +11513,25 @@ var Color$1 = class Color {
11464
11513
  }
11465
11514
  };
11466
11515
 
11516
+ // src/expression/types/collator.ts
11517
+ var Collator = class {
11518
+ constructor(caseSensitive, diacriticSensitive, locale) {
11519
+ if (caseSensitive) this.sensitivity = diacriticSensitive ? "variant" : "case";
11520
+ else this.sensitivity = diacriticSensitive ? "accent" : "base";
11521
+ this.locale = locale;
11522
+ this.collator = new Intl.Collator(this.locale ? this.locale : [], {
11523
+ sensitivity: this.sensitivity,
11524
+ usage: "search"
11525
+ });
11526
+ }
11527
+ compare(lhs, rhs) {
11528
+ return this.collator.compare(lhs, rhs);
11529
+ }
11530
+ resolvedLocale() {
11531
+ return new Intl.Collator(this.locale ? this.locale : []).resolvedOptions().locale;
11532
+ }
11533
+ };
11534
+
11467
11535
  // src/expression/types/formatted.ts
11468
11536
  const VERTICAL_ALIGN_OPTIONS = [
11469
11537
  "bottom",
@@ -11742,25 +11810,6 @@ var ProjectionDefinition = class ProjectionDefinition {
11742
11810
  }
11743
11811
  };
11744
11812
 
11745
- // src/expression/types/collator.ts
11746
- var Collator = class {
11747
- constructor(caseSensitive, diacriticSensitive, locale) {
11748
- if (caseSensitive) this.sensitivity = diacriticSensitive ? "variant" : "case";
11749
- else this.sensitivity = diacriticSensitive ? "accent" : "base";
11750
- this.locale = locale;
11751
- this.collator = new Intl.Collator(this.locale ? this.locale : [], {
11752
- sensitivity: this.sensitivity,
11753
- usage: "search"
11754
- });
11755
- }
11756
- compare(lhs, rhs) {
11757
- return this.collator.compare(lhs, rhs);
11758
- }
11759
- resolvedLocale() {
11760
- return new Intl.Collator(this.locale ? this.locale : []).resolvedOptions().locale;
11761
- }
11762
- };
11763
-
11764
11813
  // src/expression/values.ts
11765
11814
  function validateRGBA(r, g, b, a) {
11766
11815
  if (!(typeof r === "number" && r >= 0 && r <= 255 && typeof g === "number" && g >= 0 && g <= 255 && typeof b === "number" && b >= 0 && b <= 255)) return `Invalid rgba value [${(typeof a === "number" ? [
@@ -11852,539 +11901,100 @@ var Literal = class Literal {
11852
11901
  }
11853
11902
  };
11854
11903
 
11855
- // src/expression/evaluation_context.ts
11856
- const geometryTypes = [
11857
- "Unknown",
11858
- "Point",
11859
- "LineString",
11860
- "Polygon"
11861
- ];
11862
- var EvaluationContext = class {
11863
- constructor() {
11864
- this.globals = null;
11865
- this.feature = null;
11866
- this.featureState = null;
11867
- this.formattedSection = null;
11868
- this._parseColorCache = /* @__PURE__ */ new Map();
11869
- this.availableImages = null;
11870
- this.canonical = null;
11871
- }
11872
- id() {
11873
- return this.feature && "id" in this.feature ? this.feature.id : null;
11874
- }
11875
- geometryType() {
11876
- return this.feature ? typeof this.feature.type === "number" ? geometryTypes[this.feature.type] : this.feature.type : null;
11904
+ // src/expression/definitions/assertion.ts
11905
+ const types$1 = {
11906
+ string: StringType,
11907
+ number: NumberType,
11908
+ boolean: BooleanType,
11909
+ object: ObjectType
11910
+ };
11911
+ var Assertion = class Assertion {
11912
+ constructor(type, args, key) {
11913
+ this.type = type;
11914
+ this.args = args;
11915
+ this.key = key;
11877
11916
  }
11878
- geometry() {
11879
- return this.feature && "geometry" in this.feature ? this.feature.geometry : null;
11917
+ static parse(args, context) {
11918
+ if (args.length < 2) return context.error("Expected at least one argument.");
11919
+ let i = 1;
11920
+ let type;
11921
+ const name = args[0];
11922
+ if (name === "array") {
11923
+ let itemType;
11924
+ if (args.length > 2) {
11925
+ const type = args[1];
11926
+ if (typeof type !== "string" || !(type in types$1) || type === "object") return context.error("The item type argument of \"array\" must be one of string, number, boolean", 1);
11927
+ itemType = types$1[type];
11928
+ i++;
11929
+ } else itemType = ValueType;
11930
+ let N;
11931
+ if (args.length > 3) {
11932
+ if (args[2] !== null && (typeof args[2] !== "number" || args[2] < 0 || args[2] !== Math.floor(args[2]))) return context.error("The length argument to \"array\" must be a positive integer literal", 2);
11933
+ N = args[2];
11934
+ i++;
11935
+ }
11936
+ type = array(itemType, N);
11937
+ } else {
11938
+ if (!types$1[name]) throw new Error(`Types doesn't contain name = ${name}`);
11939
+ type = types$1[name];
11940
+ }
11941
+ const parsed = [];
11942
+ for (; i < args.length; i++) {
11943
+ const input = context.parse(args[i], i, ValueType);
11944
+ if (!input) return null;
11945
+ parsed.push(input);
11946
+ }
11947
+ return new Assertion(type, parsed, context.key);
11880
11948
  }
11881
- canonicalID() {
11882
- return this.canonical;
11949
+ evaluate(ctx) {
11950
+ for (let i = 0; i < this.args.length; i++) {
11951
+ const value = this.args[i].evaluate(ctx);
11952
+ if (!checkSubtype(this.type, typeOf(value))) return value;
11953
+ else if (i === this.args.length - 1) throw new RuntimeError(`Expected value to be of type ${typeToString(this.type)}, but found ${typeToString(typeOf(value))} instead.`, this.key);
11954
+ }
11955
+ throw new Error();
11883
11956
  }
11884
- properties() {
11885
- return this.feature && this.feature.properties || {};
11957
+ eachChild(fn) {
11958
+ this.args.forEach(fn);
11886
11959
  }
11887
- parseColor(input) {
11888
- let cached = this._parseColorCache.get(input);
11889
- if (!cached) {
11890
- cached = Color$1.parse(input);
11891
- this._parseColorCache.set(input, cached);
11892
- }
11893
- return cached;
11960
+ outputDefined() {
11961
+ return this.args.every((arg) => arg.outputDefined());
11894
11962
  }
11895
11963
  };
11896
11964
 
11897
- // src/expression/stops.ts
11965
+ // src/expression/definitions/coercion.ts
11966
+ const types = {
11967
+ "to-boolean": BooleanType,
11968
+ "to-color": ColorType,
11969
+ "to-number": NumberType,
11970
+ "to-string": StringType
11971
+ };
11898
11972
  /**
11899
- * Returns the index of the last stop <= input, or 0 if it doesn't exist.
11973
+ * Special form for error-coalescing coercion expressions "to-number",
11974
+ * "to-color". Since these coercions can fail at runtime, they accept multiple
11975
+ * arguments, only evaluating one at a time until one succeeds.
11976
+ *
11900
11977
  * @private
11901
11978
  */
11902
- function findStopLessThanOrEqualTo(stops, input, key) {
11903
- const lastIndex = stops.length - 1;
11904
- let lowerIndex = 0;
11905
- let upperIndex = lastIndex;
11906
- let currentIndex = 0;
11907
- let currentValue, nextValue;
11908
- while (lowerIndex <= upperIndex) {
11909
- currentIndex = Math.floor((lowerIndex + upperIndex) / 2);
11910
- currentValue = stops[currentIndex];
11911
- nextValue = stops[currentIndex + 1];
11912
- if (currentValue <= input) {
11913
- if (currentIndex === lastIndex || input < nextValue) return currentIndex;
11914
- lowerIndex = currentIndex + 1;
11915
- } else if (currentValue > input) upperIndex = currentIndex - 1;
11916
- else throw new RuntimeError("Input is not a number.", key);
11917
- }
11918
- return 0;
11919
- }
11920
-
11921
- // src/expression/definitions/step.ts
11922
- var Step = class Step {
11923
- constructor(type, input, stops, key) {
11979
+ var Coercion = class Coercion {
11980
+ constructor(type, args, key) {
11924
11981
  this.type = type;
11925
- this.input = input;
11982
+ this.args = args;
11926
11983
  this.key = key;
11927
- this.labels = [];
11928
- this.outputs = [];
11929
- for (const [label, expression] of stops) {
11930
- this.labels.push(label);
11931
- this.outputs.push(expression);
11932
- }
11933
11984
  }
11934
11985
  static parse(args, context) {
11935
- if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
11936
- if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
11937
- const input = context.parse(args[1], 1, NumberType);
11938
- if (!input) return null;
11939
- const stops = [];
11940
- let outputType = null;
11941
- if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
11942
- for (let i = 1; i < args.length; i += 2) {
11943
- const label = i === 1 ? -Infinity : args[i];
11944
- const value = args[i + 1];
11945
- const labelKey = i;
11946
- const valueKey = i + 1;
11947
- if (typeof label !== "number") return context.error("Input/output pairs for \"step\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
11948
- if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"step\" expressions must be arranged with input values in strictly ascending order.", labelKey);
11949
- const parsed = context.parse(value, valueKey, outputType);
11950
- if (!parsed) return null;
11951
- outputType = outputType || parsed.type;
11952
- stops.push([label, parsed]);
11986
+ if (args.length < 2) return context.error("Expected at least one argument.");
11987
+ const name = args[0];
11988
+ if (!types[name]) throw new Error(`Can't parse ${name} as it is not part of the known types`);
11989
+ if ((name === "to-boolean" || name === "to-string") && args.length !== 2) return context.error("Expected one argument.");
11990
+ const type = types[name];
11991
+ const parsed = [];
11992
+ for (let i = 1; i < args.length; i++) {
11993
+ const input = context.parse(args[i], i, ValueType);
11994
+ if (!input) return null;
11995
+ parsed.push(input);
11953
11996
  }
11954
- return new Step(outputType, input, stops, context.key);
11955
- }
11956
- evaluate(ctx) {
11957
- const labels = this.labels;
11958
- const outputs = this.outputs;
11959
- if (labels.length === 1) return outputs[0].evaluate(ctx);
11960
- const value = this.input.evaluate(ctx);
11961
- if (value <= labels[0]) return outputs[0].evaluate(ctx);
11962
- const stopCount = labels.length;
11963
- if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
11964
- return outputs[findStopLessThanOrEqualTo(labels, value, this.key)].evaluate(ctx);
11965
- }
11966
- eachChild(fn) {
11967
- fn(this.input);
11968
- for (const expression of this.outputs) fn(expression);
11969
- }
11970
- outputDefined() {
11971
- return this.outputs.every((out) => out.outputDefined());
11972
- }
11973
- };
11974
-
11975
- // node_modules/@mapbox/unitbezier/index.js
11976
- function unitBezier(p1x, p1y, p2x, p2y) {
11977
- const cx = 3 * p1x;
11978
- const bx = 3 * (p2x - p1x) - cx;
11979
- const ax = 1 - cx - bx;
11980
- const cy = 3 * p1y;
11981
- const by = 3 * (p2y - p1y) - cy;
11982
- const ay = 1 - cy - by;
11983
- return function solve(x, epsilon = 1e-6) {
11984
- if (x <= 0) return 0;
11985
- if (x >= 1) return 1;
11986
- let t = x;
11987
- for (let i = 0; i < 8; i++) {
11988
- const x2 = ((ax * t + bx) * t + cx) * t - x;
11989
- if (Math.abs(x2) < epsilon) return ((ay * t + by) * t + cy) * t;
11990
- const d2 = (3 * ax * t + 2 * bx) * t + cx;
11991
- if (Math.abs(d2) < 1e-6) break;
11992
- t -= x2 / d2;
11993
- }
11994
- let t0 = 0;
11995
- let t1 = 1;
11996
- t = x;
11997
- for (let i = 0; i < 20; i++) {
11998
- const x2 = ((ax * t + bx) * t + cx) * t;
11999
- if (Math.abs(x2 - x) < epsilon) break;
12000
- if (x > x2) t0 = t;
12001
- else t1 = t;
12002
- t = (t0 + t1) * .5;
12003
- }
12004
- return ((ay * t + by) * t + cy) * t;
12005
- };
12006
- }
12007
-
12008
- // src/expression/definitions/interpolate.ts
12009
- var Interpolate = class Interpolate {
12010
- constructor(type, operator, interpolation, input, stops, key) {
12011
- this.type = type;
12012
- this.operator = operator;
12013
- this.interpolation = interpolation;
12014
- this.input = input;
12015
- this.key = key;
12016
- this.labels = [];
12017
- this.outputs = [];
12018
- for (const [label, expression] of stops) {
12019
- this.labels.push(label);
12020
- this.outputs.push(expression);
12021
- }
12022
- }
12023
- static interpolationFactor(interpolation, input, lower, upper) {
12024
- let t = 0;
12025
- if (interpolation.name === "exponential") t = exponentialInterpolation(input, interpolation.base, lower, upper);
12026
- else if (interpolation.name === "linear") t = exponentialInterpolation(input, 1, lower, upper);
12027
- else if (interpolation.name === "cubic-bezier") {
12028
- const c = interpolation.controlPoints;
12029
- t = unitBezier(c[0], c[1], c[2], c[3])(exponentialInterpolation(input, 1, lower, upper));
12030
- }
12031
- return t;
12032
- }
12033
- static parse(args, context) {
12034
- let [operator, interpolation, input, ...rest] = args;
12035
- if (!Array.isArray(interpolation) || interpolation.length === 0) return context.error("Expected an interpolation type expression.", 1);
12036
- if (interpolation[0] === "linear") interpolation = { name: "linear" };
12037
- else if (interpolation[0] === "exponential") {
12038
- const base = interpolation[1];
12039
- if (typeof base !== "number") return context.error("Exponential interpolation requires a numeric base.", 1, 1);
12040
- interpolation = {
12041
- name: "exponential",
12042
- base
12043
- };
12044
- } else if (interpolation[0] === "cubic-bezier") {
12045
- const controlPoints = interpolation.slice(1);
12046
- if (controlPoints.length !== 4 || controlPoints.some((t) => typeof t !== "number" || t < 0 || t > 1)) return context.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.", 1);
12047
- interpolation = {
12048
- name: "cubic-bezier",
12049
- controlPoints
12050
- };
12051
- } else return context.error(`Unknown interpolation type ${String(interpolation[0])}`, 1, 0);
12052
- if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
12053
- if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
12054
- input = context.parse(input, 2, NumberType);
12055
- if (!input) return null;
12056
- const stops = [];
12057
- let outputType = null;
12058
- if ((operator === "interpolate-hcl" || operator === "interpolate-lab") && context.expectedType != ColorArrayType) outputType = ColorType;
12059
- else if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
12060
- for (let i = 0; i < rest.length; i += 2) {
12061
- const label = rest[i];
12062
- const value = rest[i + 1];
12063
- const labelKey = i + 3;
12064
- const valueKey = i + 4;
12065
- if (typeof label !== "number") return context.error("Input/output pairs for \"interpolate\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
12066
- if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"interpolate\" expressions must be arranged with input values in strictly ascending order.", labelKey);
12067
- const parsed = context.parse(value, valueKey, outputType);
12068
- if (!parsed) return null;
12069
- outputType = outputType || parsed.type;
12070
- stops.push([label, parsed]);
12071
- }
12072
- if (!verifyType(outputType, NumberType) && !verifyType(outputType, ProjectionDefinitionType) && !verifyType(outputType, ColorType) && !verifyType(outputType, PaddingType) && !verifyType(outputType, NumberArrayType) && !verifyType(outputType, ColorArrayType) && !verifyType(outputType, VariableAnchorOffsetCollectionType) && !verifyType(outputType, array(NumberType))) return context.error(`Type ${typeToString(outputType)} is not interpolatable.`);
12073
- return new Interpolate(outputType, operator, interpolation, input, stops, context.key);
12074
- }
12075
- evaluate(ctx) {
12076
- const labels = this.labels;
12077
- const outputs = this.outputs;
12078
- if (labels.length === 1) return outputs[0].evaluate(ctx);
12079
- const value = this.input.evaluate(ctx);
12080
- if (value <= labels[0]) return outputs[0].evaluate(ctx);
12081
- const stopCount = labels.length;
12082
- if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
12083
- const index = findStopLessThanOrEqualTo(labels, value, this.key);
12084
- const lower = labels[index];
12085
- const upper = labels[index + 1];
12086
- const t = Interpolate.interpolationFactor(this.interpolation, value, lower, upper);
12087
- const outputLower = outputs[index].evaluate(ctx);
12088
- const outputUpper = outputs[index + 1].evaluate(ctx);
12089
- switch (this.operator) {
12090
- case "interpolate": switch (this.type.kind) {
12091
- case "number": return interpolateNumber(outputLower, outputUpper, t);
12092
- case "color": return Color$1.interpolate(outputLower, outputUpper, t);
12093
- case "padding": return Padding.interpolate(outputLower, outputUpper, t);
12094
- case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t);
12095
- case "numberArray": return NumberArray.interpolate(outputLower, outputUpper, t);
12096
- case "variableAnchorOffsetCollection": return VariableAnchorOffsetCollection.interpolate(outputLower, outputUpper, t, this.key);
12097
- case "array": return interpolateArray(outputLower, outputUpper, t);
12098
- case "projectionDefinition": return ProjectionDefinition.interpolate(outputLower, outputUpper, t);
12099
- }
12100
- case "interpolate-hcl": switch (this.type.kind) {
12101
- case "color": return Color$1.interpolate(outputLower, outputUpper, t, "hcl");
12102
- case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "hcl");
12103
- }
12104
- case "interpolate-lab": switch (this.type.kind) {
12105
- case "color": return Color$1.interpolate(outputLower, outputUpper, t, "lab");
12106
- case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "lab");
12107
- }
12108
- }
12109
- }
12110
- eachChild(fn) {
12111
- fn(this.input);
12112
- for (const expression of this.outputs) fn(expression);
12113
- }
12114
- outputDefined() {
12115
- return this.outputs.every((out) => out.outputDefined());
12116
- }
12117
- };
12118
- /**
12119
- * Returns a ratio that can be used to interpolate between exponential function
12120
- * stops.
12121
- * How it works: Two consecutive stop values define a (scaled and shifted) exponential function `f(x) = a * base^x + b`, where `base` is the user-specified base,
12122
- * and `a` and `b` are constants affording sufficient degrees of freedom to fit
12123
- * the function to the given stops.
12124
- *
12125
- * Here's a bit of algebra that lets us compute `f(x)` directly from the stop
12126
- * values without explicitly solving for `a` and `b`:
12127
- *
12128
- * First stop value: `f(x0) = y0 = a * base^x0 + b`
12129
- * Second stop value: `f(x1) = y1 = a * base^x1 + b`
12130
- * => `y1 - y0 = a(base^x1 - base^x0)`
12131
- * => `a = (y1 - y0)/(base^x1 - base^x0)`
12132
- *
12133
- * Desired value: `f(x) = y = a * base^x + b`
12134
- * => `f(x) = y0 + a * (base^x - base^x0)`
12135
- *
12136
- * From the above, we can replace the `a` in `a * (base^x - base^x0)` and do a
12137
- * little algebra:
12138
- * ```
12139
- * a * (base^x - base^x0) = (y1 - y0)/(base^x1 - base^x0) * (base^x - base^x0)
12140
- * = (y1 - y0) * (base^x - base^x0) / (base^x1 - base^x0)
12141
- * ```
12142
- *
12143
- * If we let `(base^x - base^x0) / (base^x1 base^x0)`, then we have
12144
- * `f(x) = y0 + (y1 - y0) * ratio`. In other words, `ratio` may be treated as
12145
- * an interpolation factor between the two stops' output values.
12146
- *
12147
- * (Note: a slightly different form for `ratio`,
12148
- * `(base^(x-x0) - 1) / (base^(x1-x0) - 1) `, is equivalent, but requires fewer
12149
- * expensive `Math.pow()` operations.)
12150
- *
12151
- * @private
12152
- */
12153
- function exponentialInterpolation(input, base, lowerValue, upperValue) {
12154
- const difference = upperValue - lowerValue;
12155
- const progress = input - lowerValue;
12156
- if (difference === 0) return 0;
12157
- else if (base === 1) return progress / difference;
12158
- else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
12159
- }
12160
- const interpolateFactory = {
12161
- color: Color$1.interpolate,
12162
- number: interpolateNumber,
12163
- padding: Padding.interpolate,
12164
- numberArray: NumberArray.interpolate,
12165
- colorArray: ColorArray.interpolate,
12166
- variableAnchorOffsetCollection: VariableAnchorOffsetCollection.interpolate,
12167
- array: interpolateArray
12168
- };
12169
-
12170
- // src/expression/definitions/format.ts
12171
- var FormatExpression = class FormatExpression {
12172
- constructor(sections) {
12173
- this.type = FormattedType;
12174
- this.sections = sections;
12175
- }
12176
- static parse(args, context) {
12177
- if (args.length < 2) return context.error("Expected at least one argument.");
12178
- const firstArg = args[1];
12179
- if (!Array.isArray(firstArg) && typeof firstArg === "object") return context.error("First argument must be an image or text section.");
12180
- const sections = [];
12181
- let nextTokenMayBeObject = false;
12182
- for (let i = 1; i <= args.length - 1; ++i) {
12183
- const arg = args[i];
12184
- if (nextTokenMayBeObject && typeof arg === "object" && !Array.isArray(arg)) {
12185
- nextTokenMayBeObject = false;
12186
- let scale = null;
12187
- if (arg["font-scale"]) {
12188
- scale = context.parse(arg["font-scale"], 1, NumberType);
12189
- if (!scale) return null;
12190
- }
12191
- let font = null;
12192
- if (arg["text-font"]) {
12193
- font = context.parse(arg["text-font"], 1, array(StringType));
12194
- if (!font) return null;
12195
- }
12196
- let textColor = null;
12197
- if (arg["text-color"]) {
12198
- textColor = context.parse(arg["text-color"], 1, ColorType);
12199
- if (!textColor) return null;
12200
- }
12201
- let verticalAlign = null;
12202
- if (arg["vertical-align"]) {
12203
- if (typeof arg["vertical-align"] === "string" && !VERTICAL_ALIGN_OPTIONS.includes(arg["vertical-align"])) return context.error(`'vertical-align' must be one of: 'bottom', 'center', 'top' but found '${arg["vertical-align"]}' instead.`);
12204
- verticalAlign = context.parse(arg["vertical-align"], 1, StringType);
12205
- if (!verticalAlign) return null;
12206
- }
12207
- const lastExpression = sections[sections.length - 1];
12208
- lastExpression.scale = scale;
12209
- lastExpression.font = font;
12210
- lastExpression.textColor = textColor;
12211
- lastExpression.verticalAlign = verticalAlign;
12212
- } else {
12213
- const content = context.parse(args[i], 1, ValueType);
12214
- if (!content) return null;
12215
- const kind = content.type.kind;
12216
- if (kind !== "string" && kind !== "value" && kind !== "null" && kind !== "resolvedImage") return context.error("Formatted text type must be 'string', 'value', 'image' or 'null'.");
12217
- nextTokenMayBeObject = true;
12218
- sections.push({
12219
- content,
12220
- scale: null,
12221
- font: null,
12222
- textColor: null,
12223
- verticalAlign: null
12224
- });
12225
- }
12226
- }
12227
- return new FormatExpression(sections);
12228
- }
12229
- evaluate(ctx) {
12230
- const evaluateSection = (section) => {
12231
- const evaluatedContent = section.content.evaluate(ctx);
12232
- if (typeOf(evaluatedContent) === ResolvedImageType) return new FormattedSection("", evaluatedContent, null, null, null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
12233
- return new FormattedSection(valueToString(evaluatedContent), null, section.scale ? section.scale.evaluate(ctx) : null, section.font ? section.font.evaluate(ctx).join(",") : null, section.textColor ? section.textColor.evaluate(ctx) : null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
12234
- };
12235
- return new Formatted(this.sections.map(evaluateSection));
12236
- }
12237
- eachChild(fn) {
12238
- for (const section of this.sections) {
12239
- fn(section.content);
12240
- if (section.scale) fn(section.scale);
12241
- if (section.font) fn(section.font);
12242
- if (section.textColor) fn(section.textColor);
12243
- if (section.verticalAlign) fn(section.verticalAlign);
12244
- }
12245
- }
12246
- outputDefined() {
12247
- return false;
12248
- }
12249
- };
12250
-
12251
- // src/util/classify_rings.ts
12252
- /**
12253
- * Classifies an array of rings into polygons with outer rings and holes
12254
- * @param rings - the rings to classify
12255
- * @param maxRings - the maximum number of rings to include in a polygon, use 0 to include all rings
12256
- * @returns an array of polygons with internal rings as holes
12257
- */
12258
- function classifyRings$1(rings, maxRings) {
12259
- if (rings.length <= 1) return [rings];
12260
- const polygons = [];
12261
- let polygon;
12262
- let ccw;
12263
- for (const ring of rings) {
12264
- const area = calculateSignedArea(ring);
12265
- if (area === 0) continue;
12266
- ring.area = Math.abs(area);
12267
- if (ccw === void 0) ccw = area < 0;
12268
- if (ccw === area < 0) {
12269
- if (polygon) polygons.push(polygon);
12270
- polygon = [ring];
12271
- } else polygon.push(ring);
12272
- }
12273
- if (polygon) polygons.push(polygon);
12274
- return polygons;
12275
- }
12276
- /**
12277
- * Returns the signed area for the polygon ring. Positive areas are exterior rings and
12278
- * have a clockwise winding. Negative areas are interior rings and have a counter clockwise
12279
- * ordering.
12280
- *
12281
- * @param ring - Exterior or interior ring
12282
- * @returns Signed area
12283
- */
12284
- function calculateSignedArea(ring) {
12285
- let sum = 0;
12286
- for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
12287
- p1 = ring[i];
12288
- p2 = ring[j];
12289
- sum += (p2.x - p1.x) * (p1.y + p2.y);
12290
- }
12291
- return sum;
12292
- }
12293
-
12294
- // src/expression/definitions/assertion.ts
12295
- const types$1 = {
12296
- string: StringType,
12297
- number: NumberType,
12298
- boolean: BooleanType,
12299
- object: ObjectType
12300
- };
12301
- var Assertion = class Assertion {
12302
- constructor(type, args, key) {
12303
- this.type = type;
12304
- this.args = args;
12305
- this.key = key;
12306
- }
12307
- static parse(args, context) {
12308
- if (args.length < 2) return context.error("Expected at least one argument.");
12309
- let i = 1;
12310
- let type;
12311
- const name = args[0];
12312
- if (name === "array") {
12313
- let itemType;
12314
- if (args.length > 2) {
12315
- const type = args[1];
12316
- if (typeof type !== "string" || !(type in types$1) || type === "object") return context.error("The item type argument of \"array\" must be one of string, number, boolean", 1);
12317
- itemType = types$1[type];
12318
- i++;
12319
- } else itemType = ValueType;
12320
- let N;
12321
- if (args.length > 3) {
12322
- if (args[2] !== null && (typeof args[2] !== "number" || args[2] < 0 || args[2] !== Math.floor(args[2]))) return context.error("The length argument to \"array\" must be a positive integer literal", 2);
12323
- N = args[2];
12324
- i++;
12325
- }
12326
- type = array(itemType, N);
12327
- } else {
12328
- if (!types$1[name]) throw new Error(`Types doesn't contain name = ${name}`);
12329
- type = types$1[name];
12330
- }
12331
- const parsed = [];
12332
- for (; i < args.length; i++) {
12333
- const input = context.parse(args[i], i, ValueType);
12334
- if (!input) return null;
12335
- parsed.push(input);
12336
- }
12337
- return new Assertion(type, parsed, context.key);
12338
- }
12339
- evaluate(ctx) {
12340
- for (let i = 0; i < this.args.length; i++) {
12341
- const value = this.args[i].evaluate(ctx);
12342
- if (!checkSubtype(this.type, typeOf(value))) return value;
12343
- else if (i === this.args.length - 1) throw new RuntimeError(`Expected value to be of type ${typeToString(this.type)}, but found ${typeToString(typeOf(value))} instead.`, this.key);
12344
- }
12345
- throw new Error();
12346
- }
12347
- eachChild(fn) {
12348
- this.args.forEach(fn);
12349
- }
12350
- outputDefined() {
12351
- return this.args.every((arg) => arg.outputDefined());
12352
- }
12353
- };
12354
-
12355
- // src/expression/definitions/coercion.ts
12356
- const types = {
12357
- "to-boolean": BooleanType,
12358
- "to-color": ColorType,
12359
- "to-number": NumberType,
12360
- "to-string": StringType
12361
- };
12362
- /**
12363
- * Special form for error-coalescing coercion expressions "to-number",
12364
- * "to-color". Since these coercions can fail at runtime, they accept multiple
12365
- * arguments, only evaluating one at a time until one succeeds.
12366
- *
12367
- * @private
12368
- */
12369
- var Coercion = class Coercion {
12370
- constructor(type, args, key) {
12371
- this.type = type;
12372
- this.args = args;
12373
- this.key = key;
12374
- }
12375
- static parse(args, context) {
12376
- if (args.length < 2) return context.error("Expected at least one argument.");
12377
- const name = args[0];
12378
- if (!types[name]) throw new Error(`Can't parse ${name} as it is not part of the known types`);
12379
- if ((name === "to-boolean" || name === "to-string") && args.length !== 2) return context.error("Expected one argument.");
12380
- const type = types[name];
12381
- const parsed = [];
12382
- for (let i = 1; i < args.length; i++) {
12383
- const input = context.parse(args[i], i, ValueType);
12384
- if (!input) return null;
12385
- parsed.push(input);
12386
- }
12387
- return new Coercion(type, parsed, context.key);
11997
+ return new Coercion(type, parsed, context.key);
12388
11998
  }
12389
11999
  evaluate(ctx) {
12390
12000
  switch (this.type.kind) {
@@ -12467,8 +12077,172 @@ var Coercion = class Coercion {
12467
12077
  eachChild(fn) {
12468
12078
  this.args.forEach(fn);
12469
12079
  }
12470
- outputDefined() {
12471
- return this.args.every((arg) => arg.outputDefined());
12080
+ outputDefined() {
12081
+ return this.args.every((arg) => arg.outputDefined());
12082
+ }
12083
+ };
12084
+
12085
+ // src/expression/evaluation_context.ts
12086
+ const geometryTypes = [
12087
+ "Unknown",
12088
+ "Point",
12089
+ "LineString",
12090
+ "Polygon"
12091
+ ];
12092
+ var EvaluationContext = class {
12093
+ constructor() {
12094
+ this.globals = null;
12095
+ this.feature = null;
12096
+ this.featureState = null;
12097
+ this.formattedSection = null;
12098
+ this._parseColorCache = /* @__PURE__ */ new Map();
12099
+ this.availableImages = null;
12100
+ this.canonical = null;
12101
+ }
12102
+ id() {
12103
+ return this.feature && "id" in this.feature ? this.feature.id : null;
12104
+ }
12105
+ geometryType() {
12106
+ return this.feature ? typeof this.feature.type === "number" ? geometryTypes[this.feature.type] : this.feature.type : null;
12107
+ }
12108
+ geometry() {
12109
+ return this.feature && "geometry" in this.feature ? this.feature.geometry : null;
12110
+ }
12111
+ canonicalID() {
12112
+ return this.canonical;
12113
+ }
12114
+ properties() {
12115
+ return this.feature && this.feature.properties || {};
12116
+ }
12117
+ parseColor(input) {
12118
+ let cached = this._parseColorCache.get(input);
12119
+ if (!cached) {
12120
+ cached = Color$1.parse(input);
12121
+ this._parseColorCache.set(input, cached);
12122
+ }
12123
+ return cached;
12124
+ }
12125
+ };
12126
+
12127
+ // src/expression/parsing_context.ts
12128
+ /**
12129
+ * State associated parsing at a given point in an expression tree.
12130
+ * @private
12131
+ */
12132
+ var ParsingContext = class ParsingContext {
12133
+ constructor(registry, isConstantFunc, path = [], expectedType, scope = new Scope(), errors = []) {
12134
+ this.registry = registry;
12135
+ this.path = path;
12136
+ this.key = path.map((part) => `[${part}]`).join("");
12137
+ this.scope = scope;
12138
+ this.errors = errors;
12139
+ this.expectedType = expectedType;
12140
+ this._isConstant = isConstantFunc;
12141
+ }
12142
+ /**
12143
+ * @param expr the JSON expression to parse
12144
+ * @param index the optional argument index if this expression is an argument of a parent expression that's being parsed
12145
+ * @param options
12146
+ * @param options.omitTypeAnnotations set true to omit inferred type annotations. Caller beware: with this option set, the parsed expression's type will NOT satisfy `expectedType` if it would normally be wrapped in an inferred annotation.
12147
+ * @private
12148
+ */
12149
+ parse(expr, index, expectedType, bindings, options = {}) {
12150
+ if (index != null) return this.concat(index, expectedType, bindings)._parse(expr, options);
12151
+ return this._parse(expr, options);
12152
+ }
12153
+ _parse(expr, options) {
12154
+ if (expr === null || typeof expr === "string" || typeof expr === "boolean" || typeof expr === "number") expr = ["literal", expr];
12155
+ const key = this.key;
12156
+ function annotate(parsed, type, typeAnnotation) {
12157
+ if (typeAnnotation === "assert") return new Assertion(type, [parsed], key);
12158
+ else if (typeAnnotation === "coerce") return new Coercion(type, [parsed], key);
12159
+ else return parsed;
12160
+ }
12161
+ if (Array.isArray(expr)) {
12162
+ if (expr.length === 0) return this.error("Expected an array with at least one element. If you wanted a literal array, use [\"literal\", []].");
12163
+ const op = expr[0];
12164
+ if (typeof op !== "string") {
12165
+ this.error(`Expression name must be a string, but found ${typeof op} instead. If you wanted a literal array, use ["literal", [...]].`, 0);
12166
+ return null;
12167
+ }
12168
+ const Expr = this.registry[op];
12169
+ if (Expr) {
12170
+ let parsed = Expr.parse(expr, this);
12171
+ if (!parsed) return null;
12172
+ if (this.expectedType) {
12173
+ const expected = this.expectedType;
12174
+ const actual = parsed.type;
12175
+ if ((expected.kind === "string" || expected.kind === "number" || expected.kind === "boolean" || expected.kind === "object" || expected.kind === "array") && actual.kind === "value") parsed = annotate(parsed, expected, options.typeAnnotation || "assert");
12176
+ else if ("projectionDefinition" === expected.kind && [
12177
+ "string",
12178
+ "array",
12179
+ "value"
12180
+ ].includes(actual.kind) || [
12181
+ "color",
12182
+ "formatted",
12183
+ "resolvedImage"
12184
+ ].includes(expected.kind) && ["value", "string"].includes(actual.kind) || ["padding", "numberArray"].includes(expected.kind) && [
12185
+ "value",
12186
+ "number",
12187
+ "array"
12188
+ ].includes(actual.kind) || "colorArray" === expected.kind && [
12189
+ "value",
12190
+ "string",
12191
+ "array"
12192
+ ].includes(actual.kind) || "variableAnchorOffsetCollection" === expected.kind && ["value", "array"].includes(actual.kind)) parsed = annotate(parsed, expected, options.typeAnnotation || "coerce");
12193
+ else if (this.checkSubtype(expected, actual)) return null;
12194
+ }
12195
+ if (!(parsed instanceof Literal) && parsed.type.kind !== "resolvedImage" && this._isConstant(parsed)) {
12196
+ const ec = new EvaluationContext();
12197
+ try {
12198
+ parsed = new Literal(parsed.type, parsed.evaluate(ec));
12199
+ } catch (e) {
12200
+ this.error(e.message);
12201
+ return null;
12202
+ }
12203
+ }
12204
+ return parsed;
12205
+ }
12206
+ return this.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
12207
+ } else if (typeof expr === "undefined") return this.error("'undefined' value invalid. Use null instead.");
12208
+ else if (typeof expr === "object") return this.error("Bare objects invalid. Use [\"literal\", {...}] instead.");
12209
+ else return this.error(`Expected an array, but found ${typeof expr} instead.`);
12210
+ }
12211
+ /**
12212
+ * Returns a copy of this context suitable for parsing the subexpression at
12213
+ * index `index`, optionally appending to 'let' binding map.
12214
+ *
12215
+ * Note that `errors` property, intended for collecting errors while
12216
+ * parsing, is copied by reference rather than cloned.
12217
+ * @private
12218
+ */
12219
+ concat(index, expectedType, bindings) {
12220
+ const path = typeof index === "number" ? this.path.concat(index) : this.path;
12221
+ const scope = bindings ? this.scope.concat(bindings) : this.scope;
12222
+ return new ParsingContext(this.registry, this._isConstant, path, expectedType || null, scope, this.errors);
12223
+ }
12224
+ /**
12225
+ * Push a parsing (or type checking) error into the `this.errors`
12226
+ * @param error The message
12227
+ * @param keys Optionally specify the source of the error at a child
12228
+ * of the current expression at `this.key`.
12229
+ * @private
12230
+ */
12231
+ error(error, ...keys) {
12232
+ const key = `${this.key}${keys.map((k) => `[${k}]`).join("")}`;
12233
+ this.errors.push(new ExpressionParsingError(key, error));
12234
+ }
12235
+ /**
12236
+ * Returns null if `t` is a subtype of `expected`; otherwise returns an
12237
+ * error message and also pushes it to `this.errors`.
12238
+ * @param expected The expected type
12239
+ * @param t The actual type
12240
+ * @returns null if `t` is a subtype of `expected`; otherwise returns an error message
12241
+ */
12242
+ checkSubtype(expected, t) {
12243
+ const error = checkSubtype(expected, t);
12244
+ if (error) this.error(error);
12245
+ return error;
12472
12246
  }
12473
12247
  };
12474
12248
 
@@ -12740,70 +12514,343 @@ var Case = class Case {
12740
12514
  branches.push([test, result]);
12741
12515
  outputType = outputType || result.type;
12742
12516
  }
12743
- const otherwise = context.parse(args[args.length - 1], args.length - 1, outputType);
12744
- if (!otherwise) return null;
12745
- if (!outputType) throw new Error("Can't infer output type");
12746
- return new Case(outputType, branches, otherwise);
12517
+ const otherwise = context.parse(args[args.length - 1], args.length - 1, outputType);
12518
+ if (!otherwise) return null;
12519
+ if (!outputType) throw new Error("Can't infer output type");
12520
+ return new Case(outputType, branches, otherwise);
12521
+ }
12522
+ evaluate(ctx) {
12523
+ for (const [test, expression] of this.branches) if (test.evaluate(ctx)) return expression.evaluate(ctx);
12524
+ return this.otherwise.evaluate(ctx);
12525
+ }
12526
+ eachChild(fn) {
12527
+ for (const [test, expression] of this.branches) {
12528
+ fn(test);
12529
+ fn(expression);
12530
+ }
12531
+ fn(this.otherwise);
12532
+ }
12533
+ outputDefined() {
12534
+ return this.branches.every(([_, out]) => out.outputDefined()) && this.otherwise.outputDefined();
12535
+ }
12536
+ };
12537
+
12538
+ // src/expression/definitions/slice.ts
12539
+ var Slice = class Slice {
12540
+ constructor(type, input, beginIndex, key, endIndex) {
12541
+ this.type = type;
12542
+ this.input = input;
12543
+ this.beginIndex = beginIndex;
12544
+ this.key = key;
12545
+ this.endIndex = endIndex;
12546
+ }
12547
+ static parse(args, context) {
12548
+ if (args.length <= 2 || args.length >= 5) return context.error(`Expected 2 or 3 arguments, but found ${args.length - 1} instead.`);
12549
+ const input = context.parse(args[1], 1, ValueType);
12550
+ const beginIndex = context.parse(args[2], 2, NumberType);
12551
+ if (!input || !beginIndex) return null;
12552
+ if (!isValidType(input.type, [
12553
+ array(ValueType),
12554
+ StringType,
12555
+ ValueType
12556
+ ])) return context.error(`Expected first argument to be of type array or string, but found ${typeToString(input.type)} instead`);
12557
+ if (args.length === 4) {
12558
+ const endIndex = context.parse(args[3], 3, NumberType);
12559
+ if (!endIndex) return null;
12560
+ return new Slice(input.type, input, beginIndex, context.key, endIndex);
12561
+ } else return new Slice(input.type, input, beginIndex, context.key);
12562
+ }
12563
+ evaluate(ctx) {
12564
+ const input = this.input.evaluate(ctx);
12565
+ const beginIndex = this.beginIndex.evaluate(ctx);
12566
+ let endIndex;
12567
+ if (this.endIndex) endIndex = this.endIndex.evaluate(ctx);
12568
+ if (isValidNativeType(input, ["string"])) return [...input].slice(beginIndex, endIndex).join("");
12569
+ else if (isValidNativeType(input, ["array"])) return input.slice(beginIndex, endIndex);
12570
+ else throw new RuntimeError(`Expected first argument to be of type array or string, but found ${typeToString(typeOf(input))} instead.`, this.key);
12571
+ }
12572
+ eachChild(fn) {
12573
+ fn(this.input);
12574
+ fn(this.beginIndex);
12575
+ if (this.endIndex) fn(this.endIndex);
12576
+ }
12577
+ outputDefined() {
12578
+ return false;
12579
+ }
12580
+ };
12581
+
12582
+ // src/expression/stops.ts
12583
+ /**
12584
+ * Returns the index of the last stop <= input, or 0 if it doesn't exist.
12585
+ * @private
12586
+ */
12587
+ function findStopLessThanOrEqualTo(stops, input, key) {
12588
+ const lastIndex = stops.length - 1;
12589
+ let lowerIndex = 0;
12590
+ let upperIndex = lastIndex;
12591
+ let currentIndex = 0;
12592
+ let currentValue, nextValue;
12593
+ while (lowerIndex <= upperIndex) {
12594
+ currentIndex = Math.floor((lowerIndex + upperIndex) / 2);
12595
+ currentValue = stops[currentIndex];
12596
+ nextValue = stops[currentIndex + 1];
12597
+ if (currentValue <= input) {
12598
+ if (currentIndex === lastIndex || input < nextValue) return currentIndex;
12599
+ lowerIndex = currentIndex + 1;
12600
+ } else if (currentValue > input) upperIndex = currentIndex - 1;
12601
+ else throw new RuntimeError("Input is not a number.", key);
12602
+ }
12603
+ return 0;
12604
+ }
12605
+
12606
+ // src/expression/definitions/step.ts
12607
+ var Step = class Step {
12608
+ constructor(type, input, stops, key) {
12609
+ this.type = type;
12610
+ this.input = input;
12611
+ this.key = key;
12612
+ this.labels = [];
12613
+ this.outputs = [];
12614
+ for (const [label, expression] of stops) {
12615
+ this.labels.push(label);
12616
+ this.outputs.push(expression);
12617
+ }
12618
+ }
12619
+ static parse(args, context) {
12620
+ if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
12621
+ if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
12622
+ const input = context.parse(args[1], 1, NumberType);
12623
+ if (!input) return null;
12624
+ const stops = [];
12625
+ let outputType = null;
12626
+ if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
12627
+ for (let i = 1; i < args.length; i += 2) {
12628
+ const label = i === 1 ? -Infinity : args[i];
12629
+ const value = args[i + 1];
12630
+ const labelKey = i;
12631
+ const valueKey = i + 1;
12632
+ if (typeof label !== "number") return context.error("Input/output pairs for \"step\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
12633
+ if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"step\" expressions must be arranged with input values in strictly ascending order.", labelKey);
12634
+ const parsed = context.parse(value, valueKey, outputType);
12635
+ if (!parsed) return null;
12636
+ outputType = outputType || parsed.type;
12637
+ stops.push([label, parsed]);
12638
+ }
12639
+ return new Step(outputType, input, stops, context.key);
12640
+ }
12641
+ evaluate(ctx) {
12642
+ const labels = this.labels;
12643
+ const outputs = this.outputs;
12644
+ if (labels.length === 1) return outputs[0].evaluate(ctx);
12645
+ const value = this.input.evaluate(ctx);
12646
+ if (value <= labels[0]) return outputs[0].evaluate(ctx);
12647
+ const stopCount = labels.length;
12648
+ if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
12649
+ return outputs[findStopLessThanOrEqualTo(labels, value, this.key)].evaluate(ctx);
12650
+ }
12651
+ eachChild(fn) {
12652
+ fn(this.input);
12653
+ for (const expression of this.outputs) fn(expression);
12654
+ }
12655
+ outputDefined() {
12656
+ return this.outputs.every((out) => out.outputDefined());
12657
+ }
12658
+ };
12659
+
12660
+ // node_modules/@mapbox/unitbezier/index.js
12661
+ function unitBezier(p1x, p1y, p2x, p2y) {
12662
+ const cx = 3 * p1x;
12663
+ const bx = 3 * (p2x - p1x) - cx;
12664
+ const ax = 1 - cx - bx;
12665
+ const cy = 3 * p1y;
12666
+ const by = 3 * (p2y - p1y) - cy;
12667
+ const ay = 1 - cy - by;
12668
+ return function solve(x, epsilon = 1e-6) {
12669
+ if (x <= 0) return 0;
12670
+ if (x >= 1) return 1;
12671
+ let t = x;
12672
+ for (let i = 0; i < 8; i++) {
12673
+ const x2 = ((ax * t + bx) * t + cx) * t - x;
12674
+ if (Math.abs(x2) < epsilon) return ((ay * t + by) * t + cy) * t;
12675
+ const d2 = (3 * ax * t + 2 * bx) * t + cx;
12676
+ if (Math.abs(d2) < 1e-6) break;
12677
+ t -= x2 / d2;
12678
+ }
12679
+ let t0 = 0;
12680
+ let t1 = 1;
12681
+ t = x;
12682
+ for (let i = 0; i < 20; i++) {
12683
+ const x2 = ((ax * t + bx) * t + cx) * t;
12684
+ if (Math.abs(x2 - x) < epsilon) break;
12685
+ if (x > x2) t0 = t;
12686
+ else t1 = t;
12687
+ t = (t0 + t1) * .5;
12688
+ }
12689
+ return ((ay * t + by) * t + cy) * t;
12690
+ };
12691
+ }
12692
+
12693
+ // src/expression/definitions/interpolate.ts
12694
+ var Interpolate = class Interpolate {
12695
+ constructor(type, operator, interpolation, input, stops, key) {
12696
+ this.type = type;
12697
+ this.operator = operator;
12698
+ this.interpolation = interpolation;
12699
+ this.input = input;
12700
+ this.key = key;
12701
+ this.labels = [];
12702
+ this.outputs = [];
12703
+ for (const [label, expression] of stops) {
12704
+ this.labels.push(label);
12705
+ this.outputs.push(expression);
12706
+ }
12707
+ }
12708
+ static interpolationFactor(interpolation, input, lower, upper) {
12709
+ let t = 0;
12710
+ if (interpolation.name === "exponential") t = exponentialInterpolation(input, interpolation.base, lower, upper);
12711
+ else if (interpolation.name === "linear") t = exponentialInterpolation(input, 1, lower, upper);
12712
+ else if (interpolation.name === "cubic-bezier") {
12713
+ const c = interpolation.controlPoints;
12714
+ t = unitBezier(c[0], c[1], c[2], c[3])(exponentialInterpolation(input, 1, lower, upper));
12715
+ }
12716
+ return t;
12717
+ }
12718
+ static parse(args, context) {
12719
+ let [operator, interpolation, input, ...rest] = args;
12720
+ if (!Array.isArray(interpolation) || interpolation.length === 0) return context.error("Expected an interpolation type expression.", 1);
12721
+ if (interpolation[0] === "linear") interpolation = { name: "linear" };
12722
+ else if (interpolation[0] === "exponential") {
12723
+ const base = interpolation[1];
12724
+ if (typeof base !== "number") return context.error("Exponential interpolation requires a numeric base.", 1, 1);
12725
+ interpolation = {
12726
+ name: "exponential",
12727
+ base
12728
+ };
12729
+ } else if (interpolation[0] === "cubic-bezier") {
12730
+ const controlPoints = interpolation.slice(1);
12731
+ if (controlPoints.length !== 4 || controlPoints.some((t) => typeof t !== "number" || t < 0 || t > 1)) return context.error("Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.", 1);
12732
+ interpolation = {
12733
+ name: "cubic-bezier",
12734
+ controlPoints
12735
+ };
12736
+ } else return context.error(`Unknown interpolation type ${String(interpolation[0])}`, 1, 0);
12737
+ if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
12738
+ if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
12739
+ input = context.parse(input, 2, NumberType);
12740
+ if (!input) return null;
12741
+ const stops = [];
12742
+ let outputType = null;
12743
+ if ((operator === "interpolate-hcl" || operator === "interpolate-lab") && context.expectedType != ColorArrayType) outputType = ColorType;
12744
+ else if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
12745
+ for (let i = 0; i < rest.length; i += 2) {
12746
+ const label = rest[i];
12747
+ const value = rest[i + 1];
12748
+ const labelKey = i + 3;
12749
+ const valueKey = i + 4;
12750
+ if (typeof label !== "number") return context.error("Input/output pairs for \"interpolate\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", labelKey);
12751
+ if (stops.length && stops[stops.length - 1][0] >= label) return context.error("Input/output pairs for \"interpolate\" expressions must be arranged with input values in strictly ascending order.", labelKey);
12752
+ const parsed = context.parse(value, valueKey, outputType);
12753
+ if (!parsed) return null;
12754
+ outputType = outputType || parsed.type;
12755
+ stops.push([label, parsed]);
12756
+ }
12757
+ if (!verifyType(outputType, NumberType) && !verifyType(outputType, ProjectionDefinitionType) && !verifyType(outputType, ColorType) && !verifyType(outputType, PaddingType) && !verifyType(outputType, NumberArrayType) && !verifyType(outputType, ColorArrayType) && !verifyType(outputType, VariableAnchorOffsetCollectionType) && !verifyType(outputType, array(NumberType))) return context.error(`Type ${typeToString(outputType)} is not interpolatable.`);
12758
+ return new Interpolate(outputType, operator, interpolation, input, stops, context.key);
12747
12759
  }
12748
12760
  evaluate(ctx) {
12749
- for (const [test, expression] of this.branches) if (test.evaluate(ctx)) return expression.evaluate(ctx);
12750
- return this.otherwise.evaluate(ctx);
12751
- }
12752
- eachChild(fn) {
12753
- for (const [test, expression] of this.branches) {
12754
- fn(test);
12755
- fn(expression);
12761
+ const labels = this.labels;
12762
+ const outputs = this.outputs;
12763
+ if (labels.length === 1) return outputs[0].evaluate(ctx);
12764
+ const value = this.input.evaluate(ctx);
12765
+ if (value <= labels[0]) return outputs[0].evaluate(ctx);
12766
+ const stopCount = labels.length;
12767
+ if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
12768
+ const index = findStopLessThanOrEqualTo(labels, value, this.key);
12769
+ const lower = labels[index];
12770
+ const upper = labels[index + 1];
12771
+ const t = Interpolate.interpolationFactor(this.interpolation, value, lower, upper);
12772
+ const outputLower = outputs[index].evaluate(ctx);
12773
+ const outputUpper = outputs[index + 1].evaluate(ctx);
12774
+ switch (this.operator) {
12775
+ case "interpolate": switch (this.type.kind) {
12776
+ case "number": return interpolateNumber(outputLower, outputUpper, t);
12777
+ case "color": return Color$1.interpolate(outputLower, outputUpper, t);
12778
+ case "padding": return Padding.interpolate(outputLower, outputUpper, t);
12779
+ case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t);
12780
+ case "numberArray": return NumberArray.interpolate(outputLower, outputUpper, t);
12781
+ case "variableAnchorOffsetCollection": return VariableAnchorOffsetCollection.interpolate(outputLower, outputUpper, t, this.key);
12782
+ case "array": return interpolateArray(outputLower, outputUpper, t);
12783
+ case "projectionDefinition": return ProjectionDefinition.interpolate(outputLower, outputUpper, t);
12784
+ }
12785
+ case "interpolate-hcl": switch (this.type.kind) {
12786
+ case "color": return Color$1.interpolate(outputLower, outputUpper, t, "hcl");
12787
+ case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "hcl");
12788
+ }
12789
+ case "interpolate-lab": switch (this.type.kind) {
12790
+ case "color": return Color$1.interpolate(outputLower, outputUpper, t, "lab");
12791
+ case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "lab");
12792
+ }
12756
12793
  }
12757
- fn(this.otherwise);
12758
- }
12759
- outputDefined() {
12760
- return this.branches.every(([_, out]) => out.outputDefined()) && this.otherwise.outputDefined();
12761
- }
12762
- };
12763
-
12764
- // src/expression/definitions/slice.ts
12765
- var Slice = class Slice {
12766
- constructor(type, input, beginIndex, key, endIndex) {
12767
- this.type = type;
12768
- this.input = input;
12769
- this.beginIndex = beginIndex;
12770
- this.key = key;
12771
- this.endIndex = endIndex;
12772
- }
12773
- static parse(args, context) {
12774
- if (args.length <= 2 || args.length >= 5) return context.error(`Expected 2 or 3 arguments, but found ${args.length - 1} instead.`);
12775
- const input = context.parse(args[1], 1, ValueType);
12776
- const beginIndex = context.parse(args[2], 2, NumberType);
12777
- if (!input || !beginIndex) return null;
12778
- if (!isValidType(input.type, [
12779
- array(ValueType),
12780
- StringType,
12781
- ValueType
12782
- ])) return context.error(`Expected first argument to be of type array or string, but found ${typeToString(input.type)} instead`);
12783
- if (args.length === 4) {
12784
- const endIndex = context.parse(args[3], 3, NumberType);
12785
- if (!endIndex) return null;
12786
- return new Slice(input.type, input, beginIndex, context.key, endIndex);
12787
- } else return new Slice(input.type, input, beginIndex, context.key);
12788
- }
12789
- evaluate(ctx) {
12790
- const input = this.input.evaluate(ctx);
12791
- const beginIndex = this.beginIndex.evaluate(ctx);
12792
- let endIndex;
12793
- if (this.endIndex) endIndex = this.endIndex.evaluate(ctx);
12794
- if (isValidNativeType(input, ["string"])) return [...input].slice(beginIndex, endIndex).join("");
12795
- else if (isValidNativeType(input, ["array"])) return input.slice(beginIndex, endIndex);
12796
- else throw new RuntimeError(`Expected first argument to be of type array or string, but found ${typeToString(typeOf(input))} instead.`, this.key);
12797
12794
  }
12798
12795
  eachChild(fn) {
12799
12796
  fn(this.input);
12800
- fn(this.beginIndex);
12801
- if (this.endIndex) fn(this.endIndex);
12797
+ for (const expression of this.outputs) fn(expression);
12802
12798
  }
12803
12799
  outputDefined() {
12804
- return false;
12800
+ return this.outputs.every((out) => out.outputDefined());
12805
12801
  }
12806
12802
  };
12803
+ /**
12804
+ * Returns a ratio that can be used to interpolate between exponential function
12805
+ * stops.
12806
+ * How it works: Two consecutive stop values define a (scaled and shifted) exponential function `f(x) = a * base^x + b`, where `base` is the user-specified base,
12807
+ * and `a` and `b` are constants affording sufficient degrees of freedom to fit
12808
+ * the function to the given stops.
12809
+ *
12810
+ * Here's a bit of algebra that lets us compute `f(x)` directly from the stop
12811
+ * values without explicitly solving for `a` and `b`:
12812
+ *
12813
+ * First stop value: `f(x0) = y0 = a * base^x0 + b`
12814
+ * Second stop value: `f(x1) = y1 = a * base^x1 + b`
12815
+ * => `y1 - y0 = a(base^x1 - base^x0)`
12816
+ * => `a = (y1 - y0)/(base^x1 - base^x0)`
12817
+ *
12818
+ * Desired value: `f(x) = y = a * base^x + b`
12819
+ * => `f(x) = y0 + a * (base^x - base^x0)`
12820
+ *
12821
+ * From the above, we can replace the `a` in `a * (base^x - base^x0)` and do a
12822
+ * little algebra:
12823
+ * ```
12824
+ * a * (base^x - base^x0) = (y1 - y0)/(base^x1 - base^x0) * (base^x - base^x0)
12825
+ * = (y1 - y0) * (base^x - base^x0) / (base^x1 - base^x0)
12826
+ * ```
12827
+ *
12828
+ * If we let `(base^x - base^x0) / (base^x1 base^x0)`, then we have
12829
+ * `f(x) = y0 + (y1 - y0) * ratio`. In other words, `ratio` may be treated as
12830
+ * an interpolation factor between the two stops' output values.
12831
+ *
12832
+ * (Note: a slightly different form for `ratio`,
12833
+ * `(base^(x-x0) - 1) / (base^(x1-x0) - 1) `, is equivalent, but requires fewer
12834
+ * expensive `Math.pow()` operations.)
12835
+ *
12836
+ * @private
12837
+ */
12838
+ function exponentialInterpolation(input, base, lowerValue, upperValue) {
12839
+ const difference = upperValue - lowerValue;
12840
+ const progress = input - lowerValue;
12841
+ if (difference === 0) return 0;
12842
+ else if (base === 1) return progress / difference;
12843
+ else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
12844
+ }
12845
+ const interpolateFactory = {
12846
+ color: Color$1.interpolate,
12847
+ number: interpolateNumber,
12848
+ padding: Padding.interpolate,
12849
+ numberArray: NumberArray.interpolate,
12850
+ colorArray: ColorArray.interpolate,
12851
+ variableAnchorOffsetCollection: VariableAnchorOffsetCollection.interpolate,
12852
+ array: interpolateArray
12853
+ };
12807
12854
 
12808
12855
  // src/expression/definitions/coalesce.ts
12809
12856
  var Coalesce = class Coalesce {
@@ -13076,6 +13123,87 @@ var NumberFormat = class NumberFormat {
13076
13123
  }
13077
13124
  };
13078
13125
 
13126
+ // src/expression/definitions/format.ts
13127
+ var FormatExpression = class FormatExpression {
13128
+ constructor(sections) {
13129
+ this.type = FormattedType;
13130
+ this.sections = sections;
13131
+ }
13132
+ static parse(args, context) {
13133
+ if (args.length < 2) return context.error("Expected at least one argument.");
13134
+ const firstArg = args[1];
13135
+ if (!Array.isArray(firstArg) && typeof firstArg === "object") return context.error("First argument must be an image or text section.");
13136
+ const sections = [];
13137
+ let nextTokenMayBeObject = false;
13138
+ for (let i = 1; i <= args.length - 1; ++i) {
13139
+ const arg = args[i];
13140
+ if (nextTokenMayBeObject && typeof arg === "object" && !Array.isArray(arg)) {
13141
+ nextTokenMayBeObject = false;
13142
+ let scale = null;
13143
+ if (arg["font-scale"]) {
13144
+ scale = context.parse(arg["font-scale"], 1, NumberType);
13145
+ if (!scale) return null;
13146
+ }
13147
+ let font = null;
13148
+ if (arg["text-font"]) {
13149
+ font = context.parse(arg["text-font"], 1, array(StringType));
13150
+ if (!font) return null;
13151
+ }
13152
+ let textColor = null;
13153
+ if (arg["text-color"]) {
13154
+ textColor = context.parse(arg["text-color"], 1, ColorType);
13155
+ if (!textColor) return null;
13156
+ }
13157
+ let verticalAlign = null;
13158
+ if (arg["vertical-align"]) {
13159
+ if (typeof arg["vertical-align"] === "string" && !VERTICAL_ALIGN_OPTIONS.includes(arg["vertical-align"])) return context.error(`'vertical-align' must be one of: 'bottom', 'center', 'top' but found '${arg["vertical-align"]}' instead.`);
13160
+ verticalAlign = context.parse(arg["vertical-align"], 1, StringType);
13161
+ if (!verticalAlign) return null;
13162
+ }
13163
+ const lastExpression = sections[sections.length - 1];
13164
+ lastExpression.scale = scale;
13165
+ lastExpression.font = font;
13166
+ lastExpression.textColor = textColor;
13167
+ lastExpression.verticalAlign = verticalAlign;
13168
+ } else {
13169
+ const content = context.parse(args[i], 1, ValueType);
13170
+ if (!content) return null;
13171
+ const kind = content.type.kind;
13172
+ if (kind !== "string" && kind !== "value" && kind !== "null" && kind !== "resolvedImage") return context.error("Formatted text type must be 'string', 'value', 'image' or 'null'.");
13173
+ nextTokenMayBeObject = true;
13174
+ sections.push({
13175
+ content,
13176
+ scale: null,
13177
+ font: null,
13178
+ textColor: null,
13179
+ verticalAlign: null
13180
+ });
13181
+ }
13182
+ }
13183
+ return new FormatExpression(sections);
13184
+ }
13185
+ evaluate(ctx) {
13186
+ const evaluateSection = (section) => {
13187
+ const evaluatedContent = section.content.evaluate(ctx);
13188
+ if (typeOf(evaluatedContent) === ResolvedImageType) return new FormattedSection("", evaluatedContent, null, null, null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
13189
+ return new FormattedSection(valueToString(evaluatedContent), null, section.scale ? section.scale.evaluate(ctx) : null, section.font ? section.font.evaluate(ctx).join(",") : null, section.textColor ? section.textColor.evaluate(ctx) : null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
13190
+ };
13191
+ return new Formatted(this.sections.map(evaluateSection));
13192
+ }
13193
+ eachChild(fn) {
13194
+ for (const section of this.sections) {
13195
+ fn(section.content);
13196
+ if (section.scale) fn(section.scale);
13197
+ if (section.font) fn(section.font);
13198
+ if (section.textColor) fn(section.textColor);
13199
+ if (section.verticalAlign) fn(section.verticalAlign);
13200
+ }
13201
+ }
13202
+ outputDefined() {
13203
+ return false;
13204
+ }
13205
+ };
13206
+
13079
13207
  // src/expression/definitions/image.ts
13080
13208
  var ImageExpression = class ImageExpression {
13081
13209
  constructor(input) {
@@ -13425,21 +13553,64 @@ var TinyQueue = class {
13425
13553
  }
13426
13554
  data[pos] = item;
13427
13555
  }
13428
- _down(pos) {
13429
- const { data, compare } = this;
13430
- const halfLength = this.length >> 1;
13431
- const item = data[pos];
13432
- while (pos < halfLength) {
13433
- let bestChild = (pos << 1) + 1;
13434
- const right = bestChild + 1;
13435
- if (right < this.length && compare(data[right], data[bestChild]) < 0) bestChild = right;
13436
- if (compare(data[bestChild], item) >= 0) break;
13437
- data[pos] = data[bestChild];
13438
- pos = bestChild;
13439
- }
13440
- data[pos] = item;
13556
+ _down(pos) {
13557
+ const { data, compare } = this;
13558
+ const halfLength = this.length >> 1;
13559
+ const item = data[pos];
13560
+ while (pos < halfLength) {
13561
+ let bestChild = (pos << 1) + 1;
13562
+ const right = bestChild + 1;
13563
+ if (right < this.length && compare(data[right], data[bestChild]) < 0) bestChild = right;
13564
+ if (compare(data[bestChild], item) >= 0) break;
13565
+ data[pos] = data[bestChild];
13566
+ pos = bestChild;
13567
+ }
13568
+ data[pos] = item;
13569
+ }
13570
+ };
13571
+
13572
+ // src/util/classify_rings.ts
13573
+ /**
13574
+ * Classifies an array of rings into polygons with outer rings and holes
13575
+ * @param rings - the rings to classify
13576
+ * @param maxRings - the maximum number of rings to include in a polygon, use 0 to include all rings
13577
+ * @returns an array of polygons with internal rings as holes
13578
+ */
13579
+ function classifyRings$1(rings, maxRings) {
13580
+ if (rings.length <= 1) return [rings];
13581
+ const polygons = [];
13582
+ let polygon;
13583
+ let ccw;
13584
+ for (const ring of rings) {
13585
+ const area = calculateSignedArea(ring);
13586
+ if (area === 0) continue;
13587
+ ring.area = Math.abs(area);
13588
+ if (ccw === void 0) ccw = area < 0;
13589
+ if (ccw === area < 0) {
13590
+ if (polygon) polygons.push(polygon);
13591
+ polygon = [ring];
13592
+ } else polygon.push(ring);
13593
+ }
13594
+ if (polygon) polygons.push(polygon);
13595
+ return polygons;
13596
+ }
13597
+ /**
13598
+ * Returns the signed area for the polygon ring. Positive areas are exterior rings and
13599
+ * have a clockwise winding. Negative areas are interior rings and have a counter clockwise
13600
+ * ordering.
13601
+ *
13602
+ * @param ring - Exterior or interior ring
13603
+ * @returns Signed area
13604
+ */
13605
+ function calculateSignedArea(ring) {
13606
+ let sum = 0;
13607
+ for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
13608
+ p1 = ring[i];
13609
+ p2 = ring[j];
13610
+ sum += (p2.x - p1.x) * (p1.y + p2.y);
13441
13611
  }
13442
- };
13612
+ return sum;
13613
+ }
13443
13614
 
13444
13615
  // src/util/cheap_ruler.ts
13445
13616
  const RE = 6378.137;
@@ -13999,162 +14170,6 @@ const expressions = {
13999
14170
  "global-state": GlobalState
14000
14171
  };
14001
14172
 
14002
- // src/expression/parsing_error.ts
14003
- var ExpressionParsingError = class extends Error {
14004
- constructor(key, message) {
14005
- super(message);
14006
- this.message = message;
14007
- this.key = key;
14008
- }
14009
- };
14010
-
14011
- // src/expression/scope.ts
14012
- /**
14013
- * Tracks `let` bindings during expression parsing.
14014
- * @private
14015
- */
14016
- var Scope = class Scope {
14017
- constructor(parent, bindings = []) {
14018
- this.parent = parent;
14019
- this.bindings = {};
14020
- for (const [name, expression] of bindings) this.bindings[name] = expression;
14021
- }
14022
- concat(bindings) {
14023
- return new Scope(this, bindings);
14024
- }
14025
- get(name) {
14026
- if (this.bindings[name]) return this.bindings[name];
14027
- if (this.parent) return this.parent.get(name);
14028
- throw new Error(`${name} not found in scope.`);
14029
- }
14030
- has(name) {
14031
- if (this.bindings[name]) return true;
14032
- return this.parent ? this.parent.has(name) : false;
14033
- }
14034
- };
14035
-
14036
- // src/expression/parsing_context.ts
14037
- /**
14038
- * State associated parsing at a given point in an expression tree.
14039
- * @private
14040
- */
14041
- var ParsingContext = class ParsingContext {
14042
- constructor(registry, isConstantFunc, path = [], expectedType, scope = new Scope(), errors = []) {
14043
- this.registry = registry;
14044
- this.path = path;
14045
- this.key = path.map((part) => `[${part}]`).join("");
14046
- this.scope = scope;
14047
- this.errors = errors;
14048
- this.expectedType = expectedType;
14049
- this._isConstant = isConstantFunc;
14050
- }
14051
- /**
14052
- * @param expr the JSON expression to parse
14053
- * @param index the optional argument index if this expression is an argument of a parent expression that's being parsed
14054
- * @param options
14055
- * @param options.omitTypeAnnotations set true to omit inferred type annotations. Caller beware: with this option set, the parsed expression's type will NOT satisfy `expectedType` if it would normally be wrapped in an inferred annotation.
14056
- * @private
14057
- */
14058
- parse(expr, index, expectedType, bindings, options = {}) {
14059
- if (index != null) return this.concat(index, expectedType, bindings)._parse(expr, options);
14060
- return this._parse(expr, options);
14061
- }
14062
- _parse(expr, options) {
14063
- if (expr === null || typeof expr === "string" || typeof expr === "boolean" || typeof expr === "number") expr = ["literal", expr];
14064
- const key = this.key;
14065
- function annotate(parsed, type, typeAnnotation) {
14066
- if (typeAnnotation === "assert") return new Assertion(type, [parsed], key);
14067
- else if (typeAnnotation === "coerce") return new Coercion(type, [parsed], key);
14068
- else return parsed;
14069
- }
14070
- if (Array.isArray(expr)) {
14071
- if (expr.length === 0) return this.error("Expected an array with at least one element. If you wanted a literal array, use [\"literal\", []].");
14072
- const op = expr[0];
14073
- if (typeof op !== "string") {
14074
- this.error(`Expression name must be a string, but found ${typeof op} instead. If you wanted a literal array, use ["literal", [...]].`, 0);
14075
- return null;
14076
- }
14077
- const Expr = this.registry[op];
14078
- if (Expr) {
14079
- let parsed = Expr.parse(expr, this);
14080
- if (!parsed) return null;
14081
- if (this.expectedType) {
14082
- const expected = this.expectedType;
14083
- const actual = parsed.type;
14084
- if ((expected.kind === "string" || expected.kind === "number" || expected.kind === "boolean" || expected.kind === "object" || expected.kind === "array") && actual.kind === "value") parsed = annotate(parsed, expected, options.typeAnnotation || "assert");
14085
- else if ("projectionDefinition" === expected.kind && [
14086
- "string",
14087
- "array",
14088
- "value"
14089
- ].includes(actual.kind) || [
14090
- "color",
14091
- "formatted",
14092
- "resolvedImage"
14093
- ].includes(expected.kind) && ["value", "string"].includes(actual.kind) || ["padding", "numberArray"].includes(expected.kind) && [
14094
- "value",
14095
- "number",
14096
- "array"
14097
- ].includes(actual.kind) || "colorArray" === expected.kind && [
14098
- "value",
14099
- "string",
14100
- "array"
14101
- ].includes(actual.kind) || "variableAnchorOffsetCollection" === expected.kind && ["value", "array"].includes(actual.kind)) parsed = annotate(parsed, expected, options.typeAnnotation || "coerce");
14102
- else if (this.checkSubtype(expected, actual)) return null;
14103
- }
14104
- if (!(parsed instanceof Literal) && parsed.type.kind !== "resolvedImage" && this._isConstant(parsed)) {
14105
- const ec = new EvaluationContext();
14106
- try {
14107
- parsed = new Literal(parsed.type, parsed.evaluate(ec));
14108
- } catch (e) {
14109
- this.error(e.message);
14110
- return null;
14111
- }
14112
- }
14113
- return parsed;
14114
- }
14115
- return this.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
14116
- } else if (typeof expr === "undefined") return this.error("'undefined' value invalid. Use null instead.");
14117
- else if (typeof expr === "object") return this.error("Bare objects invalid. Use [\"literal\", {...}] instead.");
14118
- else return this.error(`Expected an array, but found ${typeof expr} instead.`);
14119
- }
14120
- /**
14121
- * Returns a copy of this context suitable for parsing the subexpression at
14122
- * index `index`, optionally appending to 'let' binding map.
14123
- *
14124
- * Note that `errors` property, intended for collecting errors while
14125
- * parsing, is copied by reference rather than cloned.
14126
- * @private
14127
- */
14128
- concat(index, expectedType, bindings) {
14129
- const path = typeof index === "number" ? this.path.concat(index) : this.path;
14130
- const scope = bindings ? this.scope.concat(bindings) : this.scope;
14131
- return new ParsingContext(this.registry, this._isConstant, path, expectedType || null, scope, this.errors);
14132
- }
14133
- /**
14134
- * Push a parsing (or type checking) error into the `this.errors`
14135
- * @param error The message
14136
- * @param keys Optionally specify the source of the error at a child
14137
- * of the current expression at `this.key`.
14138
- * @private
14139
- */
14140
- error(error, ...keys) {
14141
- const key = `${this.key}${keys.map((k) => `[${k}]`).join("")}`;
14142
- this.errors.push(new ExpressionParsingError(key, error));
14143
- }
14144
- /**
14145
- * Returns null if `t` is a subtype of `expected`; otherwise returns an
14146
- * error message and also pushes it to `this.errors`.
14147
- * @param expected The expected type
14148
- * @param t The actual type
14149
- * @returns null if `t` is a subtype of `expected`; otherwise returns an error message
14150
- */
14151
- checkSubtype(expected, t) {
14152
- const error = checkSubtype(expected, t);
14153
- if (error) this.error(error);
14154
- return error;
14155
- }
14156
- };
14157
-
14158
14173
  // src/expression/compound_expression.ts
14159
14174
  var CompoundExpression = class CompoundExpression {
14160
14175
  constructor(name, type, evaluate, args, key) {
@@ -14730,6 +14745,20 @@ function isGlobalPropertyConstant(e, properties) {
14730
14745
  return result;
14731
14746
  }
14732
14747
 
14748
+ // src/util/result.ts
14749
+ function success(value) {
14750
+ return {
14751
+ result: "success",
14752
+ value
14753
+ };
14754
+ }
14755
+ function error(value) {
14756
+ return {
14757
+ result: "error",
14758
+ value
14759
+ };
14760
+ }
14761
+
14733
14762
  // src/util/properties.ts
14734
14763
  function supportsPropertyExpression(spec) {
14735
14764
  return spec["property-type"] === "data-driven" || spec["property-type"] === "cross-faded-data-driven";
@@ -14966,20 +14995,6 @@ function interpolationFactor(input, base, lowerValue, upperValue) {
14966
14995
  else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
14967
14996
  }
14968
14997
 
14969
- // src/util/result.ts
14970
- function success(value) {
14971
- return {
14972
- result: "success",
14973
- value
14974
- };
14975
- }
14976
- function error(value) {
14977
- return {
14978
- result: "error",
14979
- value
14980
- };
14981
- }
14982
-
14983
14998
  // src/expression/index.ts
14984
14999
  var StyleExpression = class {
14985
15000
  constructor(expression, rootKey, propertySpec, globalState) {
@@ -15863,6 +15878,174 @@ async function loadGlyphRange(template, fontStack, start, fetchFn) {
15863
15878
  }
15864
15879
  }
15865
15880
 
15881
+ /**
15882
+ * A PNG file of RGBA pixels, for images that are drawn from a data URI, like the sheets of a
15883
+ * sprite. Without compression: its deflate stream holds the pixels in stored blocks, which needs
15884
+ * no dependency and runs in browsers too. The file is a little larger than the pixels.
15885
+ */
15886
+ /** The CRC-32 table of PNG chunks, built on first use. */
15887
+ let crcTable;
15888
+ function crc32(bytes, start, end) {
15889
+ if (!crcTable) {
15890
+ crcTable = new Uint32Array(256);
15891
+ for (let n = 0; n < 256; n++) {
15892
+ let c = n;
15893
+ for (let k = 0; k < 8; k++)
15894
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
15895
+ crcTable[n] = c >>> 0;
15896
+ }
15897
+ }
15898
+ let crc = 0xffffffff;
15899
+ for (let i = start; i < end; i++)
15900
+ crc = crcTable[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8);
15901
+ return (crc ^ 0xffffffff) >>> 0;
15902
+ }
15903
+ /** The checksum of a zlib stream. */
15904
+ function adler32(bytes) {
15905
+ let a = 1;
15906
+ let b = 0;
15907
+ // sums of up to 5552 bytes cannot overflow before the modulo
15908
+ for (let i = 0; i < bytes.length;) {
15909
+ const end = Math.min(i + 5552, bytes.length);
15910
+ for (; i < end; i++) {
15911
+ a += bytes[i];
15912
+ b += a;
15913
+ }
15914
+ a %= 65521;
15915
+ b %= 65521;
15916
+ }
15917
+ return ((b << 16) | a) >>> 0;
15918
+ }
15919
+ /** The longest stored deflate block. */
15920
+ const MAX_BLOCK = 65535;
15921
+ /** A zlib stream of the bytes in stored deflate blocks. */
15922
+ function zlibStored(raw) {
15923
+ const blocks = Math.max(1, Math.ceil(raw.length / MAX_BLOCK));
15924
+ const out = new Uint8Array(2 + blocks * 5 + raw.length + 4);
15925
+ const view = new DataView(out.buffer);
15926
+ // deflate with a 32 KB window, no dictionary, the fastest level
15927
+ out[0] = 0x78;
15928
+ out[1] = 0x01;
15929
+ let at = 2;
15930
+ for (let block = 0; block < blocks; block++) {
15931
+ const start = block * MAX_BLOCK;
15932
+ const length = Math.min(MAX_BLOCK, raw.length - start);
15933
+ out[at] = block === blocks - 1 ? 1 : 0;
15934
+ view.setUint16(at + 1, length, true);
15935
+ view.setUint16(at + 3, ~length & 0xffff, true);
15936
+ out.set(raw.subarray(start, start + length), at + 5);
15937
+ at += 5 + length;
15938
+ }
15939
+ view.setUint32(at, adler32(raw));
15940
+ return out;
15941
+ }
15942
+ /** A chunk of a PNG file: its length, type, data and CRC. */
15943
+ function chunk(type, data) {
15944
+ const out = new Uint8Array(12 + data.length);
15945
+ const view = new DataView(out.buffer);
15946
+ view.setUint32(0, data.length);
15947
+ for (let i = 0; i < 4; i++)
15948
+ out[4 + i] = type.charCodeAt(i);
15949
+ out.set(data, 8);
15950
+ view.setUint32(8 + data.length, crc32(out, 4, 8 + data.length));
15951
+ return out;
15952
+ }
15953
+ /**
15954
+ * A PNG file of an image of `width` × `height` pixels, given as RGBA, row by row, not
15955
+ * premultiplied: as `ImageData` and MapLibre's `map.addImage` have them.
15956
+ * @throws If `data` does not hold `width * height` pixels.
15957
+ */
15958
+ function encodePNG(width, height, data) {
15959
+ if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {
15960
+ throw new Error(`An image must have a positive whole width and height, not ${String(width)} × ${String(height)}`);
15961
+ }
15962
+ if (data.length !== width * height * 4) {
15963
+ throw new Error(`An image of ${String(width)} × ${String(height)} pixels needs ${String(width * height * 4)} bytes, not ${String(data.length)}`);
15964
+ }
15965
+ const header = new Uint8Array(13);
15966
+ const view = new DataView(header.buffer);
15967
+ view.setUint32(0, width);
15968
+ view.setUint32(4, height);
15969
+ // 8 bits per channel, RGBA, no interlace
15970
+ header[8] = 8;
15971
+ header[9] = 6;
15972
+ // each row starts with its filter: none
15973
+ const row = width * 4;
15974
+ const raw = new Uint8Array((row + 1) * height);
15975
+ for (let y = 0; y < height; y++)
15976
+ raw.set(data.subarray(y * row, (y + 1) * row), y * (row + 1) + 1);
15977
+ const parts = [
15978
+ new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]),
15979
+ chunk('IHDR', header),
15980
+ chunk('IDAT', zlibStored(raw)),
15981
+ chunk('IEND', new Uint8Array(0)),
15982
+ ];
15983
+ const png = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));
15984
+ let at = 0;
15985
+ for (const part of parts) {
15986
+ png.set(part, at);
15987
+ at += part.length;
15988
+ }
15989
+ return png;
15990
+ }
15991
+
15992
+ /**
15993
+ * The images in an atlas like the sprite's, each on a sheet of its own. An image that cannot be
15994
+ * drawn, e.g. with too few pixels, is left out with a warning.
15995
+ */
15996
+ function imageAtlas(images, warn) {
15997
+ const atlas = new Map();
15998
+ for (const image of images) {
15999
+ const { id, width, height, data, pixelRatio = 1, sdf = false } = image;
16000
+ if (atlas.has(id)) {
16001
+ warn(`The image "${id}" is given twice in "images": the first one is drawn.`);
16002
+ continue;
16003
+ }
16004
+ if (!(pixelRatio > 0)) {
16005
+ warn(`The image "${id}" in "images" has no positive pixelRatio: it is left out.`);
16006
+ continue;
16007
+ }
16008
+ let png;
16009
+ try {
16010
+ png = encodePNG(width, height, data);
16011
+ }
16012
+ catch (error) {
16013
+ warn(`The image "${id}" in "images" is left out: ${error.message}.`);
16014
+ continue;
16015
+ }
16016
+ const buffer = png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength);
16017
+ atlas.set(id, {
16018
+ width,
16019
+ height,
16020
+ x: 0,
16021
+ y: 0,
16022
+ pixelRatio,
16023
+ sdf,
16024
+ sheetDataUri: `data:image/png;base64,${arrayBufferToBase64(buffer)}`,
16025
+ sheetWidth: width,
16026
+ sheetHeight: height,
16027
+ });
16028
+ }
16029
+ return atlas;
16030
+ }
16031
+ /**
16032
+ * The sprite's atlas with the images added, like `map.addImage` in MapLibre GL JS, which fails for
16033
+ * an id that the style has already: an image of the sprite keeps its place, with a warning.
16034
+ */
16035
+ function withImages(sprite, images, warn) {
16036
+ if (images.size === 0)
16037
+ return sprite;
16038
+ const atlas = new Map(sprite);
16039
+ for (const [id, entry] of images) {
16040
+ if (atlas.has(id)) {
16041
+ warn(`The image "${id}" in "images" is in the sprite too: the sprite's is drawn.`);
16042
+ continue;
16043
+ }
16044
+ atlas.set(id, entry);
16045
+ }
16046
+ return atlas;
16047
+ }
16048
+
15866
16049
  // DEFLATE is a complex format; to read this code, you should probably check the RFC first:
15867
16050
  // https://tools.ietf.org/html/rfc1951
15868
16051
  // You may also wish to take a look at the guide I made about this program:
@@ -21256,6 +21439,9 @@ class SVGMapRenderer {
21256
21439
  #pmtiles;
21257
21440
  #fetch;
21258
21441
  #sprite;
21442
+ /** The images of {@link SVGMapRendererOptions.images}, encoded on first use. */
21443
+ #imageList;
21444
+ #images;
21259
21445
  #sources;
21260
21446
  #glyphRanges = new Map();
21261
21447
  #onWarning;
@@ -21268,6 +21454,7 @@ class SVGMapRenderer {
21268
21454
  constructor(options) {
21269
21455
  this.#style = options.style;
21270
21456
  this.#onWarning = options.onWarning ?? ((message) => console.warn(message));
21457
+ this.#imageList = options.images ?? [];
21271
21458
  let labels =
21272
21459
  // eslint-disable-next-line @typescript-eslint/no-deprecated -- still supported, mapped to `labels`
21273
21460
  options.labels ?? (options.renderLabels === true ? 'glyphs-text' : 'none');
@@ -21395,7 +21582,8 @@ class SVGMapRenderer {
21395
21582
  const sprite = loadSprite(this.#style, this.#fetch).then(({ atlas, complete }) => {
21396
21583
  if (!complete && this.#sprite === sprite)
21397
21584
  this.#sprite = undefined;
21398
- return atlas;
21585
+ this.#images ??= imageAtlas(this.#imageList, (message) => this.#warn(message));
21586
+ return withImages(atlas, this.#images, (message) => this.#warn(message));
21399
21587
  });
21400
21588
  this.#sprite = sprite;
21401
21589
  return sprite;