UNPKG

@maplibre/maplibre-gl-style-spec

Version:
2,101 lines 272 kB
#!/usr/bin/env node import { createRequire } from "node:module"; import { readFileSync } from "node:fs"; //#region \0rolldown/runtime.js var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports); var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))(); //#endregion //#region src/error/validation_error.ts var import_minimist = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => { function hasKey(obj, keys) { var o = obj; keys.slice(0, -1).forEach(function(key) { o = o[key] || {}; }); return keys[keys.length - 1] in o; } function isNumber(x) { if (typeof x === "number") return true; if (/^0x[0-9a-f]+$/i.test(x)) return true; return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); } function isConstructorOrProto(obj, key) { return key === "constructor" && typeof obj[key] === "function" || key === "__proto__"; } module.exports = function(args, opts) { if (!opts) opts = {}; var flags = { bools: {}, strings: {}, unknownFn: null }; if (typeof opts.unknown === "function") flags.unknownFn = opts.unknown; if (typeof opts.boolean === "boolean" && opts.boolean) flags.allBools = true; else [].concat(opts.boolean).filter(Boolean).forEach(function(key) { flags.bools[key] = true; }); var aliases = {}; function aliasIsBoolean(key) { return aliases[key].some(function(x) { return flags.bools[x]; }); } Object.keys(opts.alias || {}).forEach(function(key) { aliases[key] = [].concat(opts.alias[key]); aliases[key].forEach(function(x) { aliases[x] = [key].concat(aliases[key].filter(function(y) { return x !== y; })); }); }); [].concat(opts.string).filter(Boolean).forEach(function(key) { flags.strings[key] = true; if (aliases[key]) [].concat(aliases[key]).forEach(function(k) { flags.strings[k] = true; }); }); var defaults = opts.default || {}; var argv = { _: [] }; function argDefined(key, arg) { return flags.allBools && /^--[^=]+$/.test(arg) || flags.strings[key] || flags.bools[key] || aliases[key]; } function setKey(obj, keys, value) { var o = obj; for (var i = 0; i < keys.length - 1; i++) { var key = keys[i]; if (isConstructorOrProto(o, key)) return; if (o[key] === void 0) o[key] = {}; if (o[key] === Object.prototype || o[key] === Number.prototype || o[key] === String.prototype) o[key] = {}; if (o[key] === Array.prototype) o[key] = []; o = o[key]; } var lastKey = keys[keys.length - 1]; if (isConstructorOrProto(o, lastKey)) return; if (o === Object.prototype || o === Number.prototype || o === String.prototype) o = {}; if (o === Array.prototype) o = []; if (o[lastKey] === void 0 || flags.bools[lastKey] || typeof o[lastKey] === "boolean") o[lastKey] = value; else if (Array.isArray(o[lastKey])) o[lastKey].push(value); else o[lastKey] = [o[lastKey], value]; } function setArg(key, val, arg) { if (arg && flags.unknownFn && !argDefined(key, arg)) { if (flags.unknownFn(arg) === false) return; } var value = !flags.strings[key] && isNumber(val) ? Number(val) : val; setKey(argv, key.split("."), value); (aliases[key] || []).forEach(function(x) { setKey(argv, x.split("."), value); }); } Object.keys(flags.bools).forEach(function(key) { setArg(key, defaults[key] === void 0 ? false : defaults[key]); }); var notFlags = []; if (args.indexOf("--") !== -1) { notFlags = args.slice(args.indexOf("--") + 1); args = args.slice(0, args.indexOf("--")); } for (var i = 0; i < args.length; i++) { var arg = args[i]; var key; var next; if (/^--.+=/.test(arg)) { var m = arg.match(/^--([^=]+)=([\s\S]*)$/); key = m[1]; var value = m[2]; if (flags.bools[key]) value = value !== "false"; setArg(key, value, arg); } else if (/^--no-.+/.test(arg)) { key = arg.match(/^--no-(.+)/)[1]; setArg(key, false, arg); } else if (/^--.+/.test(arg)) { key = arg.match(/^--(.+)/)[1]; next = args[i + 1]; if (next !== void 0 && !/^(-|--)[^-]/.test(next) && !flags.bools[key] && !flags.allBools && (aliases[key] ? !aliasIsBoolean(key) : true)) { setArg(key, next, arg); i += 1; } else if (/^(true|false)$/.test(next)) { setArg(key, next === "true", arg); i += 1; } else setArg(key, flags.strings[key] ? "" : true, arg); } else if (/^-[^-]+/.test(arg)) { var letters = arg.slice(1, -1).split(""); var broken = false; for (var j = 0; j < letters.length; j++) { next = arg.slice(j + 2); if (next === "-") { setArg(letters[j], next, arg); continue; } if (/[A-Za-z]/.test(letters[j]) && next[0] === "=") { setArg(letters[j], next.slice(1), arg); broken = true; break; } if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { setArg(letters[j], next, arg); broken = true; break; } if (letters[j + 1] && letters[j + 1].match(/\W/)) { setArg(letters[j], arg.slice(j + 2), arg); broken = true; break; } else setArg(letters[j], flags.strings[letters[j]] ? "" : true, arg); } key = arg.slice(-1)[0]; if (!broken && key !== "-") if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) && !flags.bools[key] && (aliases[key] ? !aliasIsBoolean(key) : true)) { setArg(key, args[i + 1], arg); i += 1; } else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) { setArg(key, args[i + 1] === "true", arg); i += 1; } else setArg(key, flags.strings[key] ? "" : true, arg); } else { if (!flags.unknownFn || flags.unknownFn(arg) !== false) argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg)); if (opts.stopEarly) { argv._.push.apply(argv._, args.slice(i + 1)); break; } } } Object.keys(defaults).forEach(function(k) { if (!hasKey(argv, k.split("."))) { setKey(argv, k.split("."), defaults[k]); (aliases[k] || []).forEach(function(x) { setKey(argv, x.split("."), defaults[k]); }); } }); if (opts["--"]) argv["--"] = notFlags.slice(); else notFlags.forEach(function(k) { argv._.push(k); }); return argv; }; })))(), 1); var ValidationError = class { constructor(key, value, message, identifier) { this.message = (key ? `${key}: ` : "") + message; if (identifier) this.identifier = identifier; if (value !== null && value !== void 0 && value.__line__) this.line = value.__line__; } }; //#endregion //#region src/validate/validate_constants.ts function validateConstants(options) { const key = options.key; const constants = options.value; if (constants) return [new ValidationError(key, constants, "constants have been deprecated as of v8")]; else return []; } //#endregion //#region src/util/extend.ts function extendBy(output, ...inputs) { for (const input of inputs) for (const k in input) output[k] = input[k]; return output; } //#endregion //#region src/util/unbundle_jsonlint.ts function unbundle(value) { if (value instanceof Number || value instanceof String || value instanceof Boolean) return value.valueOf(); else return value; } function deepUnbundle(value) { if (Array.isArray(value)) return value.map(deepUnbundle); else if (value instanceof Object && !(value instanceof Number || value instanceof String || value instanceof Boolean)) { const unbundledValue = {}; for (const key in value) unbundledValue[key] = deepUnbundle(value[key]); return unbundledValue; } return unbundle(value); } //#endregion //#region src/expression/parsing_error.ts var ExpressionParsingError = class extends Error { constructor(key, message) { super(message); this.message = message; this.key = key; } }; //#endregion //#region src/expression/scope.ts /** * Tracks `let` bindings during expression parsing. * @private */ var Scope = class Scope { constructor(parent, bindings = []) { this.parent = parent; this.bindings = {}; for (const [name, expression] of bindings) this.bindings[name] = expression; } concat(bindings) { return new Scope(this, bindings); } get(name) { if (this.bindings[name]) return this.bindings[name]; if (this.parent) return this.parent.get(name); throw new Error(`${name} not found in scope.`); } has(name) { if (this.bindings[name]) return true; return this.parent ? this.parent.has(name) : false; } }; //#endregion //#region src/expression/types.ts const NullType = { kind: "null" }; const NumberType = { kind: "number" }; const StringType = { kind: "string" }; const BooleanType = { kind: "boolean" }; const ColorType = { kind: "color" }; const ProjectionDefinitionType = { kind: "projectionDefinition" }; const ObjectType = { kind: "object" }; const ValueType = { kind: "value" }; const ErrorType = { kind: "error" }; const CollatorType = { kind: "collator" }; const FormattedType = { kind: "formatted" }; const PaddingType = { kind: "padding" }; const ColorArrayType = { kind: "colorArray" }; const NumberArrayType = { kind: "numberArray" }; const ResolvedImageType = { kind: "resolvedImage" }; const VariableAnchorOffsetCollectionType = { kind: "variableAnchorOffsetCollection" }; function array(itemType, N) { return { kind: "array", itemType, N }; } function typeToString(type) { if (type.kind === "array") { const itemType = typeToString(type.itemType); return typeof type.N === "number" ? `array<${itemType}, ${type.N}>` : type.itemType.kind === "value" ? "array" : `array<${itemType}>`; } else return type.kind; } const valueMemberTypes = [ NullType, NumberType, StringType, BooleanType, ColorType, ProjectionDefinitionType, FormattedType, ObjectType, array(ValueType), PaddingType, NumberArrayType, ColorArrayType, ResolvedImageType, VariableAnchorOffsetCollectionType ]; /** * Returns null if `t` is a subtype of `expected`; otherwise returns an * error message. * @private */ function checkSubtype(expected, t) { if (t.kind === "error") return null; else if (expected.kind === "array") { if (t.kind === "array" && (t.N === 0 && t.itemType.kind === "value" || !checkSubtype(expected.itemType, t.itemType)) && (typeof expected.N !== "number" || expected.N === t.N)) return null; } else if (expected.kind === t.kind) return null; else if (expected.kind === "value") { for (const memberType of valueMemberTypes) if (!checkSubtype(memberType, t)) return null; } return `Expected ${typeToString(expected)} but found ${typeToString(t)} instead.`; } function isValidType(provided, allowedTypes) { return allowedTypes.some((t) => t.kind === provided.kind); } function isValidNativeType(provided, allowedTypes) { return allowedTypes.some((t) => { if (t === "null") return provided === null; else if (t === "array") return Array.isArray(provided); else if (t === "object") return provided && !Array.isArray(provided) && typeof provided === "object"; else return t === typeof provided; }); } /** * Verify whether the specified type is of the same type as the specified sample. * * @param provided Type to verify * @param sample Sample type to reference * @returns `true` if both objects are of the same type, `false` otherwise * @example basic types * if (verifyType(outputType, ValueType)) { * // type narrowed to: * outputType.kind; // 'value' * } * @example array types * if (verifyType(outputType, array(NumberType))) { * // type narrowed to: * outputType.kind; // 'array' * outputType.itemType; // NumberTypeT * outputType.itemType.kind; // 'number' * } */ function verifyType(provided, sample) { if (provided.kind === "array" && sample.kind === "array") return provided.itemType.kind === sample.itemType.kind && typeof provided.N === "number"; return provided.kind === sample.kind; } //#endregion //#region src/expression/types/color_spaces.ts const Xn = .96422, Yn = 1, Zn = .82521, t0 = 4 / 29, t1 = 6 / 29, t2 = 3 * t1 * t1, t3 = t1 * t1 * t1, deg2rad = Math.PI / 180, rad2deg = 180 / Math.PI; function constrainAngle(angle) { angle = angle % 360; if (angle < 0) angle += 360; return angle; } function rgbToLab([r, g, b, alpha]) { r = rgb2xyz(r); g = rgb2xyz(g); b = rgb2xyz(b); let x, z; const y = xyz2lab((.2225045 * r + .7168786 * g + .0606169 * b) / Yn); if (r === g && g === b) x = z = y; else { x = xyz2lab((.4360747 * r + .3850649 * g + .1430804 * b) / Xn); z = xyz2lab((.0139322 * r + .0971045 * g + .7141733 * b) / Zn); } const l = 116 * y - 16; return [ l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z), alpha ]; } function rgb2xyz(x) { return x <= .04045 ? x / 12.92 : Math.pow((x + .055) / 1.055, 2.4); } function xyz2lab(t) { return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0; } function labToRgb([l, a, b, alpha]) { let y = (l + 16) / 116, x = isNaN(a) ? y : y + a / 500, z = isNaN(b) ? y : y - b / 200; y = Yn * lab2xyz(y); x = Xn * lab2xyz(x); z = Zn * lab2xyz(z); return [ xyz2rgb(3.1338561 * x - 1.6168667 * y - .4906146 * z), xyz2rgb(-.9787684 * x + 1.9161415 * y + .033454 * z), xyz2rgb(.0719453 * x - .2289914 * y + 1.4052427 * z), alpha ]; } function xyz2rgb(x) { x = x <= .00304 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - .055; return x < 0 ? 0 : x > 1 ? 1 : x; } function lab2xyz(t) { return t > t1 ? t * t * t : t2 * (t - t0); } function rgbToHcl(rgbColor) { const [l, a, b, alpha] = rgbToLab(rgbColor); const c = Math.sqrt(a * a + b * b); return [ Math.round(c * 1e4) ? constrainAngle(Math.atan2(b, a) * rad2deg) : NaN, c, l, alpha ]; } function hclToRgb([h, c, l, alpha]) { h = isNaN(h) ? 0 : h * deg2rad; return labToRgb([ l, Math.cos(h) * c, Math.sin(h) * c, alpha ]); } function hslToRgb([h, s, l, alpha]) { h = constrainAngle(h); s /= 100; l /= 100; function f(n) { const k = (n + h / 30) % 12; const a = s * Math.min(l, 1 - l); return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1)); } return [ f(0), f(8), f(4), alpha ]; } //#endregion //#region src/util/get_own.ts const hasOwnProperty = Object.hasOwn || function hasOwnProperty(object, key) { return Object.prototype.hasOwnProperty.call(object, key); }; function getOwn(object, key) { return hasOwnProperty(object, key) ? object[key] : void 0; } //#endregion //#region src/expression/types/parse_css_color.ts /** * CSS color parser compliant with CSS Color 4 Specification. * Supports: named colors, `transparent` keyword, all rgb hex notations, * rgb(), rgba(), hsl() and hsla() functions. * Does not round the parsed values to integers from the range 0..255. * * Syntax: * * <alpha-value> = <number> | <percentage> * <hue> = <number> | <angle> * * rgb() = rgb( <percentage>{3} [ / <alpha-value> ]? ) | rgb( <number>{3} [ / <alpha-value> ]? ) * rgb() = rgb( <percentage>#{3} , <alpha-value>? ) | rgb( <number>#{3} , <alpha-value>? ) * * hsl() = hsl( <hue> <percentage> <percentage> [ / <alpha-value> ]? ) * hsl() = hsl( <hue>, <percentage>, <percentage>, <alpha-value>? ) * * Caveats: * - <angle> - <number> with optional `deg` suffix; `grad`, `rad`, `turn` are not supported * - `none` keyword is not supported * - comments inside rgb()/hsl() are not supported * - legacy color syntax rgba() is supported with an identical grammar and behavior to rgb() * - legacy color syntax hsla() is supported with an identical grammar and behavior to hsl() * * @param input CSS color string to parse. * @returns Color in sRGB color space, with `red`, `green`, `blue` * and `alpha` channels normalized to the range 0..1, * or `undefined` if the input is not a valid color string. */ function parseCssColor(input) { input = input.toLowerCase().trim(); if (input === "transparent") return [ 0, 0, 0, 0 ]; const namedColorsMatch = getOwn(namedColors, input); if (namedColorsMatch) { const [r, g, b] = namedColorsMatch; return [ r / 255, g / 255, b / 255, 1 ]; } if (input.startsWith("#")) { if (/^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(input)) { const step = input.length < 6 ? 1 : 2; let i = 1; return [ parseHex(input.slice(i, i += step)), parseHex(input.slice(i, i += step)), parseHex(input.slice(i, i += step)), parseHex(input.slice(i, i + step) || "ff") ]; } } if (input.startsWith("rgb")) { const rgbMatch = input.match(/^rgba?\(\s*([\de.+-]+)(%)?(?:\s+|\s*(,)\s*)([\de.+-]+)(%)?(?:\s+|\s*(,)\s*)([\de.+-]+)(%)?(?:\s*([,\/])\s*([\de.+-]+)(%)?)?\s*\)$/); if (rgbMatch) { const [_, r, rp, f1, g, gp, f2, b, bp, f3, a, ap] = rgbMatch; const argFormat = [ f1 || " ", f2 || " ", f3 ].join(""); if (argFormat === " " || argFormat === " /" || argFormat === ",," || argFormat === ",,,") { const valFormat = [ rp, gp, bp ].join(""); const maxValue = valFormat === "%%%" ? 100 : valFormat === "" ? 255 : 0; if (maxValue) { const rgba = [ clamp(+r / maxValue, 0, 1), clamp(+g / maxValue, 0, 1), clamp(+b / maxValue, 0, 1), a ? parseAlpha(+a, ap) : 1 ]; if (validateNumbers(rgba)) return rgba; } } return; } } const hslMatch = input.match(/^hsla?\(\s*([\de.+-]+)(?:deg)?(?:\s+|\s*(,)\s*)([\de.+-]+)%(?:\s+|\s*(,)\s*)([\de.+-]+)%(?:\s*([,\/])\s*([\de.+-]+)(%)?)?\s*\)$/); if (hslMatch) { const [_, h, f1, s, f2, l, f3, a, ap] = hslMatch; const argFormat = [ f1 || " ", f2 || " ", f3 ].join(""); if (argFormat === " " || argFormat === " /" || argFormat === ",," || argFormat === ",,,") { const hsla = [ +h, clamp(+s, 0, 100), clamp(+l, 0, 100), a ? parseAlpha(+a, ap) : 1 ]; if (validateNumbers(hsla)) return hslToRgb(hsla); } } } function parseHex(hex) { return parseInt(hex.padEnd(2, hex), 16) / 255; } function parseAlpha(a, asPercentage) { return clamp(asPercentage ? a / 100 : a, 0, 1); } function clamp(n, min, max) { return Math.min(Math.max(min, n), max); } /** * The regular expression for numeric values is not super specific, and it may * happen that it will accept a value that is not a valid number. In order to * detect and eliminate such values this function exists. * * @param array Array of uncertain numbers. * @returns `true` if the specified array contains only valid numbers, `false` otherwise. */ function validateNumbers(array) { return !array.some(Number.isNaN); } /** * To generate: * - visit {@link https://www.w3.org/TR/css-color-4/#named-colors} * - run in the console: * @example * copy(`{\n${[...document.querySelector('.named-color-table tbody').children].map((tr) => `${tr.cells[2].textContent.trim()}: [${tr.cells[4].textContent.trim().split(/\s+/).join(', ')}],`).join('\n')}\n}`); */ const namedColors = { aliceblue: [ 240, 248, 255 ], antiquewhite: [ 250, 235, 215 ], aqua: [ 0, 255, 255 ], aquamarine: [ 127, 255, 212 ], azure: [ 240, 255, 255 ], beige: [ 245, 245, 220 ], bisque: [ 255, 228, 196 ], black: [ 0, 0, 0 ], blanchedalmond: [ 255, 235, 205 ], blue: [ 0, 0, 255 ], blueviolet: [ 138, 43, 226 ], brown: [ 165, 42, 42 ], burlywood: [ 222, 184, 135 ], cadetblue: [ 95, 158, 160 ], chartreuse: [ 127, 255, 0 ], chocolate: [ 210, 105, 30 ], coral: [ 255, 127, 80 ], cornflowerblue: [ 100, 149, 237 ], cornsilk: [ 255, 248, 220 ], crimson: [ 220, 20, 60 ], cyan: [ 0, 255, 255 ], darkblue: [ 0, 0, 139 ], darkcyan: [ 0, 139, 139 ], darkgoldenrod: [ 184, 134, 11 ], darkgray: [ 169, 169, 169 ], darkgreen: [ 0, 100, 0 ], darkgrey: [ 169, 169, 169 ], darkkhaki: [ 189, 183, 107 ], darkmagenta: [ 139, 0, 139 ], darkolivegreen: [ 85, 107, 47 ], darkorange: [ 255, 140, 0 ], darkorchid: [ 153, 50, 204 ], darkred: [ 139, 0, 0 ], darksalmon: [ 233, 150, 122 ], darkseagreen: [ 143, 188, 143 ], darkslateblue: [ 72, 61, 139 ], darkslategray: [ 47, 79, 79 ], darkslategrey: [ 47, 79, 79 ], darkturquoise: [ 0, 206, 209 ], darkviolet: [ 148, 0, 211 ], deeppink: [ 255, 20, 147 ], deepskyblue: [ 0, 191, 255 ], dimgray: [ 105, 105, 105 ], dimgrey: [ 105, 105, 105 ], dodgerblue: [ 30, 144, 255 ], firebrick: [ 178, 34, 34 ], floralwhite: [ 255, 250, 240 ], forestgreen: [ 34, 139, 34 ], fuchsia: [ 255, 0, 255 ], gainsboro: [ 220, 220, 220 ], ghostwhite: [ 248, 248, 255 ], gold: [ 255, 215, 0 ], goldenrod: [ 218, 165, 32 ], gray: [ 128, 128, 128 ], green: [ 0, 128, 0 ], greenyellow: [ 173, 255, 47 ], grey: [ 128, 128, 128 ], honeydew: [ 240, 255, 240 ], hotpink: [ 255, 105, 180 ], indianred: [ 205, 92, 92 ], indigo: [ 75, 0, 130 ], ivory: [ 255, 255, 240 ], khaki: [ 240, 230, 140 ], lavender: [ 230, 230, 250 ], lavenderblush: [ 255, 240, 245 ], lawngreen: [ 124, 252, 0 ], lemonchiffon: [ 255, 250, 205 ], lightblue: [ 173, 216, 230 ], lightcoral: [ 240, 128, 128 ], lightcyan: [ 224, 255, 255 ], lightgoldenrodyellow: [ 250, 250, 210 ], lightgray: [ 211, 211, 211 ], lightgreen: [ 144, 238, 144 ], lightgrey: [ 211, 211, 211 ], lightpink: [ 255, 182, 193 ], lightsalmon: [ 255, 160, 122 ], lightseagreen: [ 32, 178, 170 ], lightskyblue: [ 135, 206, 250 ], lightslategray: [ 119, 136, 153 ], lightslategrey: [ 119, 136, 153 ], lightsteelblue: [ 176, 196, 222 ], lightyellow: [ 255, 255, 224 ], lime: [ 0, 255, 0 ], limegreen: [ 50, 205, 50 ], linen: [ 250, 240, 230 ], magenta: [ 255, 0, 255 ], maroon: [ 128, 0, 0 ], mediumaquamarine: [ 102, 205, 170 ], mediumblue: [ 0, 0, 205 ], mediumorchid: [ 186, 85, 211 ], mediumpurple: [ 147, 112, 219 ], mediumseagreen: [ 60, 179, 113 ], mediumslateblue: [ 123, 104, 238 ], mediumspringgreen: [ 0, 250, 154 ], mediumturquoise: [ 72, 209, 204 ], mediumvioletred: [ 199, 21, 133 ], midnightblue: [ 25, 25, 112 ], mintcream: [ 245, 255, 250 ], mistyrose: [ 255, 228, 225 ], moccasin: [ 255, 228, 181 ], navajowhite: [ 255, 222, 173 ], navy: [ 0, 0, 128 ], oldlace: [ 253, 245, 230 ], olive: [ 128, 128, 0 ], olivedrab: [ 107, 142, 35 ], orange: [ 255, 165, 0 ], orangered: [ 255, 69, 0 ], orchid: [ 218, 112, 214 ], palegoldenrod: [ 238, 232, 170 ], palegreen: [ 152, 251, 152 ], paleturquoise: [ 175, 238, 238 ], palevioletred: [ 219, 112, 147 ], papayawhip: [ 255, 239, 213 ], peachpuff: [ 255, 218, 185 ], peru: [ 205, 133, 63 ], pink: [ 255, 192, 203 ], plum: [ 221, 160, 221 ], powderblue: [ 176, 224, 230 ], purple: [ 128, 0, 128 ], rebeccapurple: [ 102, 51, 153 ], red: [ 255, 0, 0 ], rosybrown: [ 188, 143, 143 ], royalblue: [ 65, 105, 225 ], saddlebrown: [ 139, 69, 19 ], salmon: [ 250, 128, 114 ], sandybrown: [ 244, 164, 96 ], seagreen: [ 46, 139, 87 ], seashell: [ 255, 245, 238 ], sienna: [ 160, 82, 45 ], silver: [ 192, 192, 192 ], skyblue: [ 135, 206, 235 ], slateblue: [ 106, 90, 205 ], slategray: [ 112, 128, 144 ], slategrey: [ 112, 128, 144 ], snow: [ 255, 250, 250 ], springgreen: [ 0, 255, 127 ], steelblue: [ 70, 130, 180 ], tan: [ 210, 180, 140 ], teal: [ 0, 128, 128 ], thistle: [ 216, 191, 216 ], tomato: [ 255, 99, 71 ], turquoise: [ 64, 224, 208 ], violet: [ 238, 130, 238 ], wheat: [ 245, 222, 179 ], white: [ 255, 255, 255 ], whitesmoke: [ 245, 245, 245 ], yellow: [ 255, 255, 0 ], yellowgreen: [ 154, 205, 50 ] }; //#endregion //#region src/util/interpolate-primitives.ts function interpolateNumber(from, to, t) { return from + t * (to - from); } function interpolateArray(from, to, t) { return from.map((d, i) => { return interpolateNumber(d, to[i], t); }); } //#endregion //#region src/expression/types/color.ts /** * Color representation used by WebGL. * Defined in sRGB color space and pre-blended with alpha. * @private */ var Color = class Color { /** * @param r Red component premultiplied by `alpha` 0..1 * @param g Green component premultiplied by `alpha` 0..1 * @param b Blue component premultiplied by `alpha` 0..1 * @param [alpha=1] Alpha component 0..1 * @param [premultiplied=true] Whether the `r`, `g` and `b` values have already * been multiplied by alpha. If `true` nothing happens if `false` then they will * be multiplied automatically. */ constructor(r, g, b, alpha = 1, premultiplied = true) { this.r = r; this.g = g; this.b = b; this.a = alpha; if (!premultiplied) { this.r *= alpha; this.g *= alpha; this.b *= alpha; if (!alpha) this.overwriteGetter("rgb", [ r, g, b, alpha ]); } } static { this.black = new Color(0, 0, 0, 1); } static { this.white = new Color(1, 1, 1, 1); } static { this.transparent = new Color(0, 0, 0, 0); } static { this.red = new Color(1, 0, 0, 1); } /** * Parses CSS color strings and converts colors to sRGB color space if needed. * Officially supported color formats: * - keyword, e.g. 'aquamarine' or 'steelblue' * - hex (with 3, 4, 6 or 8 digits), e.g. '#f0f' or '#e9bebea9' * - rgb and rgba, e.g. 'rgb(0,240,120)' or 'rgba(0%,94%,47%,0.1)' or 'rgb(0 240 120 / .3)' * - hsl and hsla, e.g. 'hsl(0,0%,83%)' or 'hsla(0,0%,83%,.5)' or 'hsl(0 0% 83% / 20%)' * * @param input CSS color string to parse. * @returns A `Color` instance, or `undefined` if the input is not a valid color string. */ static parse(input) { if (input instanceof Color) return input; if (typeof input !== "string") return; const rgba = parseCssColor(input); if (rgba) return new Color(...rgba, false); } /** * Used in color interpolation and by 'to-rgba' expression. * * @returns Gien color, with reversed alpha blending, in sRGB color space. */ get rgb() { const { r, g, b, a } = this; const f = a || Infinity; return this.overwriteGetter("rgb", [ r / f, g / f, b / f, a ]); } /** * Used in color interpolation. * * @returns Gien color, with reversed alpha blending, in HCL color space. */ get hcl() { return this.overwriteGetter("hcl", rgbToHcl(this.rgb)); } /** * Used in color interpolation. * * @returns Gien color, with reversed alpha blending, in LAB color space. */ get lab() { return this.overwriteGetter("lab", rgbToLab(this.rgb)); } /** * Lazy getter pattern. When getter is called for the first time lazy value * is calculated and then overwrites getter function in given object instance. * * @example: * const redColor = Color.parse('red'); * let x = redColor.hcl; // this will invoke `get hcl()`, which will calculate * // the value of red in HCL space and invoke this `overwriteGetter` function * // which in turn will set a field with a key 'hcl' in the `redColor` object. * // In other words it will override `get hcl()` from its `Color` prototype * // with its own property: hcl = [calculated red value in hcl]. * let y = redColor.hcl; // next call will no longer invoke getter but simply * // return the previously calculated value * x === y; // true - `x` is exactly the same object as `y` * * @param getterKey Getter key * @param lazyValue Lazily calculated value to be memoized by current instance * @private */ overwriteGetter(getterKey, lazyValue) { Object.defineProperty(this, getterKey, { value: lazyValue }); return lazyValue; } /** * Used by 'to-string' expression. * * @returns Serialized color in format `rgba(r,g,b,a)` * where r,g,b are numbers within 0..255 and alpha is number within 1..0 * * @example * var purple = new Color.parse('purple'); * purple.toString; // = "rgba(128,0,128,1)" * var translucentGreen = new Color.parse('rgba(26, 207, 26, .73)'); * translucentGreen.toString(); // = "rgba(26,207,26,0.73)" */ toString() { const [r, g, b, a] = this.rgb; return `rgba(${[ r, g, b ].map((n) => Math.round(n * 255)).join(",")},${a})`; } static interpolate(from, to, t, spaceKey = "rgb") { switch (spaceKey) { case "rgb": { const [r, g, b, alpha] = interpolateArray(from.rgb, to.rgb, t); return new Color(r, g, b, alpha, false); } case "hcl": { const [hue0, chroma0, light0, alphaF] = from.hcl; const [hue1, chroma1, light1, alphaT] = to.hcl; let hue, chroma; if (!isNaN(hue0) && !isNaN(hue1)) { let dh = hue1 - hue0; if (hue1 > hue0 && dh > 180) dh -= 360; else if (hue1 < hue0 && hue0 - hue1 > 180) dh += 360; hue = hue0 + t * dh; } else if (!isNaN(hue0)) { hue = hue0; if (light1 === 1 || light1 === 0) chroma = chroma0; } else if (!isNaN(hue1)) { hue = hue1; if (light0 === 1 || light0 === 0) chroma = chroma1; } else hue = NaN; const [r, g, b, alpha] = hclToRgb([ hue, chroma ?? interpolateNumber(chroma0, chroma1, t), interpolateNumber(light0, light1, t), interpolateNumber(alphaF, alphaT, t) ]); return new Color(r, g, b, alpha, false); } case "lab": { const [r, g, b, alpha] = labToRgb(interpolateArray(from.lab, to.lab, t)); return new Color(r, g, b, alpha, false); } } } }; //#endregion //#region src/expression/types/collator.ts var Collator = class { constructor(caseSensitive, diacriticSensitive, locale) { if (caseSensitive) this.sensitivity = diacriticSensitive ? "variant" : "case"; else this.sensitivity = diacriticSensitive ? "accent" : "base"; this.locale = locale; this.collator = new Intl.Collator(this.locale ? this.locale : [], { sensitivity: this.sensitivity, usage: "search" }); } compare(lhs, rhs) { return this.collator.compare(lhs, rhs); } resolvedLocale() { return new Intl.Collator(this.locale ? this.locale : []).resolvedOptions().locale; } }; //#endregion //#region src/expression/types/formatted.ts const VERTICAL_ALIGN_OPTIONS = [ "bottom", "center", "top" ]; var FormattedSection = class { constructor(text, image, scale, fontStack, textColor, verticalAlign) { this.text = text; this.image = image; this.scale = scale; this.fontStack = fontStack; this.textColor = textColor; this.verticalAlign = verticalAlign; } }; var Formatted = class Formatted { constructor(sections) { this.sections = sections; } static fromString(unformatted) { return new Formatted([new FormattedSection(unformatted, null, null, null, null, null)]); } isEmpty() { if (this.sections.length === 0) return true; return !this.sections.some((section) => section.text.length !== 0 || section.image && section.image.name.length !== 0); } static factory(text) { if (text instanceof Formatted) return text; else return Formatted.fromString(text); } toString() { if (this.sections.length === 0) return ""; return this.sections.map((section) => section.text).join(""); } }; //#endregion //#region src/expression/types/padding.ts /** * A set of four numbers representing padding around a box. Create instances from * bare arrays or numeric values using the static method `Padding.parse`. * @private */ var Padding = class Padding { constructor(values) { this.values = values.slice(); } /** * Numeric padding values * @param input A padding value * @returns A `Padding` instance, or `undefined` if the input is not a valid padding value. */ static parse(input) { if (input instanceof Padding) return input; if (typeof input === "number") return new Padding([ input, input, input, input ]); if (!Array.isArray(input)) return; if (input.length < 1 || input.length > 4) return; for (const val of input) if (typeof val !== "number") return; switch (input.length) { case 1: input = [ input[0], input[0], input[0], input[0] ]; break; case 2: input = [ input[0], input[1], input[0], input[1] ]; break; case 3: input = [ input[0], input[1], input[2], input[1] ]; break; } return new Padding(input); } toString() { return JSON.stringify(this.values); } static interpolate(from, to, t) { return new Padding(interpolateArray(from.values, to.values, t)); } }; //#endregion //#region src/expression/types/number_array.ts /** * An array of numbers. Create instances from * bare arrays or numeric values using the static method `NumberArray.parse`. * @private */ var NumberArray = class NumberArray { constructor(values) { this.values = values.slice(); } /** * Numeric NumberArray values * @param input A NumberArray value * @returns A `NumberArray` instance, or `undefined` if the input is not a valid NumberArray value. */ static parse(input) { if (input instanceof NumberArray) return input; if (typeof input === "number") return new NumberArray([input]); if (!Array.isArray(input)) return; for (const val of input) if (typeof val !== "number") return; return new NumberArray(input); } toString() { return JSON.stringify(this.values); } static interpolate(from, to, t) { return new NumberArray(interpolateArray(from.values, to.values, t)); } }; //#endregion //#region src/expression/types/color_array.ts /** * An array of colors. Create instances from * bare arrays or strings using the static method `ColorArray.parse`. * @private */ var ColorArray = class ColorArray { constructor(values) { this.values = values.slice(); } /** * ColorArray values * @param input A ColorArray value * @returns A `ColorArray` instance, or `undefined` if the input is not a valid ColorArray value. */ static parse(input) { if (input instanceof ColorArray) return input; if (typeof input === "string") { const parsed_val = Color.parse(input); if (!parsed_val) return; return new ColorArray([parsed_val]); } if (!Array.isArray(input)) return; const colors = []; for (const val of input) { if (typeof val !== "string") return; const parsed_val = Color.parse(val); if (!parsed_val) return; colors.push(parsed_val); } return new ColorArray(colors); } toString() { return JSON.stringify(this.values); } static interpolate(from, to, t, spaceKey = "rgb") { const colors = []; if (from.values.length != to.values.length) throw new Error(`colorArray: Arrays have mismatched length (${from.values.length} vs. ${to.values.length}), cannot interpolate.`); for (let i = 0; i < from.values.length; i++) colors.push(Color.interpolate(from.values[i], to.values[i], t, spaceKey)); return new ColorArray(colors); } }; //#endregion //#region src/expression/runtime_error.ts var RuntimeError = class extends Error { constructor(message) { super(message); this.name = "RuntimeError"; } toJSON() { return this.message; } }; //#endregion //#region src/expression/types/variable_anchor_offset_collection.ts /** Set of valid anchor positions, as a set for validation */ const anchors = /* @__PURE__ */ new Set([ "center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right" ]); /** * Utility class to assist managing values for text-variable-anchor-offset property. Create instances from * bare arrays using the static method `VariableAnchorOffsetCollection.parse`. * @private */ var VariableAnchorOffsetCollection = class VariableAnchorOffsetCollection { constructor(values) { this.values = values.slice(); } static parse(input) { if (input instanceof VariableAnchorOffsetCollection) return input; if (!Array.isArray(input) || input.length < 1 || input.length % 2 !== 0) return; for (let i = 0; i < input.length; i += 2) { const anchorValue = input[i]; const offsetValue = input[i + 1]; if (typeof anchorValue !== "string" || !anchors.has(anchorValue)) return; if (!Array.isArray(offsetValue) || offsetValue.length !== 2 || typeof offsetValue[0] !== "number" || typeof offsetValue[1] !== "number") return; } return new VariableAnchorOffsetCollection(input); } toString() { return JSON.stringify(this.values); } static interpolate(from, to, t) { const fromValues = from.values; const toValues = to.values; if (fromValues.length !== toValues.length) throw new RuntimeError(`Cannot interpolate values of different length. from: ${from.toString()}, to: ${to.toString()}`); const output = []; for (let i = 0; i < fromValues.length; i += 2) { if (fromValues[i] !== toValues[i]) throw new RuntimeError(`Cannot interpolate values containing mismatched anchors. from[${i}]: ${fromValues[i]}, to[${i}]: ${toValues[i]}`); output.push(fromValues[i]); const [fx, fy] = fromValues[i + 1]; const [tx, ty] = toValues[i + 1]; output.push([interpolateNumber(fx, tx, t), interpolateNumber(fy, ty, t)]); } return new VariableAnchorOffsetCollection(output); } }; //#endregion //#region src/expression/types/resolved_image.ts var ResolvedImage = class ResolvedImage { constructor(options) { this.name = options.name; this.available = options.available; } toString() { return this.name; } static fromString(name) { if (!name) return null; return new ResolvedImage({ name, available: false }); } }; //#endregion //#region src/expression/types/projection_definition.ts var ProjectionDefinition = class ProjectionDefinition { constructor(from, to, transition) { this.from = from; this.to = to; this.transition = transition; } static interpolate(from, to, t) { return new ProjectionDefinition(from, to, t); } static parse(input) { if (input instanceof ProjectionDefinition) return input; if (Array.isArray(input) && input.length === 3 && typeof input[0] === "string" && typeof input[1] === "string" && typeof input[2] === "number") return new ProjectionDefinition(input[0], input[1], input[2]); if (typeof input === "object" && typeof input.from === "string" && typeof input.to === "string" && typeof input.transition === "number") return new ProjectionDefinition(input.from, input.to, input.transition); if (typeof input === "string") return new ProjectionDefinition(input, input, 1); } }; //#endregion //#region src/expression/values.ts function validateRGBA(r, g, b, a) { 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" ? [ r, g, b, a ] : [ r, g, b ]).join(", ")}]: 'r', 'g', and 'b' must be between 0 and 255.`; if (!(typeof a === "undefined" || typeof a === "number" && a >= 0 && a <= 1)) return `Invalid rgba value [${[ r, g, b, a ].join(", ")}]: 'a' must be between 0 and 1.`; return null; } function isValue(mixed) { if (mixed === null || typeof mixed === "string" || typeof mixed === "boolean" || typeof mixed === "number" || mixed instanceof ProjectionDefinition || mixed instanceof Color || mixed instanceof Collator || mixed instanceof Formatted || mixed instanceof Padding || mixed instanceof NumberArray || mixed instanceof ColorArray || mixed instanceof VariableAnchorOffsetCollection || mixed instanceof ResolvedImage) return true; else if (Array.isArray(mixed)) { for (const item of mixed) if (!isValue(item)) return false; return true; } else if (typeof mixed === "object") { for (const key in mixed) if (!isValue(mixed[key])) return false; return true; } else return false; } function typeOf(value) { if (value === null) return NullType; else if (typeof value === "string") return StringType; else if (typeof value === "boolean") return BooleanType; else if (typeof value === "number") return NumberType; else if (value instanceof Color) return ColorType; else if (value instanceof ProjectionDefinition) return ProjectionDefinitionType; else if (value instanceof Collator) return CollatorType; else if (value instanceof Formatted) return FormattedType; else if (value instanceof Padding) return PaddingType; else if (value instanceof NumberArray) return NumberArrayType; else if (value instanceof ColorArray) return ColorArrayType; else if (value instanceof VariableAnchorOffsetCollection) return VariableAnchorOffsetCollectionType; else if (value instanceof ResolvedImage) return ResolvedImageType; else if (Array.isArray(value)) { const length = value.length; let itemType; for (const item of value) { const t = typeOf(item); if (!itemType) itemType = t; else if (itemType === t) continue; else { itemType = ValueType; break; } } return array(itemType || ValueType, length); } else return ObjectType; } function valueToString(value) { const type = typeof value; if (value === null) return ""; else if (type === "string" || type === "number" || type === "boolean") return String(value); else if (value instanceof Color || value instanceof ProjectionDefinition || value instanceof Formatted || value instanceof Padding || value instanceof NumberArray || value instanceof ColorArray || value instanceof VariableAnchorOffsetCollection || value instanceof ResolvedImage) return value.toString(); else return JSON.stringify(value); } //#endregion //#region src/expression/definitions/literal.ts var Literal = class Literal { constructor(type, value) { this.type = type; this.value = value; } static parse(args, context) { if (args.length !== 2) return context.error(`'literal' expression requires exactly one argument, but found ${args.length - 1} instead.`); if (!isValue(args[1])) return context.error("invalid value"); const value = args[1]; let type = typeOf(value); const expected = context.expectedType; if (type.kind === "array" && type.N === 0 && expected && expected.kind === "array" && (typeof expected.N !== "number" || expected.N === 0)) type = expected; return new Literal(type, value); } evaluate() { return this.value; } eachChild() {} outputDefined() { return true; } }; //#endregion //#region src/expression/definitions/assertion.ts const types$1 = { string: StringType, number: NumberType, boolean: BooleanType, object: ObjectType }; var Assertion = class Assertion { constructor(type, args) { this.type = type; this.args = args; } static parse(args, context) { if (args.length < 2) return context.error("Expected at least one argument."); let i = 1; let type; const name = args[0]; if (name === "array") { let itemType; if (args.length > 2) { const type = args[1]; 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); itemType = types$1[type]; i++; } else itemType = ValueType; let N; if (args.length > 3) { 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); N = args[2]; i++; } type = array(itemType, N); } else { if (!types$1[name]) throw new Error(`Types doesn't contain name = ${name}`); type = types$1[name]; } const parsed = []; for (; i < args.length; i++) { const input = context.parse(args[i], i, ValueType); if (!input) return null; parsed.push(input); } return new Assertion(type, parsed); } evaluate(ctx) { for (let i = 0; i < this.args.length; i++) { const value = this.args[i].evaluate(ctx); if (!checkSubtype(this.type, typeOf(value))) return value; 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.`); } throw new Error(); } eachChild(fn) { this.args.forEach(fn); } outputDefined() { return this.args.every((arg) => arg.outputDefined()); } }; //#endregion //#region src/expression/definitions/coercion.ts const types = { "to-boolean": BooleanType, "to-color": ColorType, "to-number": NumberType, "to-string": StringType }; /** * Special form for error-coalescing coercion expressions "to-number", * "to-color". Since these coercions can fail at runtime, they accept multiple * arguments, only evaluating one at a time until one succeeds. * * @private */ var Coercion = class Coercion { constructor(type, args) { this.type = type; this.args = args; } static parse(args, context) { if (args.length < 2) return context.error("Expected at least one argument."); const name = args[0]; if (!types[name]) throw new Error(`Can't parse ${name} as it is not part of the known types`); if ((name === "to-boolean" || name === "to-string") && args.length !== 2) return context.error("Expected one argument."); const type = types[name]; const parsed = []; for (let i = 1; i < args.length; i++) { const input = context.parse(args[i], i, ValueType); if (!input) return null; parsed.push(input); } return new Coercion(type, parsed); } evaluate(ctx) { switch (this.type.kind) { case "boolean": return Boolean(this.args[0].evaluate(ctx)); case "color": { let input; let error; for (const arg of this.args) { input = arg.evaluate(ctx); error = null; if (input instanceof Color) return input; else if (typeof input === "string") { const c = ctx.parseColor(input); if (c) return c; } else if (Array.isArray(input)) { if (input.length < 3 || input.length > 4) error = `Invalid rgba value ${JSON.stringify(input)}: expected an array containing either three or four numeric values.`; else error = validateRGBA(input[0], input[1], input[2], input[3]); if (!error) return new Color(input[0] / 255, input[1] / 255, input[2] / 255, input[3]); } } throw new RuntimeError(error || `Could not parse color from value '${typeof input === "string" ? input : JSON.stringify(input)}'`); } case "padding": { let input; for (const arg of this.args) { input = arg.evaluate(ctx); const pad = Padding.parse(input); if (pad) return pad; } throw new RuntimeError(`Could not parse padding from value '${typeof input === "string" ? input : JSON.stringify(input)}'`); } case "numberArray": { let input; for (const arg of this.args) { input = arg.evaluate(ctx); const val = NumberArray.parse(input); if (val) return val; } throw new RuntimeError(`Could not parse numberArray from value '${typeof input === "string" ? input : JSON.stringify(input)}'`); } case "colorArray": { let input; for (const arg of this.args) { input = arg.evaluate(ctx); const val = ColorArray.parse(input); if (val) return val; } throw new RuntimeError(`Could not parse colorArray from value '${typeof input === "string" ? input : JSON.stringify(input)}'`); } case "variableAnchorOffsetCollection": { let input; for (const arg of this.args) { input = arg.evaluate(ctx); const coll = VariableAnchorOffsetCollection.parse(input); if (coll) return coll; } throw new RuntimeError(`Could not parse variableAnchorOffsetCollection from value '${typeof input === "string" ? input : JSON.stringify(input)}'`); } case "number": { let value = null; for (const arg of this.args) { value = arg.evaluate(ctx); if (value === null) return 0; const num = Number(value); if (isNaN(num)) continue; return num; } throw new RuntimeError(`Could not convert ${JSON.stringify(value)} to number.`); } case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx))); case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx))); case "projectionDefinition": return this.args[0].evaluate(ctx); default: return valueToString(this.args[0].evaluate(ctx)); } } eachChild(fn) { this.args.forEach(fn); } outputDefined() { return this.args.every((arg) => arg.outputDefined()); } }; //#endregion //#region src/expression/evaluation_context.ts const geometryTypes = [ "Unknown", "Point", "LineString", "Polygon" ]; var EvaluationContext = class { constructor() { this.globals = null; this.feature = null; this.featureState = null; this.formattedSection = null; this._parseColorCache = /* @__PURE__ */ new Map(); this.availableImages = null; this.canonical = null; } id() { return this.feature && "id" in this.feature ? this.feature.id : null; } geometryType() { return this.feature ? typeof this.feature.type === "number" ? geometryTypes[this.feature.type] : this.feature.type : null; } geometry() { return this.feature && "geometry" in this.feature ? thi