maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
31,837 lines • 1.11 MB
JavaScript
/**
* MapLibre GL JS
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.5.0/LICENSE.txt
*/
//#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 || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
//#region node_modules/@mapbox/point-geometry/index.js
/**
* A standalone point geometry with useful accessor, comparison, and
* modification methods.
*
* @class
* @param {number} x the x-coordinate. This could be longitude or screen pixels, or any other sort of unit.
* @param {number} y the y-coordinate. This could be latitude or screen pixels, or any other sort of unit.
*
* @example
* const point = new Point(-77, 38);
*/
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
/**
* Clone this point, returning a new point that can be modified
* without affecting the old one.
* @return {Point} the clone
*/
clone() {
return new Point(this.x, this.y);
},
/**
* Add this point's x & y coordinates to another point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
add(p) {
return this.clone()._add(p);
},
/**
* Subtract this point's x & y coordinates to from point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
sub(p) {
return this.clone()._sub(p);
},
/**
* Multiply this point's x & y coordinates by point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
multByPoint(p) {
return this.clone()._multByPoint(p);
},
/**
* Divide this point's x & y coordinates by point,
* yielding a new point.
* @param {Point} p the other point
* @return {Point} output point
*/
divByPoint(p) {
return this.clone()._divByPoint(p);
},
/**
* Multiply this point's x & y coordinates by a factor,
* yielding a new point.
* @param {number} k factor
* @return {Point} output point
*/
mult(k) {
return this.clone()._mult(k);
},
/**
* Divide this point's x & y coordinates by a factor,
* yielding a new point.
* @param {number} k factor
* @return {Point} output point
*/
div(k) {
return this.clone()._div(k);
},
/**
* Rotate this point around the 0, 0 origin by an angle a,
* given in radians
* @param {number} a angle to rotate around, in radians
* @return {Point} output point
*/
rotate(a) {
return this.clone()._rotate(a);
},
/**
* Rotate this point around p point by an angle a,
* given in radians
* @param {number} a angle to rotate around, in radians
* @param {Point} p Point to rotate around
* @return {Point} output point
*/
rotateAround(a, p) {
return this.clone()._rotateAround(a, p);
},
/**
* Multiply this point by a 4x1 transformation matrix
* @param {[number, number, number, number]} m transformation matrix
* @return {Point} output point
*/
matMult(m) {
return this.clone()._matMult(m);
},
/**
* Calculate this point but as a unit vector from 0, 0, meaning
* that the distance from the resulting point to the 0, 0
* coordinate will be equal to 1 and the angle from the resulting
* point to the 0, 0 coordinate will be the same as before.
* @return {Point} unit vector point
*/
unit() {
return this.clone()._unit();
},
/**
* Compute a perpendicular point, where the new y coordinate
* is the old x coordinate and the new x coordinate is the old y
* coordinate multiplied by -1
* @return {Point} perpendicular point
*/
perp() {
return this.clone()._perp();
},
/**
* Return a version of this point with the x & y coordinates
* rounded to integers.
* @return {Point} rounded point
*/
round() {
return this.clone()._round();
},
/**
* Return the magnitude of this point: this is the Euclidean
* distance from the 0, 0 coordinate to this point's x and y
* coordinates.
* @return {number} magnitude
*/
mag() {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
/**
* Judge whether this point is equal to another point, returning
* true or false.
* @param {Point} other the other point
* @return {boolean} whether the points are equal
*/
equals(other) {
return this.x === other.x && this.y === other.y;
},
/**
* Calculate the distance from this point to another point
* @param {Point} p the other point
* @return {number} distance
*/
dist(p) {
return Math.sqrt(this.distSqr(p));
},
/**
* Calculate the distance from this point to another point,
* without the square root step. Useful if you're comparing
* relative distances.
* @param {Point} p the other point
* @return {number} distance
*/
distSqr(p) {
const dx = p.x - this.x, dy = p.y - this.y;
return dx * dx + dy * dy;
},
/**
* Get the angle from the 0, 0 coordinate to this point, in radians
* coordinates.
* @return {number} angle
*/
angle() {
return Math.atan2(this.y, this.x);
},
/**
* Get the angle from this point to another point, in radians
* @param {Point} b the other point
* @return {number} angle
*/
angleTo(b) {
return Math.atan2(this.y - b.y, this.x - b.x);
},
/**
* Get the angle between this point and another point, in radians
* @param {Point} b the other point
* @return {number} angle
*/
angleWith(b) {
return this.angleWithSep(b.x, b.y);
},
/**
* Find the angle of the two vectors, solving the formula for
* the cross product a x b = |a||b|sin(θ) for θ.
* @param {number} x the x-coordinate
* @param {number} y the y-coordinate
* @return {number} the angle in radians
*/
angleWithSep(x, y) {
return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
},
/** @param {[number, number, number, number]} m */
_matMult(m) {
const x = m[0] * this.x + m[1] * this.y, y = m[2] * this.x + m[3] * this.y;
this.x = x;
this.y = y;
return this;
},
/** @param {Point} p */
_add(p) {
this.x += p.x;
this.y += p.y;
return this;
},
/** @param {Point} p */
_sub(p) {
this.x -= p.x;
this.y -= p.y;
return this;
},
/** @param {number} k */
_mult(k) {
this.x *= k;
this.y *= k;
return this;
},
/** @param {number} k */
_div(k) {
this.x /= k;
this.y /= k;
return this;
},
/** @param {Point} p */
_multByPoint(p) {
this.x *= p.x;
this.y *= p.y;
return this;
},
/** @param {Point} p */
_divByPoint(p) {
this.x /= p.x;
this.y /= p.y;
return this;
},
_unit() {
this._div(this.mag());
return this;
},
_perp() {
const y = this.y;
this.y = this.x;
this.x = -y;
return this;
},
/** @param {number} angle */
_rotate(angle) {
const cos = Math.cos(angle), sin = Math.sin(angle), x = cos * this.x - sin * this.y, y = sin * this.x + cos * this.y;
this.x = x;
this.y = y;
return this;
},
/**
* @param {number} angle
* @param {Point} p
*/
_rotateAround(angle, p) {
const cos = Math.cos(angle), sin = Math.sin(angle), x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y), y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
this.x = x;
this.y = y;
return this;
},
_round() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
},
constructor: Point
};
/**
* Construct a point from an array if necessary, otherwise if the input
* is already a Point, return it unchanged.
* @param {Point | [number, number] | {x: number, y: number}} p input value
* @return {Point} constructed point.
* @example
* // this
* var point = Point.convert([0, 1]);
* // is equivalent to
* var point = new Point(0, 1);
*/
Point.convert = function(p) {
if (p instanceof Point) return p;
if (Array.isArray(p)) return new Point(+p[0], +p[1]);
if (p.x !== void 0 && p.y !== void 0) return new Point(+p.x, +p.y);
throw new Error("Expected [x, y] or {x, y} point format");
};
//#endregion
//#region node_modules/@mapbox/unitbezier/index.js
function unitBezier$1(p1x, p1y, p2x, p2y) {
const cx = 3 * p1x;
const bx = 3 * (p2x - p1x) - cx;
const ax = 1 - cx - bx;
const cy = 3 * p1y;
const by = 3 * (p2y - p1y) - cy;
const ay = 1 - cy - by;
return function solve(x, epsilon = 1e-6) {
if (x <= 0) return 0;
if (x >= 1) return 1;
let t = x;
for (let i = 0; i < 8; i++) {
const x2 = ((ax * t + bx) * t + cx) * t - x;
if (Math.abs(x2) < epsilon) return ((ay * t + by) * t + cy) * t;
const d2 = (3 * ax * t + 2 * bx) * t + cx;
if (Math.abs(d2) < 1e-6) break;
t -= x2 / d2;
}
let t0 = 0;
let t1 = 1;
t = x;
for (let i = 0; i < 20; i++) {
const x2 = ((ax * t + bx) * t + cx) * t;
if (Math.abs(x2 - x) < epsilon) break;
if (x > x2) t0 = t;
else t1 = t;
t = (t0 + t1) * .5;
}
return ((ay * t + by) * t + cy) * t;
};
}
//#endregion
//#region src/util/offscreen_canvas_supported.ts
let supportsOffscreenCanvas;
function offscreenCanvasSupported() {
supportsOffscreenCanvas ??= typeof OffscreenCanvas !== "undefined" && new OffscreenCanvas(1, 1).getContext("2d") && typeof createImageBitmap === "function";
return supportsOffscreenCanvas;
}
//#endregion
//#region src/util/offscreen_canvas_distorted.ts
let offscreenCanvasDistorted;
/**
* Some browsers don't return the exact pixels from a canvas to prevent user fingerprinting (see #3185).
* This function writes pixels to an OffscreenCanvas and reads them back using getImageData, returning false
* if they don't match.
*
* @returns true if the browser supports OffscreenCanvas but it distorts getImageData results, false otherwise.
*/
function isOffscreenCanvasDistorted() {
if (offscreenCanvasDistorted == null) {
offscreenCanvasDistorted = false;
if (offscreenCanvasSupported()) {
const size = 5;
const context = new OffscreenCanvas(size, size).getContext("2d", { willReadFrequently: true });
if (context) {
for (let i = 0; i < 25; i++) {
const base = i * 4;
context.fillStyle = `rgb(${base},${base + 1},${base + 2})`;
context.fillRect(i % size, Math.floor(i / size), 1, 1);
}
const data = context.getImageData(0, 0, size, size).data;
for (let i = 0; i < 100; i++) if (i % 4 !== 3 && data[i] !== i) {
offscreenCanvasDistorted = true;
break;
}
}
}
}
return offscreenCanvasDistorted || false;
}
var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array;
Math.PI / 180;
180 / Math.PI;
/**
* 2x2 Matrix
* @module mat2
*/
/**
* Creates a new identity mat2
*
* @returns {mat2} a new 2x2 matrix
*/
function create$8() {
var out = new ARRAY_TYPE(4);
if (ARRAY_TYPE != Float32Array) {
out[1] = 0;
out[2] = 0;
}
out[0] = 1;
out[3] = 1;
return out;
}
/**
* Inverts a mat2
*
* @param {mat2} out the receiving matrix
* @param {ReadonlyMat2} a the source matrix
* @returns {mat2 | null} out, or null if source matrix is not invertible
*/
function invert$5(out, a) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
var det = a0 * a3 - a2 * a1;
if (!det) return null;
det = 1 / det;
out[0] = a3 * det;
out[1] = -a1 * det;
out[2] = -a2 * det;
out[3] = a0 * det;
return out;
}
/**
* Calculates the determinant of a mat2
*
* @param {ReadonlyMat2} a the source matrix
* @returns {Number} determinant of a
*/
function determinant$3(a) {
return a[0] * a[3] - a[2] * a[1];
}
/**
* Rotates a mat2 by the given angle
*
* @param {mat2} out the receiving matrix
* @param {ReadonlyMat2} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat2} out
*/
function rotate$4(out, a, rad) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
var s = Math.sin(rad);
var c = Math.cos(rad);
out[0] = a0 * c + a2 * s;
out[1] = a1 * c + a3 * s;
out[2] = a0 * -s + a2 * c;
out[3] = a1 * -s + a3 * c;
return out;
}
/**
* 3x3 Matrix
* @module mat3
*/
/**
* Creates a new identity mat3
*
* @returns {mat3} a new 3x3 matrix
*/
function create$6() {
var out = new ARRAY_TYPE(9);
if (ARRAY_TYPE != Float32Array) {
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[5] = 0;
out[6] = 0;
out[7] = 0;
}
out[0] = 1;
out[4] = 1;
out[8] = 1;
return out;
}
/**
* Calculates the determinant of a mat3
*
* @param {ReadonlyMat3} a the source matrix
* @returns {Number} determinant of a
*/
function determinant$1(a) {
var a00 = a[0], a01 = a[1], a02 = a[2];
var a10 = a[3], a11 = a[4], a12 = a[5];
var a20 = a[6], a21 = a[7], a22 = a[8];
return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);
}
/**
* Creates a matrix from a given angle
* This is equivalent to (but much faster than):
*
* mat3.identity(dest);
* mat3.rotate(dest, dest, rad);
*
* @param {mat3} out mat3 receiving operation result
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat3} out
*/
function fromRotation$2(out, rad) {
var s = Math.sin(rad), c = Math.cos(rad);
out[0] = c;
out[1] = s;
out[2] = 0;
out[3] = -s;
out[4] = c;
out[5] = 0;
out[6] = 0;
out[7] = 0;
out[8] = 1;
return out;
}
/**
* Calculates a 3x3 matrix from the given quaternion
*
* @param {mat3} out mat3 receiving operation result
* @param {ReadonlyQuat} q Quaternion to create matrix from
*
* @returns {mat3} out
*/
function fromQuat$1(out, q) {
var x = q[0], y = q[1], z = q[2], w = q[3];
var x2 = x + x;
var y2 = y + y;
var z2 = z + z;
var xx = x * x2;
var yx = y * x2;
var yy = y * y2;
var zx = z * x2;
var zy = z * y2;
var zz = z * z2;
var wx = w * x2;
var wy = w * y2;
var wz = w * z2;
out[0] = 1 - yy - zz;
out[3] = yx - wz;
out[6] = zx + wy;
out[1] = yx + wz;
out[4] = 1 - xx - zz;
out[7] = zy - wx;
out[2] = zx - wy;
out[5] = zy + wx;
out[8] = 1 - xx - yy;
return out;
}
/**
* 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.
* @module mat4
*/
/**
* Creates a new identity mat4
*
* @returns {mat4} a new 4x4 matrix
*/
function create$5() {
var out = new ARRAY_TYPE(16);
if (ARRAY_TYPE != Float32Array) {
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
}
out[0] = 1;
out[5] = 1;
out[10] = 1;
out[15] = 1;
return out;
}
/**
* Creates a new mat4 initialized with values from an existing matrix
*
* @param {ReadonlyMat4} a matrix to clone
* @returns {mat4} a new 4x4 matrix
*/
function clone$6(a) {
var out = new ARRAY_TYPE(16);
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return out;
}
/**
* Copy the values from one mat4 to another
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the source matrix
* @returns {mat4} out
*/
function copy$5(out, a) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return out;
}
/**
* Set a mat4 to the identity matrix
*
* @param {mat4} out the receiving matrix
* @returns {mat4} out
*/
function identity$2(out) {
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = 1;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 1;
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
out[15] = 1;
return out;
}
/**
* Inverts a mat4
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the source matrix
* @returns {mat4 | null} out, or null if source matrix is not invertible
*/
function invert$2(out, a) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
var b00 = a00 * a11 - a01 * a10;
var b01 = a00 * a12 - a02 * a10;
var b02 = a00 * a13 - a03 * a10;
var b03 = a01 * a12 - a02 * a11;
var b04 = a01 * a13 - a03 * a11;
var b05 = a02 * a13 - a03 * a12;
var b06 = a20 * a31 - a21 * a30;
var b07 = a20 * a32 - a22 * a30;
var b08 = a20 * a33 - a23 * a30;
var b09 = a21 * a32 - a22 * a31;
var b10 = a21 * a33 - a23 * a31;
var b11 = a22 * a33 - a23 * a32;
var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det) return null;
det = 1 / det;
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
return out;
}
/**
* Multiplies two mat4s
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the first operand
* @param {ReadonlyMat4} b the second operand
* @returns {mat4} out
*/
function multiply$5(out, a, b) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
b0 = b[4];
b1 = b[5];
b2 = b[6];
b3 = b[7];
out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
b0 = b[8];
b1 = b[9];
b2 = b[10];
b3 = b[11];
out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
b0 = b[12];
b1 = b[13];
b2 = b[14];
b3 = b[15];
out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
return out;
}
/**
* Translate a mat4 by the given vector
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the matrix to translate
* @param {ReadonlyVec3} v vector to translate by
* @returns {mat4} out
*/
function translate$2(out, a, v) {
var x = v[0], y = v[1], z = v[2];
var a00, a01, a02, a03;
var a10, a11, a12, a13;
var a20, a21, a22, a23;
if (a === out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
} else {
a00 = a[0];
a01 = a[1];
a02 = a[2];
a03 = a[3];
a10 = a[4];
a11 = a[5];
a12 = a[6];
a13 = a[7];
a20 = a[8];
a21 = a[9];
a22 = a[10];
a23 = a[11];
out[0] = a00;
out[1] = a01;
out[2] = a02;
out[3] = a03;
out[4] = a10;
out[5] = a11;
out[6] = a12;
out[7] = a13;
out[8] = a20;
out[9] = a21;
out[10] = a22;
out[11] = a23;
out[12] = a00 * x + a10 * y + a20 * z + a[12];
out[13] = a01 * x + a11 * y + a21 * z + a[13];
out[14] = a02 * x + a12 * y + a22 * z + a[14];
out[15] = a03 * x + a13 * y + a23 * z + a[15];
}
return out;
}
/**
* Scales the mat4 by the dimensions in the given vec3 not using vectorization
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the matrix to scale
* @param {ReadonlyVec3} v the vec3 to scale the matrix by
* @returns {mat4} out
**/
function scale$5(out, a, v) {
var x = v[0], y = v[1], z = v[2];
out[0] = a[0] * x;
out[1] = a[1] * x;
out[2] = a[2] * x;
out[3] = a[3] * x;
out[4] = a[4] * y;
out[5] = a[5] * y;
out[6] = a[6] * y;
out[7] = a[7] * y;
out[8] = a[8] * z;
out[9] = a[9] * z;
out[10] = a[10] * z;
out[11] = a[11] * z;
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return out;
}
/**
* Rotates a matrix by the given angle around the X axis
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat4} out
*/
function rotateX$3(out, a, rad) {
var s = Math.sin(rad);
var c = Math.cos(rad);
var a10 = a[4];
var a11 = a[5];
var a12 = a[6];
var a13 = a[7];
var a20 = a[8];
var a21 = a[9];
var a22 = a[10];
var a23 = a[11];
if (a !== out) {
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
out[4] = a10 * c + a20 * s;
out[5] = a11 * c + a21 * s;
out[6] = a12 * c + a22 * s;
out[7] = a13 * c + a23 * s;
out[8] = a20 * c - a10 * s;
out[9] = a21 * c - a11 * s;
out[10] = a22 * c - a12 * s;
out[11] = a23 * c - a13 * s;
return out;
}
/**
* Rotates a matrix by the given angle around the Y axis
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat4} out
*/
function rotateY$3(out, a, rad) {
var s = Math.sin(rad);
var c = Math.cos(rad);
var a00 = a[0];
var a01 = a[1];
var a02 = a[2];
var a03 = a[3];
var a20 = a[8];
var a21 = a[9];
var a22 = a[10];
var a23 = a[11];
if (a !== out) {
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
out[0] = a00 * c - a20 * s;
out[1] = a01 * c - a21 * s;
out[2] = a02 * c - a22 * s;
out[3] = a03 * c - a23 * s;
out[8] = a00 * s + a20 * c;
out[9] = a01 * s + a21 * c;
out[10] = a02 * s + a22 * c;
out[11] = a03 * s + a23 * c;
return out;
}
/**
* Rotates a matrix by the given angle around the Z axis
*
* @param {mat4} out the receiving matrix
* @param {ReadonlyMat4} a the matrix to rotate
* @param {Number} rad the angle to rotate the matrix by
* @returns {mat4} out
*/
function rotateZ$3(out, a, rad) {
var s = Math.sin(rad);
var c = Math.cos(rad);
var a00 = a[0];
var a01 = a[1];
var a02 = a[2];
var a03 = a[3];
var a10 = a[4];
var a11 = a[5];
var a12 = a[6];
var a13 = a[7];
if (a !== out) {
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
out[0] = a00 * c + a10 * s;
out[1] = a01 * c + a11 * s;
out[2] = a02 * c + a12 * s;
out[3] = a03 * c + a13 * s;
out[4] = a10 * c - a00 * s;
out[5] = a11 * c - a01 * s;
out[6] = a12 * c - a02 * s;
out[7] = a13 * c - a03 * s;
return out;
}
/**
* Creates a matrix from a vector scaling
* This is equivalent to (but much faster than):
*
* mat4.identity(dest);
* mat4.scale(dest, dest, vec);
*
* @param {mat4} out mat4 receiving operation result
* @param {ReadonlyVec3} v Scaling vector
* @returns {mat4} out
*/
function fromScaling(out, v) {
out[0] = v[0];
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = v[1];
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = v[2];
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
out[15] = 1;
return out;
}
/**
* Generates a perspective projection matrix with the given bounds.
* The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
* which matches WebGL/OpenGL's clip volume.
* Passing null/undefined/no value for far will generate infinite projection matrix.
*
* @param {mat4} out mat4 frustum matrix will be written into
* @param {number} fovy Vertical field of view in radians
* @param {number} aspect Aspect ratio. typically viewport width/height
* @param {number} near Near bound of the frustum
* @param {number} far Far bound of the frustum, can be null or Infinity
* @returns {mat4} out
*/
function perspectiveNO(out, fovy, aspect, near, far) {
var f = 1 / Math.tan(fovy / 2);
out[0] = f / aspect;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = f;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[11] = -1;
out[12] = 0;
out[13] = 0;
out[15] = 0;
if (far != null && far !== Infinity) {
var nf = 1 / (near - far);
out[10] = (far + near) * nf;
out[14] = 2 * far * near * nf;
} else {
out[10] = -1;
out[14] = -2 * near;
}
return out;
}
/**
* Alias for {@link mat4.perspectiveNO}
* @function
*/
var perspective = perspectiveNO;
/**
* Generates a orthogonal projection matrix with the given bounds.
* The near/far clip planes correspond to a normalized device coordinate Z range of [-1, 1],
* which matches WebGL/OpenGL's clip volume.
*
* @param {mat4} out mat4 frustum matrix will be written into
* @param {number} left Left bound of the frustum
* @param {number} right Right bound of the frustum
* @param {number} bottom Bottom bound of the frustum
* @param {number} top Top bound of the frustum
* @param {number} near Near bound of the frustum
* @param {number} far Far bound of the frustum
* @returns {mat4} out
*/
function orthoNO(out, left, right, bottom, top, near, far) {
var lr = 1 / (left - right);
var bt = 1 / (bottom - top);
var nf = 1 / (near - far);
out[0] = -2 * lr;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = -2 * bt;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 2 * nf;
out[11] = 0;
out[12] = (left + right) * lr;
out[13] = (top + bottom) * bt;
out[14] = (far + near) * nf;
out[15] = 1;
return out;
}
/**
* Alias for {@link mat4.orthoNO}
* @function
*/
var ortho = orthoNO;
/**
* Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
*
* @param {ReadonlyMat4} a The first matrix.
* @param {ReadonlyMat4} b The second matrix.
* @returns {Boolean} True if the matrices are equal, false otherwise.
*/
function exactEquals$5(a, b) {
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] && a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15];
}
/**
* Returns whether or not the matrices have approximately the same elements in the same position.
*
* @param {ReadonlyMat4} a The first matrix.
* @param {ReadonlyMat4} b The second matrix.
* @returns {Boolean} True if the matrices are equal, false otherwise.
*/
function equals$6(a, b) {
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
var a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7];
var a8 = a[8], a9 = a[9], a10 = a[10], a11 = a[11];
var a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15];
var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
var b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7];
var b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11];
var b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
return Math.abs(a0 - b0) <= 1e-6 * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= 1e-6 * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= 1e-6 * Math.max(1, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= 1e-6 * Math.max(1, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= 1e-6 * Math.max(1, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= 1e-6 * Math.max(1, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= 1e-6 * Math.max(1, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= 1e-6 * Math.max(1, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= 1e-6 * Math.max(1, Math.abs(a8), Math.abs(b8)) && Math.abs(a9 - b9) <= 1e-6 * Math.max(1, Math.abs(a9), Math.abs(b9)) && Math.abs(a10 - b10) <= 1e-6 * Math.max(1, Math.abs(a10), Math.abs(b10)) && Math.abs(a11 - b11) <= 1e-6 * Math.max(1, Math.abs(a11), Math.abs(b11)) && Math.abs(a12 - b12) <= 1e-6 * Math.max(1, Math.abs(a12), Math.abs(b12)) && Math.abs(a13 - b13) <= 1e-6 * Math.max(1, Math.abs(a13), Math.abs(b13)) && Math.abs(a14 - b14) <= 1e-6 * Math.max(1, Math.abs(a14), Math.abs(b14)) && Math.abs(a15 - b15) <= 1e-6 * Math.max(1, Math.abs(a15), Math.abs(b15));
}
/**
* 3 Dimensional Vector
* @module vec3
*/
/**
* Creates a new, empty vec3
*
* @returns {vec3} a new 3D vector
*/
function create$4() {
var out = new ARRAY_TYPE(3);
if (ARRAY_TYPE != Float32Array) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
}
return out;
}
/**
* Creates a new vec3 initialized with values from an existing vector
*
* @param {ReadonlyVec3} a vector to clone
* @returns {vec3} a new 3D vector
*/
function clone$5(a) {
var out = new ARRAY_TYPE(3);
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
return out;
}
/**
* Calculates the length of a vec3
*
* @param {ReadonlyVec3} a vector to calculate length of
* @returns {Number} length of a
*/
function length$4(a) {
var x = a[0];
var y = a[1];
var z = a[2];
return Math.sqrt(x * x + y * y + z * z);
}
/**
* Creates a new vec3 initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @returns {vec3} a new 3D vector
*/
function fromValues$4(x, y, z) {
var out = new ARRAY_TYPE(3);
out[0] = x;
out[1] = y;
out[2] = z;
return out;
}
/**
* Adds two vec3's
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the first operand
* @param {ReadonlyVec3} b the second operand
* @returns {vec3} out
*/
function add$4(out, a, b) {
out[0] = a[0] + b[0];
out[1] = a[1] + b[1];
out[2] = a[2] + b[2];
return out;
}
/**
* Subtracts vector b from vector a
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the first operand
* @param {ReadonlyVec3} b the second operand
* @returns {vec3} out
*/
function subtract$2(out, a, b) {
out[0] = a[0] - b[0];
out[1] = a[1] - b[1];
out[2] = a[2] - b[2];
return out;
}
/**
* Scales a vec3 by a scalar number
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the vector to scale
* @param {Number} b amount to scale the vector by
* @returns {vec3} out
*/
function scale$4(out, a, b) {
out[0] = a[0] * b;
out[1] = a[1] * b;
out[2] = a[2] * b;
return out;
}
/**
* Adds two vec3's after scaling the second operand by a scalar value
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the first operand
* @param {ReadonlyVec3} b the second operand
* @param {Number} scale the amount to scale b by before adding
* @returns {vec3} out
*/
function scaleAndAdd$2(out, a, b, scale) {
out[0] = a[0] + b[0] * scale;
out[1] = a[1] + b[1] * scale;
out[2] = a[2] + b[2] * scale;
return out;
}
/**
* Negates the components of a vec3
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a vector to negate
* @returns {vec3} out
*/
function negate$2(out, a) {
out[0] = -a[0];
out[1] = -a[1];
out[2] = -a[2];
return out;
}
/**
* Normalize a vec3
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a vector to normalize
* @returns {vec3} out
*/
function normalize$4(out, a) {
var x = a[0];
var y = a[1];
var z = a[2];
var len = x * x + y * y + z * z;
if (len > 0) len = 1 / Math.sqrt(len);
out[0] = a[0] * len;
out[1] = a[1] * len;
out[2] = a[2] * len;
return out;
}
/**
* Calculates the dot product of two vec3's
*
* @param {ReadonlyVec3} a the first operand
* @param {ReadonlyVec3} b the second operand
* @returns {Number} dot product of a and b
*/
function dot$5(a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
/**
* Computes the cross product of two vec3's
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the first operand
* @param {ReadonlyVec3} b the second operand
* @returns {vec3} out
*/
function cross$2(out, a, b) {
var ax = a[0], ay = a[1], az = a[2];
var bx = b[0], by = b[1], bz = b[2];
out[0] = ay * bz - az * by;
out[1] = az * bx - ax * bz;
out[2] = ax * by - ay * bx;
return out;
}
/**
* Transforms the vec3 with a mat4.
* 4th vector component is implicitly '1'
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the vector to transform
* @param {ReadonlyMat4} m matrix to transform with
* @returns {vec3} out
*/
function transformMat4$2(out, a, m) {
var x = a[0], y = a[1], z = a[2];
var w = m[3] * x + m[7] * y + m[11] * z + m[15];
w = w || 1;
out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
return out;
}
/**
* Transforms the vec3 with a mat3.
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the vector to transform
* @param {ReadonlyMat3} m the 3x3 matrix to transform with
* @returns {vec3} out
*/
function transformMat3$1(out, a, m) {
var x = a[0], y = a[1], z = a[2];
out[0] = x * m[0] + y * m[3] + z * m[6];
out[1] = x * m[1] + y * m[4] + z * m[7];
out[2] = x * m[2] + y * m[5] + z * m[8];
return out;
}
/**
* Transforms the vec3 with a quat
* Can also be used for dual quaternions. (Multiply it with the real part)
*
* @param {vec3} out the receiving vector
* @param {ReadonlyVec3} a the vector to transform
* @param {ReadonlyQuat} q normalized quaternion to transform with
* @returns {vec3} out
*/
function transformQuat$1(out, a, q) {
var qx = q[0], qy = q[1], qz = q[2], qw = q[3];
var vx = a[0], vy = a[1], vz = a[2];
var tx = qy * vz - qz * vy;
var ty = qz * vx - qx * vz;
var tz = qx * vy - qy * vx;
tx = tx + tx;
ty = ty + ty;
tz = tz + tz;
out[0] = vx + qw * tx + qy * tz - qz * ty;
out[1] = vy + qw * ty + qz * tx - qx * tz;
out[2] = vz + qw * tz + qx * ty - qy * tx;
return out;
}
/**
* Rotate a 3D vector around the x-axis
* @param {vec3} out The receiving vec3
* @param {ReadonlyVec3} a The vec3 point to rotate
* @param {ReadonlyVec3} b The origin of the rotation
* @param {Number} rad The angle of rotation in radians
* @returns {vec3} out
*/
function rotateX$2(out, a, b, rad) {
var p = [], r = [];
p[0] = a[0] - b[0];
p[1] = a[1] - b[1];
p[2] = a[2] - b[2];
r[0] = p[0];
r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad);
r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad);
out[0] = r[0] + b[0];
out[1] = r[1] + b[1];
out[2] = r[2] + b[2];
return out;
}
/**
* Rotate a 3D vector around the y-axis
* @param {vec3} out The receiving vec3
* @param {ReadonlyVec3} a The vec3 point to rotate
* @param {ReadonlyVec3} b The origin of the rotation
* @param {Number} rad The angle of rotation in radians
* @returns {vec3} out
*/
function rotateY$2(out, a, b, rad) {
var p = [], r = [];
p[0] = a[0] - b[0];
p[1] = a[1] - b[1];
p[2] = a[2] - b[2];
r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad);
r[1] = p[1];
r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad);
out[0] = r[0] + b[0];
out[1] = r[1] + b[1];
out[2] = r[2] + b[2];
return out;
}
/**
* Rotate a 3D vector around the z-axis
* @param {vec3} out The receiving vec3
* @param {ReadonlyVec3} a The vec3 point to rotate
* @param {ReadonlyVec3} b The origin of the rotation
* @param {Number} rad The angle of rotation in radians
* @returns {vec3} out
*/
function rotateZ$2(out, a, b, rad) {
var p = [], r = [];
p[0] = a[0] - b[0];
p[1] = a[1] - b[1];
p[2] = a[2] - b[2];
r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad);
r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad);
r[2] = p[2];
out[0] = r[0] + b[0];
out[1] = r[1] + b[1];
out[2] = r[2] + b[2];
return out;
}
/**
* Set the components of a vec3 to zero
*
* @param {vec3} out the receiving vector
* @returns {vec3} out
*/
function zero$2(out) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
return out;
}
/**
* Alias for {@link vec3.subtract}
* @function
*/
var sub$2 = subtract$2;
/**
* Alias for {@link vec3.length}
* @function
*/
var len$4 = length$4;
(function() {
var vec = create$4();
return function(a, stride, offset, count, fn, arg) {
var i, l;
if (!stride) stride = 3;
if (!offset) offset = 0;
if (count) l = Math.min(count * stride + offset, a.length);
else l = a.length;
for (i = offset; i < l; i += stride) {
vec[0] = a[i];
vec[1] = a[i + 1];
vec[2] = a[i + 2];
fn(vec, vec, arg);
a[i] = vec[0];
a[i + 1] = vec[1];
a[i + 2] = vec[2];
}
return a;
};
})();
/**
* 4 Dimensional Vector
* @module vec4
*/
/**
* Creates a new, empty vec4
*
* @returns {vec4} a new 4D vector
*/
function create$3() {
var out = new ARRAY_TYPE(4);
if (ARRAY_TYPE != Float32Array) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
out[3] = 0;
}
return out;
}
/**
* Creates a new vec4 initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @param {Number} w W component
* @returns {vec4} a new 4D vector
*/
function fromValues$3(x, y, z, w) {
var out = new ARRAY_TYPE(4);
out[0] = x;
out[1] = y;
out[2] = z;
out[3] = w;
return out;
}
/**
* Multiplies two vec4's
*
* @param {vec4} out the receiving vector
* @param {ReadonlyVec4} a the first operand
* @param {ReadonlyVec4} b the second operand
* @returns {vec4} out
*/
function multiply$3(out, a, b) {
out[0] = a[0] * b[0];
out[1] = a[1] * b[1];
out[2] = a[2] * b[2];
out[3] = a[3] * b[3];
return out;
}
/**
* Scales a vec4 by a scalar number
*
* @param {vec4} out the receiving vector
* @param {ReadonlyVec4} a the vector to scale
* @param {Number} b amount to scale the vector by
* @returns {vec4} out
*/
function scale$3(out, a, b) {
out[0] = a[0] * b;
out[1] = a[1] * b;
out[2] = a[2] * b;
out[3] = a[3] * b;
return out;
}
/**
* Normalize a vec4
*
* @param {vec4} out the receiving vector
* @param {ReadonlyVec4} a vector to normalize
* @returns {vec4} out
*/
function normalize$3(out, a) {
var x = a[0];
var y = a[1];
var z = a[2];
var w = a[3];
var len = x * x + y * y + z * z + w * w;
if (len > 0) len = 1 / Math.sqrt(len);
out[0] = x * len;
out[1] = y * len;
out[2] = z * len;
out[3] = w * len;
return out;
}
/**
* Transforms the vec4 with a mat4.
*
* @param {vec4} out the receiving vector
* @param {ReadonlyVec4} a the vector to transform
* @param {ReadonlyMat4} m matrix to transform with
* @returns {vec4} out
*/
function transformMat4$1(out, a, m) {
var x = a[0], y = a[1], z = a[2], w = a[3];
out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
return out;
}
/**
* Alias for {@link vec4.multiply}
* @function
*/
var mul$3 = multiply$3;
(function() {
var vec = create$3();
return function(a, stride, offset, count, fn, arg) {
var i, l;
if (!stride) stride = 4;
if (!offset) offset = 0;
if (count) l = Math.min(count * stride + offset, a.length);
else l = a.length;
for (i = offset; i < l; i += stride) {
vec[0] = a[i];
vec[1] = a[i + 1];
vec[2] = a[i + 2];
vec[3] = a[i + 3];
fn(vec, vec, arg);
a[i] = vec[0];
a[i + 1] = vec[1];
a[i + 2] = vec[2];
a[i + 3] = vec[3];
}
return a;
};
})();
/**
* Quaternion in the format XYZW
* @module quat
*/
/**
* Creates a new identity quat
*
* @returns {quat} a new quaternion
*/
function create$2() {
var out = new ARRAY_TYPE(4);
if (ARRAY_TYPE != Float32Array) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
}
out[3] = 1;
return out;
}
/**
* Sets a quat from the given angle and rotation axis,
* then returns it.
*
* @param {quat} out the receiving quaternion
* @param {ReadonlyVec3} axis the axis around which to rotate
* @param {Number} rad the angle in radians
* @returns {quat} out
**/
function setAxisAngle(out, axis, rad) {
rad = rad * .5;
var s = Math.sin(rad);
out[0] = s * axis[0];
out[1] = s * axis[1];
out[2] = s * axis[2];
out[3] = Math.cos(rad);
return out;
}
/**
* Multiplies two quat's
*
* @param {quat} out the receiving quaternion
* @param {ReadonlyQuat} a the first operand
* @param {ReadonlyQuat} b the second operand
* @returns {quat} out
*/
function multiply$2(out, a, b) {
var ax = a[0], ay = a[1], az = a[2], aw = a[3];
var bx = b[0], by = b[1], bz = b[2], bw = b[3];
out[0] = ax * bw + aw * bx + ay * bz - az * by;
out[1] = ay * bw + aw * by + az * bx - ax * bz;
out[2] = az * bw + aw * bz + ax * by - ay * bx;
out[3] = aw * bw - ax * bx - ay * by - az * bz;
return out;
}
/**
* Performs a spherical linear interpolation between two quat
*
* @param {quat} out the receiving quaternion
* @param {ReadonlyQuat} a the first operand
* @param {ReadonlyQuat} b the second operand
* @param {Number} t interpolation amount, in the range [0-1], between the two inputs
* @returns {quat} out
*/
function slerp(out, a, b, t) {
var ax = a[0], ay = a[1], az = a[2], aw = a[3];
var bx = b[0], by = b[1], bz = b[2], bw = b[3];
var omega, cosom = ax * bx + ay * by + az * bz + aw * bw, sinom, scale0, scale1;
if (cosom < 0) {
cosom = -cosom;
bx = -bx;
by = -by;
bz = -bz;
bw = -bw;
}
if (1 - cosom > 1e-6) {
omega = Math.acos(cosom);
sinom = Math.sin(omega);
scale0 = Math.sin((1 - t) * omega) / sinom;
scale1 = Math.sin(t * omega) / sinom;
} else {
scale0 = 1 - t;
scale1 = t;
}
out[0] = scale0 * ax + scale1 * bx;
out[1] = scale0 * ay + scale1 * by;
out[2] = scale0 * az + scale1 * bz;
out[3] = scale0 * aw + scale1 * bw;
return out;
}
/**
* Creates a quaternion from the given 3x3 rotation matrix.
*
* NOTE: The resultant quaternion is not normalized, so you should be sure
* to renormalize the quaternion yourself where necessary.
*
* @param {quat} out the receiving quaternion
* @param {ReadonlyMat3} m rotation matrix
* @returns {quat} out
* @function
*/
function fromMat3(out, m) {
var fTrace = m[0] + m[4] + m[8];
var fRoot;
if (fTrace > 0) {
fRoot = Math.sqrt(fTrace + 1);
out[3] = .5 * fRoot;
fRoot = .5 / fRoot;
out[0] = (m[5] - m[7]) * fRoot;
out[1] = (m[6] - m[2]) * fRoot;
out[2] = (m[1] - m[3]) * fRoot;
} else {
var i = 0;
if (m[4] > m[0]) i = 1;
if (m[8] > m[i * 3 + i]) i = 2;
var j = (i + 1) % 3;
var k = (i + 2) % 3;
fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1);
out[i] = .5 * fRoot;
fRoot = .5 / fRoot;
out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot;
out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot;
out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot;
}
return out;
}
/**
* Creates a quaternion from the given euler angle x, y, z using the provided intrinsic order for the conversion.
*
* @param {quat} out the receiving quaternion
* @param {Number} x Angle to rotate around X axis in degrees.
* @param {Number} y Angle to rotate around Y axis in degrees.
* @param {Number} z Angle to rotate around Z axis in degrees.
* @param {'xyz'|'xzy'|'yxz'|'yzx'|'zxy'|'zyx'} order Intrinsic order for conversion, default is zyx.
* @returns {quat} out
* @function
*/
function fromEuler(out, x, y, z) {
var order = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : "zyx";
var halfToRad = Math.PI / 360;
x *= halfToRad;
z *= halfToRad;
y *= halfToRad;
var sx = Math.sin(x);
var cx = Math.cos(x);
var sy = Math.sin(y);
var cy = Math.cos(y);
var sz = Math.sin(z);
var cz = Math.cos(z);
switch (order) {
case "xyz":
out[0] = sx * cy * cz + cx * sy * sz;
out[1] = cx * sy * cz - sx * cy * sz;
out[2] = cx * cy * sz + sx * sy * cz;
out[3] = cx * cy * cz - sx * sy * sz;
break;
case "xzy":
out[0] = sx * cy * cz - cx * sy * sz;
out[1] = cx * sy * cz - sx * cy * sz;
out[2] = cx * cy * sz + sx * sy * cz;
out[3] = cx * cy * cz + sx * sy * sz;
break;
case "yxz":
out[0] = sx * cy * cz + cx * sy * sz;
out[1] = cx * sy * cz - sx * cy * sz;
out[2] = cx * cy * sz - sx * sy * cz;
out[3] = cx * cy * cz + sx * sy * sz;
break;
case "yzx":
out[0] = sx * cy * cz + cx * sy * sz;
out[1] = cx * sy * cz + sx * cy * sz;
out[2] = cx * cy * sz - sx * sy * cz;
out[3] = cx * cy * cz - sx * sy * sz;
break;
case "zxy":
out[0] = sx * cy * cz - cx * sy * sz;
out[1] = cx * sy * cz + sx * cy * sz;
out[2] = cx * cy * sz + sx * sy * cz;
out[3] = cx * cy * cz - sx * sy * sz;
break;
case "zyx":
out[0] = sx * cy * cz - cx * sy * sz;
out[1] = cx * sy * cz + sx * cy * sz;
out[2] = cx * cy * sz - sx * sy * cz;
out[3] = cx * cy * cz + sx * sy * sz;
break;
default: throw new Error("Unknown angle order " + order);
}
return out;
}
/**
* Creates a new quat initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @param {Number} z Z component
* @param {Number} w W component
* @returns {quat} a new quaternion
* @function
*/
var fromValues$2 = fromValues$3;
/**
* Normalize a quat
*
* @param {quat} out the receiving quaternion
* @param {ReadonlyQuat} a quaternion to normalize
* @returns {quat} out
* @function
*/
var normalize$2 = normalize$3;
(function() {
var tmpvec3 = create$4();
var xUnitVec3 = fromValues$4(1, 0, 0);
var yUnitVec3 = fromValues$4(0, 1, 0);
return function(out, a, b) {
var dot = dot$5(a, b);
if (dot < -.999999) {
cross$2(tmpvec3, xUnitVec3, a);
if (len$4(tmpvec3) < 1e-6) cross$2(tmpvec3, yUnitVec3, a);
normalize$4(tmpvec3, tmpvec3);
setAxisAngle(out, tmpvec3, Math.PI);
return out;
} else if (dot > .999999) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
out[3] = 1;
return out;
} else {
cross$2(tmpvec3, a, b);
out[0] = tmpvec3[0];
out[1] = tmpvec3[1];
out[2] = tmpvec3[2];
out[3] = 1 + dot;
return normalize$2(out, out);
}
};
})();
(function() {
var temp1 = create$2();
var temp2 = create$2();
return function(out, a, b, c, d, t) {
slerp(temp1, a, d, t);
slerp(temp2, b, c, t);
slerp(out, temp1, temp2, 2 * t * (1 - t));
return out;
};
})();
(function() {
var matr = create$6();
return function(out, view, right, up) {
matr[0] = right[0];
matr[3] = right[1];
matr[6] = right[2];
matr[1] = up[0];
matr[4] = up[1];
matr[7] = up[2];
matr[2] = -view[0];
matr[5] = -view[1];
matr[8] = -view[2];
return normalize$2(out, fromMat3(out, matr));
};
})();
/**
* 2 Dimensional Vector
* @module vec2
*/
/**
* Creates a new, empty vec2
*
* @returns {vec2} a new 2D vector
*/
function create() {
var out = new ARRAY_TYPE(2);
if (ARRAY_TYPE != Float32Array) {
out[0] = 0;
out[1] = 0;
}
return out;
}
/**
* Creates a new vec2 initialized with the given values
*
* @param {Number} x X component
* @param {Number} y Y component
* @returns {vec2} a new 2D vector
*/
function fromValues(x, y) {
var out = new ARRAY_TYPE(2);
out[0] = x;
out[1] = y;
return out;
}
/**
* Scales a vec2 by a scalar number
*
* @param {vec2} out the receiving vector
* @param {ReadonlyVec2} a the vector to scale
* @param {Number} b amount to scale the vector by
* @returns {vec2} out
*/
function scale(out, a, b) {
out[0] = a[0] * b;
out[1] = a[1] * b;
return out;
}
/**
* Calculates the length of a vec2
*
* @param {ReadonlyVec2} a vector to calculate length of
* @returns {Number} length of a
*/
function length(a) {
var x = a[0], y = a[1];
return Math.sqrt(x * x + y * y);
}
/**
* Calculates the squared length of a vec2
*
* @param {ReadonlyVec2} a vector to calculate squared length of
* @returns {Number} squared length of a
*/
function squaredLength(a) {
var x = a[0], y = a[1];
return x * x + y * y;
}
/**
* Calculates the dot product of two vec2's
*
* @param {ReadonlyVec2} a the first operand
* @param {ReadonlyVec2} b the second operand
* @returns {Number} dot product of a and b
*/
function dot$1(a, b) {
return a[0] * b[0] + a[1] * b[1];
}
/**
* Set the components of a vec2 to zero
*
* @param {vec2} out the receiving vector
* @returns {vec2} out
*/
function zero(out) {
out[0] = 0;
out[1] = 0;
return out;
}
/**
* Alias for {@link vec2.squaredLength}
* @function
*/
var sqrLen = squaredLength;
(function() {
var vec = create();
return function(a, stride, offset, count, fn, arg) {
var i, l;
if (!stride) stride = 2;
if (!offset) offset = 0;
if (count) l = Math.min(count * stride + offset, a.length);
else l = a.length;
for (i = offset; i < l; i += stride) {
vec[0] = a[i];
vec[1] = a[i + 1];
fn(vec, vec, arg);
a[i] = vec[0];
a[i + 1] = vec[1];
}
return a;
};
})();
//#endregion
//#region src/data/extent.ts
/**
* The maximum value of a coordinate in the internal tile coordinate system. Coordinates of
* all source features normalized to this extent upon load.
*
* The value is a consequence of the following:
*
* * Vertex buffer store positions as signed 16 bit integers.
* * One bit is lost for signedness to support tile buffers.
* * One bit is lost because the line vertex buffer used to pack 1 bit of other data into the int.
* * One bit is lost to support features extending past the extent on the right edge of the tile.
* * This leaves us with 2^13 = 8192
*/
const EXTENT$1 = 8192;
//#endregion
//#region src/source/pixels_to_tile_units.ts
/**
* Converts a pixel value at a the given zoom level to tile units.
*
* The shaders mostly calculate everything in tile units so style
* properties need to be converted from pixels to tile units using this.
*
* For example, a translation by 30 pixels at zoom 6.5 will be a
* translation by pixelsToTileUnits(30, 6.5) tile units.
*
* @returns value in tile units
*/
function pixelsToTileUnits(tile, pixelValue, z) {
return pixelValue * (EXTENT$1 / (tile.tileSize * Math.pow(2, z - tile.tileID.overscaledZ)));
}
//#endregion
//#region src/util/util.ts
const JSON_PREFIX = "__$json__:";
/**
* Ensures that a value is an `Error` instance.
* If the value is already an `Error`, it is returned as-is.
* Otherwise, a new `Error` is created from its string representation.
*/
function ensureError(e) {
if (e instanceof Error) return e;
return new Error(typeof e === "string" ? e : String(e));
}
/**
* Returns a new 64 bit float vec4 of zeroes.
*/
function createVec4f64() {
return /* @__PURE__ */ new Float64Array(4);
}
/**
* Returns a new 64 bit float vec3 of zeroes.
*/
function createVec3f64() {
return /* @__PURE__ */ new Float64Array(3);
}
/**
* Returns a new 64 bit float mat4 of zeroes.
*/
function createMat4f64() {
return /* @__PURE__ */ new Float64Array(16);
}
/**
* Returns a new 64 bit float mat4 set to identity.
*/
function createIdentityMat4f64() {
const m = /* @__PURE__ */ new Float64Array(16);
identity$2(m);
return m;
}
/**
* Returns a new 32 bit float mat4 set to identity.
*/
function createIdentityMat4f32() {
const m = /* @__PURE__ */ new Float32Array(16);
identity$2(m);
return m;
}
/**
* Returns a translation in tile units that correctly incorporates the view angle and the *-translate and *-translate-anchor properties.
* @param inViewportPixelUnitsUnits - True when the units accepted by the matrix are in viewport pixels instead of tile units.
*/
function translatePosition(transform, tile, translate, translateAnchor, inViewportPixelUnitsUnits = false) {
if (!translate[0] && !translate[1]) return [0, 0];
const angle = inViewportPixelUnitsUnits ? translateAnchor === "map" ? -transform.bearingInRadians : 0 : translateAnchor === "viewport" ? transform.bearingInRadians : 0;
if (angle) {
const sinA = Math.sin(angle);
const cosA = Math.cos(angle);
translate = [translate[0] * cosA - translate[1] * sinA, translate[0] * sinA + translate[1] * cosA];
}
return [inViewportPixelUnitsUnits ? translate[0] : pixelsToTileUnits(tile, translate[0], transform.zoom), inViewportPixelUnitsUnits ? translate[1] : pixelsToTileUnits(tile, translate[1], transform.zoom)];
}
/**
* Returns the signed distance between a point and a plane.
* @param plane - The plane equation, in the form where the first three components are the normal and the fourth component is the plane's distance from origin along normal.
* @param point - The point whose distance from plane is returned.
* @returns Signed distance of the point from the plane. Positive distances are in the half space where the plane normal points to, negative otherwise.
*/
function pointPlaneSignedDistance(plane, point) {
return plane[0] * point[0] + plane[1] * point[1] + plane[2] * point[2] + plane[3];
}
/**
* Finds an intersection points of three planes. Returns `null` if no such (single) point exists.
* The planes *must* be in Hessian normal form - their xyz components must form a unit vector.
*/
function threePlaneIntersection(plane0, plane1, plane2) {
const det = determinant$1([
plane0[0],
plane0[1],
plane0[2],
plane1[0],
plane1[1],
plane1[2],
plane2[0],
plane2[1],
plane2[2]
]);
if (det === 0) return null;
const cross12 = cross$2([], [
plane1[0],
plane1[1],
plane1[2]
], [
plane2[0],
plane2[1],
plane2[2]
]);
const cross20 = cross$2([], [
plane2[0],
plane2[1],
plane2[2]
], [
plane0[0],
plane0[1],
plane0[2]
]);
const cross01 = cross$2([], [
plane0[0],
plane0[1],
plane0[2]
], [
plane1[0],
plane1[1],
plane1[2]
]);
const sum = scale$4([], cross12, -plane0[3]);
add$4(sum, sum, scale$4([], cross20, -plane1[3]));
add$4(sum, sum, scale$4([], cross01, -plane2[3]));
scale$4(sum, sum, 1 / det);
return sum;
}
/**
* Returns a parameter `t` such that the point obtained by
* `origin + direction * t` lies on the given plane.
* If the ray is parallel to the plane, returns null.
* Returns a negative value if the ray is pointing away from the plane.
* Direction does not need to be normalized.
*/
function rayPlaneIntersection(origin, direction, plane) {
const dotOriginPlane = origin[0] * plane[0] + origin[1] * plane[1] + origin[2] * plane[2];
const dotDirectionPlane = direction[0] * plane[0] + direction[1] * plane[1] + direction[2] * plane[2];
if (dotDirectionPlane === 0) return null;
return (-dotOriginPlane - plane[3]) / dotDirectionPlane;
}
/**
* Returns the angle in radians between two 2D vectors.
* The angle is signed and describes how much the first vector would need to be be rotated clockwise
* (assuming X is right and Y is down) so that it points in the same direction as the second vector.
* @param vec1x - The X component of the first vector.
* @param vec1y - The Y component of the first vector.
* @param vec2x - The X component of the second vector.
* @param vec2y - The Y component of the second vector.
* @returns The signed angle between the two vectors, in range -PI..PI.
*/
function angleToRotateBetweenVectors2D(vec1x, vec1y, vec2x, vec2y) {
const length1 = Math.sqrt(vec1x * vec1x + vec1y * vec1y);
const length2 = Math.sqrt(vec2x * vec2x + vec2y * vec2y);
vec1x /= length1;
vec1y /= length1;
vec2x /= length2;
vec2y /= length2;
const dot = vec1x * vec2x + vec1y * vec2y;
const angle = Math.acos(dot);
if (-vec1y * vec2x + vec1x * vec2y > 0) return angle;
else return -angle;
}
/**
* For two angles in degrees, returns how many degrees to add to the first angle in order to obtain the second angle.
* The returned difference value is always the shorted of the two - its absolute value is never greater than 180°.
*/
function differenceOfAnglesDegrees(degreesA, degreesB) {
const a = mod(degreesA, 360);
const b = mod(degreesB, 360);
const diff1 = b - a;
const diff2 = b > a ? diff1 - 360 : diff1 + 360;
if (Math.abs(diff1) < Math.abs(diff2)) return diff1;
else return diff2;
}
/**
* When given two angles in radians, returns the angular distance between them - the shorter one of the two possible arcs.
*/
function distanceOfAnglesRadians(radiansA, radiansB) {
const a = mod(radiansA, Math.PI * 2);
const b = mod(radiansB, Math.PI * 2);
return Math.min(Math.abs(a - b), Math.abs(a - b + Math.PI * 2), Math.abs(a - b - Math.PI * 2));
}
/**
* Modulo function, as opposed to javascript's `%`, which is a remainder.
* This functions will return positive values, even if the first operand is negative.
*/
function mod(n, m) {
return (n % m + m) % m;
}
/**
* Takes a value in *old range*, linearly maps that range to *new range*, and returns the value in that new range.
* Additionally, if the value is outside *old range*, it is clamped inside it.
* Also works if one of the ranges is flipped (its `min` being larger than `max`).
*/
function remapSaturate(value, oldRangeMin, oldRangeMax, newRangeMin, newRangeMax) {
return lerp(newRangeMin, newRangeMax, clamp$2((value - oldRangeMin) / (oldRangeMax - oldRangeMin), 0, 1));
}
/**
* Linearly interpolate between two values, similar to `mix` function from GLSL. No clamping is done.
* @param a - The first value to interpolate. This value is returned when mix=0.
* @param b - The second value to interpolate. This value is returned when mix=1.
* @param mix - The interpolation factor. Range 0..1 interpolates between `a` and `b`, but values outside this range are also accepted.
*/
function lerp(a, b, mix) {
return a * (1 - mix) + b * mix;
}
/**
* For a given collection of 2D points, returns their axis-aligned bounding box,
* in the format [minX, minY, maxX, maxY].
*/
function getAABB(points) {
let tlX = Infinity;
let tlY = Infinity;
let brX = -Infinity;
let brY = -Infinity;
for (const p of points) {
tlX = Math.min(tlX, p.x);
tlY = Math.min(tlY, p.y);
brX = Math.max(brX, p.x);
brY = Math.max(brY, p.y);
}
return [
tlX,
tlY,
brX,
brY
];
}
/**
* For a given set of tile ids, returns the edge tile ids for the bounding box.
*/
function getEdgeTiles(tileIDs) {
if (!tileIDs.length) return /* @__PURE__ */ new Set();
const targetZ = Math.max(...tileIDs.map((id) => id.canonical.z));
let minX = Infinity, maxX = -Infinity;
let minY = Infinity, maxY = -Infinity;
const projected = [];
for (const id of tileIDs) {
const { x, y, z } = id.canonical;
const scale = Math.pow(2, targetZ - z);
const px = x * scale;
const py = y * scale;
projected.push({
id,
x: px,
y: py
});
if (px < minX) minX = px;
if (px > maxX) maxX = px;
if (py < minY) minY = py;
if (py > maxY) maxY = py;
}
const edgeTiles = /* @__PURE__ */ new Set();
for (const p of projected) if (p.x === minX || p.x === maxX || p.y === minY || p.y === maxY) edgeTiles.add(p.id);
return edgeTiles;
}
/**
* Given a value `t` that varies between 0 and 1, return
* an interpolation function that eases between 0 and 1 in a pleasing
* cubic in-out fashion.
*/
function easeCubicInOut(t) {
if (t <= 0) return 0;
if (t >= 1) return 1;
const t2 = t * t, t3 = t2 * t;
return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
}
/**
* Given given (x, y), (x1, y1) control points for a bezier curve,
* return a function that interpolates along that curve.
*
* @param p1x - control point 1 x coordinate
* @param p1y - control point 1 y coordinate
* @param p2x - control point 2 x coordinate
* @param p2y - control point 2 y coordinate
*/
function bezier(p1x, p1y, p2x, p2y) {
return unitBezier$1(p1x, p1y, p2x, p2y);
}
/**
* A default bezier-curve powered easing function with
* control points (0.25, 0.1) and (0.25, 1)
*/
const defaultEasing = bezier(.25, .1, .25, 1);
/**
* constrain n to the given range via min + max
*
* @param n - value
* @param min - the minimum value to be returned
* @param max - the maximum value to be returned
* @returns the clamped value
*/
function clamp$2(n, min, max) {
return Math.min(max, Math.max(min, n));
}
/**
* constrain n to the given range, excluding the minimum, via modular arithmetic
*
* @param n - value
* @param min - the minimum value to be returned, exclusive
* @param max - the maximum value to be returned, inclusive
* @returns constrained number
*/
function wrap$1(n, min, max) {
const d = max - min;
const w = ((n - min) % d + d) % d + min;
return w === min ? max : w;
}
function extend(dest, ...sources) {
for (const src of sources) for (const k in src) dest[k] = src[k];
return dest;
}
/**
* Given an object and a number of properties as strings, return version
* of that object with only those properties.
*
* @param src - the object
* @param properties - an array of property names chosen
* to appear on the resulting object.
* @returns object with limited properties.
* @example
* ```ts
* let foo = { name: 'Charlie', age: 10 };
* let justName = pick(foo, ['name']); // justName = { name: 'Charlie' }
* ```
*/
function pick(src, properties) {
const result = {};
for (const k of properties) if (k in src) result[k] = src[k];
return result;
}
let id = 1;
/**
* Return a unique numeric id, starting at 1 and incrementing with
* each call.
*
* @returns unique numeric id.
*/
function uniqueId() {
return id++;
}
/**
* Return whether a given value is a power of two
*/
function isPowerOfTwo(value) {
return Math.log(value) / Math.LN2 % 1 === 0;
}
/**
* Return the next power of two, or the input value if already a power of two
*/
function nextPowerOfTwo(value) {
if (value <= 1) return 1;
return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2));
}
/**
* Computes scaling from zoom level.
*/
function zoomScale(zoom) {
return Math.pow(2, zoom);
}
/**
* Computes zoom level from scaling.
*/
function scaleZoom(scale) {
return Math.log(scale) / Math.LN2;
}
/**
* Evaluates the snapped zoom level based on zoomSnap. If zoomSnap is 0 or less, the zoom level is returned unchanged.
* If delta is provided, it performs directional snapping (ceil for zoom-in, floor for zoom-out).
* @param zoom - The input zoom level
* @param zoomSnap - The grid interval to snap to, e.g. 1.0 for 1.0 zoom levels, 0.5 for 0.5 zoom levels, etc.
* @param delta - Optional scroll delta or direction. If positive, snaps up; if negative, snaps down.
* @returns The snapped zoom level
*/
function evaluateZoomSnap(zoom, zoomSnap, delta) {
if (zoomSnap <= 0) return zoom;
const inv = 1 / zoomSnap;
if (delta === void 0 || Math.abs(delta) < 1e-10) return Math.round(zoom * inv) / inv;
return (delta > 0 ? Math.ceil(zoom * inv - 1e-9) : Math.floor(zoom * inv + 1e-10)) / inv;
}
/**
* Create an object by mapping all the values of an existing object while
* preserving their keys.
*/
function mapObject(input, iterator, context) {
const output = {};
for (const key in input) output[key] = iterator.call(context || this, input[key], key, input);
return output;
}
/**
* Create an object by filtering out values of an existing object.
*/
function filterObject(input, iterator, context) {
const output = {};
for (const key in input) if (iterator.call(context || this, input[key], key, input)) output[key] = input[key];
return output;
}
/**
* Deeply compares two object literals.
* @param a - first object literal to be compared
* @param b - second object literal to be compared
* @returns true if the two object literals are deeply equal, false otherwise
*/
function deepEqual$1(a, b) {
if (Array.isArray(a)) {
if (!Array.isArray(b) || a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) if (!deepEqual$1(a[i], b[i])) return false;
return true;
}
if (typeof a === "object" && a !== null && b !== null) {
if (!(typeof b === "object")) return false;
if (Object.keys(a).length !== Object.keys(b).length) return false;
for (const key in a) if (!deepEqual$1(a[key], b[key])) return false;
return true;
}
return a === b;
}
/**
* Deeply clones two objects.
*/
function clone(input) {
if (Array.isArray(input)) return input.map(clone);
else if (typeof input === "object" && input) return mapObject(input, clone);
else return input;
}
/**
* Print a warning message to the console and ensure duplicate warning messages
* are not printed.
*/
const warnOnceHistory = {};
function warnOnce(message) {
if (!warnOnceHistory[message]) {
if (typeof console !== "undefined") console.warn(message);
warnOnceHistory[message] = true;
}
}
/**
* Indicates if the provided Points are in a counter clockwise (true) or clockwise (false) order
*
* @returns true for a counter clockwise set of points
*/
function isCounterClockwise(a, b, c) {
return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);
}
/**
* For two lines a and b in 2d space, defined by any two points along the lines,
* find the intersection point, or return null if the lines are parallel
*
* @param a1 - First point on line a
* @param a2 - Second point on line a
* @param b1 - First point on line b
* @param b2 - Second point on line b
*
* @returns the intersection point of the two lines or null if they are parallel
*/
function findLineIntersection(a1, a2, b1, b2) {
const aDeltaY = a2.y - a1.y;
const aDeltaX = a2.x - a1.x;
const bDeltaY = b2.y - b1.y;
const bDeltaX = b2.x - b1.x;
const denominator = bDeltaY * aDeltaX - bDeltaX * aDeltaY;
if (denominator === 0) return null;
const originDeltaY = a1.y - b1.y;
const originDeltaX = a1.x - b1.x;
const aInterpolation = (bDeltaX * originDeltaY - bDeltaY * originDeltaX) / denominator;
return new Point(a1.x + aInterpolation * aDeltaX, a1.y + aInterpolation * aDeltaY);
}
/**
* Converts spherical coordinates to cartesian coordinates.
*
* @param spherical - Spherical coordinates, in [radial, azimuthal, polar]
* @returns cartesian coordinates in [x, y, z]
*/
function sphericalToCartesian([r, azimuthal, polar]) {
azimuthal += 90;
azimuthal *= Math.PI / 180;
polar *= Math.PI / 180;
return [
r * Math.cos(azimuthal) * Math.sin(polar),
r * Math.sin(azimuthal) * Math.sin(polar),
r * Math.cos(polar)
];
}
/**
* Returns true if the when run in the web-worker context.
*
* @returns `true` if the when run in the web-worker context.
*/
function isWorker(self) {
return typeof WorkerGlobalScope !== "undefined" && typeof self !== "undefined" && self instanceof WorkerGlobalScope;
}
/**
* Parses data from 'Cache-Control' headers.
*
* @param cacheControl - Value of 'Cache-Control' header
* @returns object containing parsed header info.
*/
function parseCacheControl(cacheControl) {
const re = /(?:^|(?:\s*\,\s*))([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)(?:\=(?:([^\x00-\x20\(\)<>@\,;\:\\"\/\[\]\?\=\{\}\x7F]+)|(?:\"((?:[^"\\]|\\.)*)\")))?/g;
const header = {};
cacheControl.replace(re, ($0, $1, $2, $3) => {
const value = $2 || $3;
header[$1] = value ? value.toLowerCase() : true;
return "";
});
if (header["max-age"]) {
const maxAge = parseInt(header["max-age"], 10);
if (isNaN(maxAge)) delete header["max-age"];
else header["max-age"] = maxAge;
}
return header;
}
let _isSafari = null;
/**
* Returns true when run in WebKit derived browsers.
* This is used as a workaround for a memory leak in Safari caused by using Transferable objects to
* transfer data between WebWorkers and the main thread.
* https://github.com/mapbox/mapbox-gl-js/issues/8771
*
* This should be removed once the underlying Safari issue is fixed.
*
* @param scope - Since this function is used both on the main thread and WebWorker context,
* let the calling scope pass in the global scope object.
* @returns `true` when run in WebKit derived browsers.
*/
function isSafari(scope) {
if (_isSafari == null) {
const userAgent = scope.navigator ? scope.navigator.userAgent : null;
_isSafari = !!scope.safari || !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || !!userAgent.match("Safari") && !userAgent.match("Chrome")));
}
return _isSafari;
}
function isImageBitmap(image) {
return typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap;
}
/**
* Converts an ArrayBuffer to an ImageBitmap.
*
* Used mostly for testing purposes only, because mocking libs don't know how to work with ArrayBuffers, but work
* perfectly fine with ImageBitmaps. Might also be used for environments (other than testing) not supporting
* ArrayBuffers.
*
* @param data - Data to convert
* @returns - A promise resolved when the conversion is finished
*/
const arrayBufferToImageBitmap = async (data, options) => {
if (data.byteLength === 0) return createImageBitmap(new ImageData(1, 1), options);
const blob = new Blob([new Uint8Array(data)], { type: "image/png" });
try {
return createImageBitmap(blob, options);
} catch (e) {
throw new Error(`Could not load image because of ${ensureError(e).message}. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.`);
}
};
const transparentPngUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=";
/**
* Converts an ArrayBuffer to an HTMLImageElement.
*
* Used mostly for testing purposes only, because mocking libs don't know how to work with ArrayBuffers, but work
* perfectly fine with ImageBitmaps. Might also be used for environments (other than testing) not supporting
* ArrayBuffers.
*
* @param data - Data to convert
* @returns - A promise resolved when the conversion is finished
*/
const arrayBufferToImage = (data) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img);
URL.revokeObjectURL(img.src);
img.onload = null;
window.requestAnimationFrame(() => img.src = transparentPngUrl);
};
img.onerror = () => reject(/* @__PURE__ */ new Error("Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported."));
const blob = new Blob([new Uint8Array(data)], { type: "image/png" });
img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl;
});
};
/**
* Computes the webcodecs VideoFrame API options to select a rectangle out of
* an image and write it into the destination rectangle.
*
* Rect (x/y/width/height) select the overlapping rectangle from the source image
* and layout (offset/stride) write that overlapping rectangle to the correct place
* in the destination image.
*
* Offset is the byte offset in the dest image that the first pixel appears at
* and stride is the number of bytes to the start of the next row:
* ┌───────────┐
* │ dest │
* │ ┌───┼───────┐
* │offset→│▓▓▓│ source│
* │ │▓▓▓│ │
* │ └───┼───────┘
* │stride ⇠╌╌╌│
* │╌╌╌╌╌╌→ │
* └───────────┘
*
* @param image - source image containing a width and height attribute
* @param x - top-left x coordinate to read from the image
* @param y - top-left y coordinate to read from the image
* @param width - width of the rectangle to read from the image
* @param height - height of the rectangle to read from the image
* @returns the layout and rect options to pass into VideoFrame API
*/
function computeVideoFrameParameters(image, x, y, width, height) {
const destRowOffset = Math.max(-x, 0) * 4;
const offset = (Math.max(0, y) - y) * width * 4 + destRowOffset;
const stride = width * 4;
const sourceLeft = Math.max(0, x);
const sourceTop = Math.max(0, y);
const sourceRight = Math.min(image.width, x + width);
const sourceBottom = Math.min(image.height, y + height);
return {
rect: {
x: sourceLeft,
y: sourceTop,
width: sourceRight - sourceLeft,
height: sourceBottom - sourceTop
},
layout: [{
offset,
stride
}]
};
}
/**
* Reads pixels from an ImageBitmap/Image/canvas using webcodec VideoFrame API.
*
* @param data - image, imagebitmap, or canvas to parse
* @param x - top-left x coordinate to read from the image
* @param y - top-left y coordinate to read from the image
* @param width - width of the rectangle to read from the image
* @param height - height of the rectangle to read from the image
* @returns a promise containing the parsed RGBA pixel values of the image, or the error if an error occurred
*/
async function readImageUsingVideoFrame(image, x, y, width, height) {
if (typeof VideoFrame === "undefined") throw new Error("VideoFrame not supported");
const frame = new VideoFrame(image, { timestamp: 0 });
try {
const format = frame?.format;
if (!format || !(format.startsWith("BGR") || format.startsWith("RGB"))) throw new Error(`Unrecognized format ${format}`);
const swapBR = format.startsWith("BGR");
const result = new Uint8ClampedArray(width * height * 4);
await frame.copyTo(result, computeVideoFrameParameters(image, x, y, width, height));
if (swapBR) for (let i = 0; i < result.length; i += 4) {
const tmp = result[i];
result[i] = result[i + 2];
result[i + 2] = tmp;
}
return result;
} finally {
frame.close();
}
}
let offscreenCanvas;
let offscreenCanvasContext;
/**
* Reads pixels from an ImageBitmap/Image/canvas using OffscreenCanvas
*
* @param data - image, imagebitmap, or canvas to parse
* @param x - top-left x coordinate to read from the image
* @param y - top-left y coordinate to read from the image
* @param width - width of the rectangle to read from the image
* @param height - height of the rectangle to read from the image
* @returns a promise containing the parsed RGBA pixel values of the image, or the error if an error occurred
*/
function readImageDataUsingOffscreenCanvas(imgBitmap, x, y, width, height) {
const origWidth = imgBitmap.width;
const origHeight = imgBitmap.height;
if (!offscreenCanvas || !offscreenCanvasContext) {
offscreenCanvas = new OffscreenCanvas(origWidth, origHeight);
offscreenCanvasContext = offscreenCanvas.getContext("2d", { willReadFrequently: true });
}
offscreenCanvas.width = origWidth;
offscreenCanvas.height = origHeight;
offscreenCanvasContext.drawImage(imgBitmap, 0, 0, origWidth, origHeight);
const imgData = offscreenCanvasContext.getImageData(x, y, width, height);
offscreenCanvasContext.clearRect(0, 0, origWidth, origHeight);
return imgData.data;
}
/**
* Reads RGBA pixels from an preferring OffscreenCanvas, but falling back to VideoFrame if supported and
* the browser is mangling OffscreenCanvas getImageData results.
*
* @param data - image, imagebitmap, or canvas to parse
* @param x - top-left x coordinate to read from the image
* @param y - top-left y coordinate to read from the image
* @param width - width of the rectangle to read from the image
* @param height - height of the rectangle to read from the image
* @returns a promise containing the parsed RGBA pixel values of the image
*/
async function getImageData(image, x, y, width, height) {
if (isOffscreenCanvasDistorted()) try {
return await readImageUsingVideoFrame(image, x, y, width, height);
} catch {}
return readImageDataUsingOffscreenCanvas(image, x, y, width, height);
}
/**
* This method is used in order to register an event listener using a lambda function.
* The return value will allow unsubscribing from the event, without the need to store the method reference.
* @param target - The target
* @param message - The message
* @param listener - The listener
* @param options - The options
* @returns a subscription object that can be used to unsubscribe from the event
*/
function subscribe(target, message, listener, options) {
target.addEventListener(message, listener, options);
return { unsubscribe: () => {
target.removeEventListener(message, listener, options);
} };
}
/**
* This method converts degrees to radians.
* The return value is the radian value.
* @param degrees - The number of degrees
* @returns radians
*/
function degreesToRadians(degrees) {
return degrees * Math.PI / 180;
}
/**
* This method converts radians to degrees.
* The return value is the degrees value.
* @param degrees - The number of radians
* @returns degrees
*/
function radiansToDegrees(degrees) {
return degrees / Math.PI * 180;
}
function rollPitchBearingEqual(a, b) {
return a.roll == b.roll && a.pitch == b.pitch && a.bearing == b.bearing;
}
/**
* This method converts a rotation quaternion to roll, pitch, and bearing angles in degrees.
* @param rotation - The rotation quaternion
* @returns roll, pitch, and bearing angles in degrees
*/
function getRollPitchBearing(rotation) {
const m = /* @__PURE__ */ new Float64Array(9);
fromQuat$1(m, rotation);
const xAngle = radiansToDegrees(-Math.asin(clamp$2(m[2], -1, 1)));
let roll;
let bearing;
if (Math.hypot(m[5], m[8]) < .001) {
roll = 0;
bearing = -radiansToDegrees(Math.atan2(m[3], m[4]));
} else {
roll = radiansToDegrees(m[5] === 0 && m[8] === 0 ? 0 : Math.atan2(m[5], m[8]));
bearing = radiansToDegrees(m[1] === 0 && m[0] === 0 ? 0 : Math.atan2(m[1], m[0]));
}
return {
roll,
pitch: xAngle + 90,
bearing
};
}
function getAngleDelta(lastPoint, currentPoint, center) {
const pointVect = fromValues(currentPoint.x - center.x, currentPoint.y - center.y);
const lastPointVec = fromValues(lastPoint.x - center.x, lastPoint.y - center.y);
const crossProduct = pointVect[0] * lastPointVec[1] - pointVect[1] * lastPointVec[0];
return radiansToDegrees(Math.atan2(crossProduct, dot$1(pointVect, lastPointVec)));
}
/**
* This method converts roll, pitch, and bearing angles in degrees to a rotation quaternion.
* @param roll - Roll angle in degrees
* @param pitch - Pitch angle in degrees
* @param bearing - Bearing angle in degrees
* @returns The rotation quaternion
*/
function rollPitchBearingToQuat(roll, pitch, bearing) {
const rotation = /* @__PURE__ */ new Float64Array(4);
fromEuler(rotation, roll, pitch - 90, bearing);
return rotation;
}
const MAX_VALID_LATITUDE = 85.051129;
const touchableEvents = {
touchstart: true,
touchmove: true,
touchmoveWindow: true,
touchend: true,
touchcancel: true
};
const pointableEvents = {
dblclick: true,
click: true,
mouseover: true,
mouseout: true,
mousedown: true,
mousemove: true,
mousemoveWindow: true,
mouseup: true,
mouseupWindow: true,
contextmenu: true,
wheel: true
};
function isTouchableEvent(event, eventType) {
return touchableEvents[eventType] && "touches" in event;
}
/**
* Checks if an event is a pointable event (mouse or wheel event).
* Uses the event target's window context for cross-window support.
*/
function isPointableEvent(event, eventType) {
if (!pointableEvents[eventType]) return false;
const domEvent = event;
const targetWindow = (domEvent?.target)?.ownerDocument?.defaultView || window;
return domEvent instanceof targetWindow.MouseEvent || domEvent instanceof targetWindow.WheelEvent;
}
function isTouchableOrPointableType(eventType) {
return touchableEvents[eventType] || pointableEvents[eventType];
}
//#endregion
//#region src/util/abort_error.ts
/**
* An error message to use when an operation is aborted
*/
const ABORT_ERROR = "AbortError";
var AbortError = class extends Error {
constructor(messageOrError = ABORT_ERROR) {
super(messageOrError instanceof Error ? messageOrError.message : messageOrError);
this.name = ABORT_ERROR;
if (messageOrError instanceof Error && messageOrError.stack) this.stack = messageOrError.stack;
}
};
/**
* Check if an error is an abort error
* @param error - An error object
* @returns - true if the error is an abort error
*/
function isAbortError(error) {
return error instanceof Error && error.name === "AbortError";
}
/**
* Throws an AbortError if the provided abort signal has already been aborted.
*
* @param signal - The abort signal to check.
* @throws AbortError If the signal is aborted.
*/
function throwIfAborted(signal) {
if (signal.aborted) throw new AbortError(signal.reason);
}
//#endregion
//#region src/util/config.ts
const config = {
MAX_PARALLEL_IMAGE_REQUESTS: 16,
MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME: 8,
MAX_TILE_CACHE_ZOOM_LEVELS: 5,
REGISTERED_PROTOCOLS: {},
WORKER_URL: ""
};
//#endregion
//#region src/source/protocol_crud.ts
function getProtocol(url) {
return config.REGISTERED_PROTOCOLS[url.substring(0, url.indexOf("://"))];
}
/**
* Adds a custom load resource function that will be called when using a URL that starts with a custom url schema.
* This will happen in the main thread, and workers might call it if they don't know how to handle the protocol.
* The example below will be triggered for custom:// urls defined in the sources list in the style definitions.
* The function passed will receive the request parameters and should return with the resulting resource,
* for example a pbf vector tile, non-compressed, represented as ArrayBuffer.
*
* @param customProtocol - the protocol to hook, for example 'custom'
* @param loadFn - the function to use when trying to fetch a tile specified by the customProtocol
* @example
* ```ts
* // This will fetch a file using the fetch API (this is obviously a non interesting example...)
* addProtocol('custom', async (params, abortController) => {
* const t = await fetch(`https://${params.url.split("://")[1]}`);
* if (t.status == 200) {
* const buffer = await t.arrayBuffer();
* return {data: buffer}
* } else {
* throw new Error(`Tile fetch error: ${t.statusText}`);
* }
* });
* // the following is an example of a way to return an error when trying to load a tile
* addProtocol('custom2', async (params, abortController) => {
* throw new Error('someErrorMessage');
* });
* ```
* @see [Add a COG raster source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-cog-raster-source/)
* @see [Add Contour Lines](https://maplibre.org/maplibre-gl-js/docs/examples/add-contour-lines/)
* @see [PMTiles source and protocol](https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles-source-and-protocol/)
* @see [Use addProtocol to Transform Feature Properties](https://maplibre.org/maplibre-gl-js/docs/examples/use-addprotocol-to-transform-feature-properties/)
*/
function addProtocol(customProtocol, loadFn) {
config.REGISTERED_PROTOCOLS[customProtocol] = loadFn;
}
/**
* Removes a previously added protocol in the main thread.
*
* @param customProtocol - the custom protocol to remove registration for
* @example
* ```ts
* removeProtocol('custom');
* ```
*/
function removeProtocol(customProtocol) {
delete config.REGISTERED_PROTOCOLS[customProtocol];
}
//#endregion
//#region src/util/ajax.ts
/**
* This is used to identify the global dispatcher id when sending a message from the worker without a target map id.
*/
const GLOBAL_DISPATCHER_ID = "global-dispatcher";
/**
* An error thrown when a HTTP request results in an error response.
*/
var AJAXError = class extends Error {
/**
* @param status - The response's HTTP status code.
* @param statusText - The response's HTTP status text.
* @param url - The request's URL.
* @param body - The response's body.
*/
constructor(status, statusText, url, body) {
super(`AJAXError: ${statusText} (${status}): ${url}`);
this.status = status;
this.statusText = statusText;
this.url = url;
this.body = body;
}
};
/**
* Ensure that we're sending the correct referrer from blob URL worker bundles.
* For files loaded from the local file system, `location.origin` will be set
* to the string(!) "null" (Firefox), or "file://" (Chrome, Safari, Edge),
* and we will set an empty referrer. Otherwise, we're using the document's URL.
* If we're on a blob URL and parent window is cross-origin, parent.location throws
* SecurityError DOMException, this means we are probably not in blob URL worker bundle.
*/
function getReferrer() {
if (isWorker(self)) return self.worker?.referrer;
if (window.location.protocol === "blob:") try {
return window.parent.location.href;
} catch {}
return window.location.href;
}
/**
* Determines whether a URL is a file:// URL. This is obviously the case if it begins
* with file://. Relative URLs are also file:// URLs iff the original document was loaded
* via a file:// URL.
* @param url - The URL to check
* @returns `true` if the URL is a file:// URL, `false` otherwise
*/
const isFileURL = (url) => url.startsWith("file:") || getReferrer()?.startsWith("file:") && !/^\w+:/.test(url);
async function makeFetchRequest(requestParameters, abortController) {
const request = new Request(requestParameters.url, {
method: requestParameters.method || "GET",
body: requestParameters.body,
credentials: requestParameters.credentials,
headers: requestParameters.headers,
cache: requestParameters.cache,
referrer: getReferrer(),
referrerPolicy: requestParameters.referrerPolicy,
signal: abortController.signal
});
if (requestParameters.type === "json" && !request.headers.has("Accept")) request.headers.set("Accept", "application/json");
let response;
try {
response = await fetch(request);
} catch (e) {
if (isAbortError(e)) throw e;
throw new AJAXError(0, ensureError(e).message, requestParameters.url, new Blob());
}
if (!response.ok) {
const body = await response.blob();
throw new AJAXError(response.status, response.statusText, requestParameters.url, body);
}
let parsePromise;
if (requestParameters.type === "arrayBuffer" || requestParameters.type === "image") parsePromise = response.arrayBuffer();
else if (requestParameters.type === "json") parsePromise = response.json();
else parsePromise = response.text();
const result = await parsePromise;
throwIfAborted(abortController.signal);
return {
data: result,
cacheControl: response.headers.get("Cache-Control"),
expires: response.headers.get("Expires"),
etag: response.headers.get("ETag")
};
}
function makeXMLHttpRequest(requestParameters, abortController) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(requestParameters.method || "GET", requestParameters.url, true);
if (requestParameters.type === "arrayBuffer" || requestParameters.type === "image") xhr.responseType = "arraybuffer";
for (const k in requestParameters.headers) xhr.setRequestHeader(k, requestParameters.headers[k]);
if (requestParameters.type === "json") {
xhr.responseType = "text";
if (!requestParameters.headers?.Accept) xhr.setRequestHeader("Accept", "application/json");
}
xhr.withCredentials = requestParameters.credentials === "include";
xhr.onerror = () => {
reject(new Error(xhr.statusText));
};
xhr.onload = () => {
if (abortController.signal.aborted) return;
if ((xhr.status >= 200 && xhr.status < 300 || xhr.status === 0) && xhr.response !== null) {
let data = xhr.response;
if (requestParameters.type === "json") try {
data = JSON.parse(xhr.response);
} catch (err) {
reject(err);
return;
}
resolve({
data,
cacheControl: xhr.getResponseHeader("Cache-Control"),
expires: xhr.getResponseHeader("Expires"),
etag: xhr.getResponseHeader("ETag")
});
} else {
const body = new Blob([xhr.response], { type: xhr.getResponseHeader("Content-Type") });
reject(new AJAXError(xhr.status, xhr.statusText, requestParameters.url, body));
}
};
abortController.signal.addEventListener("abort", () => {
xhr.abort();
reject(new AbortError(abortController.signal.reason));
});
xhr.send(requestParameters.body);
});
}
/**
* We're trying to use the Fetch API if possible. However, requests for resources with the file:// URI scheme don't work with the Fetch API.
* In this case we unconditionally use XHR on the current thread since referrers don't matter.
* This method can also use the registered method if `addProtocol` was called.
* @param requestParameters - The request parameters
* @param abortController - The abort controller allowing to cancel the request
* @returns a promise resolving to the response, including cache control and expiry data
*/
const makeRequest = async function(requestParameters, abortController) {
if (requestParameters.url.includes("://") && !/^https?:|^file:/.test(requestParameters.url)) {
const protocolLoadFn = getProtocol(requestParameters.url);
if (protocolLoadFn) {
const response = await protocolLoadFn(requestParameters, abortController);
if (!response.data && requestParameters.type === "arrayBuffer") return extend(response, { data: /* @__PURE__ */ new ArrayBuffer(0) });
return response;
}
if (isWorker(self) && self.worker?.actor) return self.worker.actor.sendAsync({
type: "GR",
data: requestParameters,
targetMapId: GLOBAL_DISPATCHER_ID
}, abortController);
}
if (!isFileURL(requestParameters.url)) {
if (fetch && Request && AbortController && Object.hasOwn(Request.prototype, "signal")) return makeFetchRequest(requestParameters, abortController);
if (isWorker(self) && self.worker?.actor) return self.worker.actor.sendAsync({
type: "GR",
data: requestParameters,
mustQueue: true,
targetMapId: GLOBAL_DISPATCHER_ID
}, abortController);
}
return makeXMLHttpRequest(requestParameters, abortController);
};
const getJSON = (requestParameters, abortController) => {
return makeRequest(extend(requestParameters, { type: "json" }), abortController);
};
const getArrayBuffer = (requestParameters, abortController) => {
return makeRequest(extend(requestParameters, { type: "arrayBuffer" }), abortController);
};
/**
* Determines whether a URL is same origin as the current location. Supports relative URLs too.
*
* A relative URL "/foo" or "./foo" will throw exception in URL's ctor,
* try-catch is expansive so just use a heuristic check to avoid it
*
* - Relative URL and empty URL are always same origin.
* - data URL containing an image is always same origin.
* - blob URL is checked using `URL` constructor by its parent URL; opaque blob URL is never same origin.
* - Absolute URL is checked using `URL` constructor.
*
* Checks blob URL before relative URL because opaque blob URL does not contain `://` too.
*
* @param inComingUrl - The URL to check
* @returns `true` if the URL is same origin as current location, `false` otherwise
*/
function sameOrigin(inComingUrl) {
if (!inComingUrl) return true;
if (inComingUrl.startsWith("data:image/")) return true;
if (inComingUrl.startsWith("blob:")) {
inComingUrl = inComingUrl.slice(5);
if (inComingUrl.startsWith("null")) return false;
}
if (inComingUrl.indexOf("://") <= 0) return true;
const urlObj = new URL(inComingUrl);
const locationObj = window.location;
return urlObj.protocol === locationObj.protocol && urlObj.host === locationObj.host;
}
const getVideo = (urls) => {
const video = window.document.createElement("video");
video.muted = true;
return new Promise((resolve) => {
video.onloadstart = () => {
resolve(video);
};
for (const url of urls) {
const s = window.document.createElement("source");
if (!sameOrigin(url)) video.crossOrigin = "Anonymous";
s.src = url;
video.appendChild(s);
}
});
};
//#endregion
//#region src/util/evented.ts
function _addEventListener(type, listener, listenerList) {
if (!listenerList[type]?.includes(listener)) {
listenerList[type] ||= [];
listenerList[type].push(listener);
}
}
function _removeEventListener(type, listener, listenerList) {
if (listenerList?.[type]) {
const index = listenerList[type].indexOf(listener);
if (index !== -1) listenerList[type].splice(index, 1);
}
}
/**
* The event class
*/
var Event = class {
constructor(type, data = {}) {
extend(this, data);
this.type = type;
}
};
/**
* An error event
*/
var ErrorEvent = class extends Event {
constructor(error, data = {}) {
super("error", extend({ error }, data));
}
};
/**
* Methods mixed in to other classes for event capabilities.
*
* @group Event Related
*/
var Evented = class {
/**
* Adds a listener to a specified event type.
*
* @param type - The event type to add a listen for.
* @param listener - The function to be called when the event is fired.
* The listener function is called with the data object passed to `fire`,
* extended with `target` and `type` properties.
*/
on(type, listener) {
this._listeners ||= {};
_addEventListener(type, listener, this._listeners);
return { unsubscribe: () => {
this.off(type, listener);
} };
}
/**
* Removes a previously registered event listener.
*
* @param type - The event type to remove listeners for.
* @param listener - The listener function to remove.
*/
off(type, listener) {
_removeEventListener(type, listener, this._listeners);
_removeEventListener(type, listener, this._oneTimeListeners);
return this;
}
once(type, listener) {
if (!listener) return new Promise((resolve) => this.once(type, resolve));
this._oneTimeListeners ||= {};
_addEventListener(type, listener, this._oneTimeListeners);
return this;
}
fire(event, properties) {
const firedEvent = typeof event === "string" ? new Event(event, properties || {}) : event;
const type = firedEvent.type;
if (this.listens(type)) {
firedEvent.target = this;
const listeners = this._listeners?.[type]?.slice() ?? [];
for (const listener of listeners) listener.call(this, firedEvent);
const oneTimeListeners = this._oneTimeListeners?.[type]?.slice() ?? [];
for (const listener of oneTimeListeners) {
_removeEventListener(type, listener, this._oneTimeListeners);
listener.call(this, firedEvent);
}
const parent = this._eventedParent;
if (parent) {
extend(firedEvent, typeof this._eventedParentData === "function" ? this._eventedParentData() : this._eventedParentData);
parent.fire(firedEvent);
}
} else if (firedEvent instanceof ErrorEvent) console.error(firedEvent.error);
return this;
}
/**
* Returns a true if this instance of Evented or any forwardeed instances of Evented have a listener for the specified type.
*
* @param type - The event type
* @returns `true` if there is at least one registered listener for specified event type, `false` otherwise
*/
listens(type) {
return Boolean(this._listeners?.[type]?.length || this._oneTimeListeners?.[type]?.length || this._eventedParent?.listens(type));
}
/**
* Bubble all events fired by this instance of Evented to this parent instance of Evented.
*/
setEventedParent(parent, data) {
this._eventedParent = parent;
this._eventedParentData = data;
return this;
}
};
const latest = {
$version: 8,
$root: {
"version": {
"required": true,
"type": "enum",
"values": [8]
},
"name": { "type": "string" },
"metadata": { "type": "*" },
"center": {
"type": "array",
"value": "number",
"length": 2
},
"centerAltitude": { "type": "number" },
"zoom": { "type": "number" },
"bearing": {
"type": "number",
"default": 0,
"period": 360,
"units": "degrees"
},
"pitch": {
"type": "number",
"default": 0,
"units": "degrees"
},
"roll": {
"type": "number",
"default": 0,
"units": "degrees"
},
"state": {
"type": "state",
"default": {}
},
"light": { "type": "light" },
"sky": { "type": "sky" },
"projection": { "type": "projection" },
"terrain": { "type": "terrain" },
"sources": {
"required": true,
"type": "sources"
},
"sprite": { "type": "sprite" },
"glyphs": { "type": "string" },
"font-faces": { "type": "fontFaces" },
"transition": { "type": "transition" },
"layers": {
"required": true,
"type": "array",
"value": "layer"
}
},
sources: { "*": { "type": "source" } },
source: [
"source_vector",
"source_raster",
"source_raster_dem",
"source_geojson",
"source_video",
"source_image"
],
source_vector: {
"type": {
"required": true,
"type": "enum",
"values": { "vector": {} }
},
"url": { "type": "string" },
"tiles": {
"type": "array",
"value": "string"
},
"bounds": {
"type": "array",
"value": "number",
"length": 4,
"default": [
-180,
-85.051129,
180,
85.051129
]
},
"scheme": {
"type": "enum",
"values": {
"xyz": {},
"tms": {}
},
"default": "xyz"
},
"minzoom": {
"type": "number",
"default": 0
},
"maxzoom": {
"type": "number",
"default": 22
},
"attribution": { "type": "string" },
"promoteId": { "type": "promoteId" },
"volatile": {
"type": "boolean",
"default": false
},
"encoding": {
"type": "enum",
"values": {
"mvt": {},
"mlt": {}
},
"default": "mvt"
},
"*": { "type": "*" }
},
source_raster: {
"type": {
"required": true,
"type": "enum",
"values": { "raster": {} }
},
"url": { "type": "string" },
"tiles": {
"type": "array",
"value": "string"
},
"bounds": {
"type": "array",
"value": "number",
"length": 4,
"default": [
-180,
-85.051129,
180,
85.051129
]
},
"minzoom": {
"type": "number",
"default": 0
},
"maxzoom": {
"type": "number",
"default": 22
},
"tileSize": {
"type": "number",
"default": 512,
"units": "pixels"
},
"scheme": {
"type": "enum",
"values": {
"xyz": {},
"tms": {}
},
"default": "xyz"
},
"attribution": { "type": "string" },
"volatile": {
"type": "boolean",
"default": false
},
"*": { "type": "*" }
},
source_raster_dem: {
"type": {
"required": true,
"type": "enum",
"values": { "raster-dem": {} }
},
"url": { "type": "string" },
"tiles": {
"type": "array",
"value": "string"
},
"bounds": {
"type": "array",
"value": "number",
"length": 4,
"default": [
-180,
-85.051129,
180,
85.051129
]
},
"minzoom": {
"type": "number",
"default": 0
},
"maxzoom": {
"type": "number",
"default": 22
},
"tileSize": {
"type": "number",
"default": 512,
"units": "pixels"
},
"attribution": { "type": "string" },
"encoding": {
"type": "enum",
"values": {
"terrarium": {},
"mapbox": {},
"custom": {}
},
"default": "mapbox"
},
"redFactor": {
"type": "number",
"default": 1
},
"blueFactor": {
"type": "number",
"default": 1
},
"greenFactor": {
"type": "number",
"default": 1
},
"baseShift": {
"type": "number",
"default": 0
},
"volatile": {
"type": "boolean",
"default": false
},
"*": { "type": "*" }
},
source_geojson: {
"type": {
"required": true,
"type": "enum",
"values": { "geojson": {} }
},
"data": {
"required": true,
"type": "*"
},
"maxzoom": {
"type": "number",
"default": 18
},
"attribution": { "type": "string" },
"buffer": {
"type": "number",
"default": 128,
"maximum": 512,
"minimum": 0
},
"filter": { "type": "filter" },
"tolerance": {
"type": "number",
"default": .375
},
"cluster": {
"type": "boolean",
"default": false
},
"clusterRadius": {
"type": "number",
"default": 50,
"minimum": 0
},
"clusterMaxZoom": { "type": "number" },
"clusterMinPoints": { "type": "number" },
"clusterProperties": { "type": "*" },
"lineMetrics": {
"type": "boolean",
"default": false
},
"generateId": {
"type": "boolean",
"default": false
},
"promoteId": { "type": "promoteId" }
},
source_video: {
"type": {
"required": true,
"type": "enum",
"values": { "video": {} }
},
"urls": {
"required": true,
"type": "array",
"value": "string"
},
"coordinates": {
"required": true,
"type": "array",
"length": 4,
"value": {
"type": "array",
"length": 2,
"value": "number"
}
}
},
source_image: {
"type": {
"required": true,
"type": "enum",
"values": { "image": {} }
},
"url": {
"required": true,
"type": "string"
},
"coordinates": {
"required": true,
"type": "array",
"length": 4,
"value": {
"type": "array",
"length": 2,
"value": "number"
}
}
},
layer: {
"id": {
"type": "string",
"required": true
},
"type": {
"type": "enum",
"values": {
"fill": {},
"line": {},
"symbol": {},
"circle": {},
"heatmap": {},
"fill-extrusion": {},
"raster": {},
"hillshade": {},
"color-relief": {},
"background": {}
},
"required": true
},
"metadata": { "type": "*" },
"source": { "type": "string" },
"source-layer": { "type": "string" },
"minzoom": {
"type": "number",
"minimum": 0,
"maximum": 24
},
"maxzoom": {
"type": "number",
"minimum": 0,
"maximum": 24
},
"filter": { "type": "filter" },
"layout": { "type": "layout" },
"paint": { "type": "paint" }
},
layout: [
"layout_fill",
"layout_line",
"layout_circle",
"layout_heatmap",
"layout_fill-extrusion",
"layout_symbol",
"layout_raster",
"layout_hillshade",
"layout_color-relief",
"layout_background"
],
layout_background: { "visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
} },
layout_fill: {
"fill-sort-key": {
"type": "number",
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
}
},
layout_circle: {
"circle-sort-key": {
"type": "number",
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
}
},
layout_heatmap: { "visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
} },
"layout_fill-extrusion": {
"visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
},
"fill-extrusion-rounded-corner-distance": {
"type": "number",
"default": 0,
"minimum": 0,
"units": "meters",
"property-type": "constant"
}
},
layout_line: {
"line-cap": {
"type": "enum",
"values": {
"butt": {},
"round": {},
"square": {}
},
"default": "butt",
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"line-join": {
"type": "enum",
"values": {
"bevel": {},
"round": {},
"miter": {}
},
"default": "miter",
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"line-miter-limit": {
"type": "number",
"default": 2,
"requires": [{ "line-join": "miter" }],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"line-round-limit": {
"type": "number",
"default": 1.05,
"requires": [{ "line-join": "round" }],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"line-sort-key": {
"type": "number",
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
}
},
layout_symbol: {
"symbol-placement": {
"type": "enum",
"values": {
"point": {},
"line": {},
"line-center": {}
},
"default": "point",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"symbol-spacing": {
"type": "number",
"default": 250,
"minimum": 1,
"units": "pixels",
"requires": [{ "symbol-placement": "line" }],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"symbol-avoid-edges": {
"type": "boolean",
"default": false,
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"symbol-sort-key": {
"type": "number",
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"symbol-z-order": {
"type": "enum",
"values": {
"auto": {},
"viewport-y": {},
"source": {}
},
"default": "auto",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-allow-overlap": {
"type": "boolean",
"default": false,
"requires": ["icon-image", { "!": "icon-overlap" }],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-overlap": {
"type": "enum",
"values": {
"never": {},
"always": {},
"cooperative": {}
},
"requires": ["icon-image"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-ignore-placement": {
"type": "boolean",
"default": false,
"requires": ["icon-image"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-optional": {
"type": "boolean",
"default": false,
"requires": ["icon-image", "text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-rotation-alignment": {
"type": "enum",
"values": {
"map": {},
"viewport": {},
"auto": {}
},
"default": "auto",
"requires": ["icon-image"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-size": {
"type": "number",
"default": 1,
"minimum": 0,
"units": "factor of the original icon size",
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"icon-text-fit": {
"type": "enum",
"values": {
"none": {},
"width": {},
"height": {},
"both": {}
},
"default": "none",
"requires": ["icon-image", "text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-text-fit-padding": {
"type": "array",
"value": "number",
"length": 4,
"default": [
0,
0,
0,
0
],
"units": "pixels",
"requires": [
"icon-image",
"text-field",
{ "icon-text-fit": [
"both",
"width",
"height"
] }
],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-image": {
"type": "resolvedImage",
"tokens": true,
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"icon-rotate": {
"type": "number",
"default": 0,
"period": 360,
"units": "degrees",
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"icon-padding": {
"type": "padding",
"default": [2],
"units": "pixels",
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"icon-keep-upright": {
"type": "boolean",
"default": false,
"requires": [
"icon-image",
{ "icon-rotation-alignment": "map" },
{ "symbol-placement": ["line", "line-center"] }
],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-offset": {
"type": "array",
"value": "number",
"length": 2,
"default": [0, 0],
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"icon-anchor": {
"type": "enum",
"values": {
"center": {},
"left": {},
"right": {},
"top": {},
"bottom": {},
"top-left": {},
"top-right": {},
"bottom-left": {},
"bottom-right": {}
},
"default": "center",
"requires": ["icon-image"],
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"icon-pitch-alignment": {
"type": "enum",
"values": {
"map": {},
"viewport": {},
"auto": {}
},
"default": "auto",
"requires": ["icon-image"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-pitch-alignment": {
"type": "enum",
"values": {
"map": {},
"viewport": {},
"auto": {}
},
"default": "auto",
"requires": ["text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-rotation-alignment": {
"type": "enum",
"values": {
"map": {},
"viewport": {},
"viewport-glyph": {},
"auto": {}
},
"default": "auto",
"requires": ["text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-field": {
"type": "formatted",
"default": "",
"tokens": true,
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-font": {
"type": "array",
"value": "string",
"default": ["Open Sans Regular", "Arial Unicode MS Regular"],
"requires": ["text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-size": {
"type": "number",
"default": 16,
"minimum": 0,
"units": "pixels",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-max-width": {
"type": "number",
"default": 10,
"minimum": 0,
"units": "ems",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-line-height": {
"type": "number",
"default": 1.2,
"units": "ems",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-letter-spacing": {
"type": "number",
"default": 0,
"units": "ems",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-justify": {
"type": "enum",
"values": {
"auto": {},
"left": {},
"center": {},
"right": {}
},
"default": "center",
"requires": ["text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-radial-offset": {
"type": "number",
"units": "ems",
"default": 0,
"requires": ["text-field"],
"property-type": "data-driven",
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
}
},
"text-variable-anchor": {
"type": "array",
"value": "enum",
"values": {
"center": {},
"left": {},
"right": {},
"top": {},
"bottom": {},
"top-left": {},
"top-right": {},
"bottom-left": {},
"bottom-right": {}
},
"requires": ["text-field", { "symbol-placement": ["point"] }],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-variable-anchor-offset": {
"type": "variableAnchorOffsetCollection",
"requires": ["text-field", { "symbol-placement": ["point"] }],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-anchor": {
"type": "enum",
"values": {
"center": {},
"left": {},
"right": {},
"top": {},
"bottom": {},
"top-left": {},
"top-right": {},
"bottom-left": {},
"bottom-right": {}
},
"default": "center",
"requires": ["text-field", { "!": "text-variable-anchor" }],
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-max-angle": {
"type": "number",
"default": 45,
"units": "degrees",
"requires": ["text-field", { "symbol-placement": ["line", "line-center"] }],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-writing-mode": {
"type": "array",
"value": "enum",
"values": {
"horizontal": {},
"vertical": {}
},
"requires": ["text-field", { "symbol-placement": ["point"] }],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-rotate": {
"type": "number",
"default": 0,
"period": 360,
"units": "degrees",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-padding": {
"type": "number",
"default": 2,
"minimum": 0,
"units": "pixels",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-keep-upright": {
"type": "boolean",
"default": true,
"requires": [
"text-field",
{ "text-rotation-alignment": "map" },
{ "symbol-placement": ["line", "line-center"] }
],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-transform": {
"type": "enum",
"values": {
"none": {},
"uppercase": {},
"lowercase": {}
},
"default": "none",
"requires": ["text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-offset": {
"type": "array",
"value": "number",
"units": "ems",
"length": 2,
"default": [0, 0],
"requires": ["text-field", { "!": "text-radial-offset" }],
"expression": {
"interpolated": true,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
"text-allow-overlap": {
"type": "boolean",
"default": false,
"requires": ["text-field", { "!": "text-overlap" }],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-overlap": {
"type": "enum",
"values": {
"never": {},
"always": {},
"cooperative": {}
},
"requires": ["text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-ignore-placement": {
"type": "boolean",
"default": false,
"requires": ["text-field"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-optional": {
"type": "boolean",
"default": false,
"requires": ["text-field", "icon-image"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
}
},
layout_raster: { "visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
} },
layout_hillshade: { "visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
} },
"layout_color-relief": { "visibility": {
"type": "enum",
"values": {
"visible": {},
"none": {}
},
"default": "visible",
"expression": {
"interpolated": false,
"parameters": ["global-state"]
},
"property-type": "data-constant"
} },
filter: {
"type": "boolean",
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "data-driven"
},
filter_operator: {
"type": "enum",
"values": {
"==": {},
"!=": {},
">": {},
">=": {},
"<": {},
"<=": {},
"in": {},
"!in": {},
"all": {},
"any": {},
"none": {},
"has": {},
"!has": {}
}
},
geometry_type: {
"type": "enum",
"values": {
"Point": {},
"LineString": {},
"Polygon": {}
}
},
"function": {
"expression": { "type": "expression" },
"stops": {
"type": "array",
"value": "function_stop"
},
"base": {
"type": "number",
"default": 1,
"minimum": 0
},
"property": {
"type": "string",
"default": "$zoom"
},
"type": {
"type": "enum",
"values": {
"identity": {},
"exponential": {},
"interval": {},
"categorical": {}
},
"default": "exponential"
},
"colorSpace": {
"type": "enum",
"values": {
"rgb": {},
"lab": {},
"hcl": {}
},
"default": "rgb"
},
"default": {
"type": "*",
"required": false
}
},
function_stop: {
"type": "array",
"minimum": 0,
"maximum": 24,
"value": ["number", "color"],
"length": 2
},
expression: {
"type": "array",
"value": "expression_name",
"minimum": 1
},
light: {
"anchor": {
"type": "enum",
"default": "viewport",
"values": {
"map": {},
"viewport": {}
},
"property-type": "data-constant",
"transition": false,
"expression": {
"interpolated": false,
"parameters": ["zoom"]
}
},
"position": {
"type": "array",
"default": [
1.15,
210,
30
],
"length": 3,
"value": "number",
"property-type": "data-constant",
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
}
},
"color": {
"type": "color",
"property-type": "data-constant",
"default": "#ffffff",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
},
"intensity": {
"type": "number",
"property-type": "data-constant",
"default": .5,
"minimum": 0,
"maximum": 1,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
}
},
sky: {
"sky-color": {
"type": "color",
"property-type": "data-constant",
"default": "#88C6FC",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
},
"horizon-color": {
"type": "color",
"property-type": "data-constant",
"default": "#ffffff",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
},
"fog-color": {
"type": "color",
"property-type": "data-constant",
"default": "#ffffff",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
},
"fog-ground-blend": {
"type": "number",
"property-type": "data-constant",
"default": .5,
"minimum": 0,
"maximum": 1,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
},
"horizon-fog-blend": {
"type": "number",
"property-type": "data-constant",
"default": .8,
"minimum": 0,
"maximum": 1,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
},
"sky-horizon-blend": {
"type": "number",
"property-type": "data-constant",
"default": .8,
"minimum": 0,
"maximum": 1,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
},
"atmosphere-blend": {
"type": "number",
"property-type": "data-constant",
"default": .8,
"minimum": 0,
"maximum": 1,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"transition": true
}
},
terrain: {
"source": {
"type": "string",
"required": true
},
"exaggeration": {
"type": "number",
"minimum": 0,
"default": 1
}
},
projection: { "type": {
"type": "projectionDefinition",
"default": "mercator",
"property-type": "data-constant",
"transition": false,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
}
} },
paint: [
"paint_fill",
"paint_line",
"paint_circle",
"paint_heatmap",
"paint_fill-extrusion",
"paint_symbol",
"paint_raster",
"paint_hillshade",
"paint_color-relief",
"paint_background"
],
paint_fill: {
"fill-antialias": {
"type": "boolean",
"default": true,
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"fill-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"fill-layer-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom", "global-state"]
},
"property-type": "data-constant"
},
"fill-color": {
"type": "color",
"default": "#000000",
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"fill-outline-color": {
"type": "color",
"transition": true,
"requires": [{ "!": "fill-pattern" }, { "fill-antialias": true }],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"fill-translate": {
"type": "array",
"value": "number",
"length": 2,
"default": [0, 0],
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"fill-translate-anchor": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "map",
"requires": ["fill-translate"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"fill-pattern": {
"type": "resolvedImage",
"transition": true,
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "cross-faded-data-driven"
}
},
"paint_fill-extrusion": {
"fill-extrusion-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"fill-extrusion-color": {
"type": "color",
"default": "#000000",
"transition": true,
"requires": [{ "!": "fill-extrusion-pattern" }],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"fill-extrusion-translate": {
"type": "array",
"value": "number",
"length": 2,
"default": [0, 0],
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"fill-extrusion-translate-anchor": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "map",
"requires": ["fill-extrusion-translate"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"fill-extrusion-pattern": {
"type": "resolvedImage",
"transition": true,
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "cross-faded-data-driven"
},
"fill-extrusion-height": {
"type": "number",
"default": 0,
"minimum": 0,
"units": "meters",
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"fill-extrusion-base": {
"type": "number",
"default": 0,
"minimum": 0,
"units": "meters",
"transition": true,
"requires": ["fill-extrusion-height"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"fill-extrusion-vertical-gradient": {
"type": "boolean",
"default": true,
"transition": false,
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
}
},
paint_line: {
"line-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"line-layer-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom", "global-state"]
},
"property-type": "data-constant"
},
"line-color": {
"type": "color",
"default": "#000000",
"transition": true,
"requires": [{ "!": "line-pattern" }],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"line-translate": {
"type": "array",
"value": "number",
"length": 2,
"default": [0, 0],
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"line-translate-anchor": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "map",
"requires": ["line-translate"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"line-width": {
"type": "number",
"default": 1,
"minimum": 0,
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"line-gap-width": {
"type": "number",
"default": 0,
"minimum": 0,
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"line-offset": {
"type": "number",
"default": 0,
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"line-blur": {
"type": "number",
"default": 0,
"minimum": 0,
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"line-dasharray": {
"type": "array",
"value": "number",
"minimum": 0,
"transition": true,
"units": "line widths",
"requires": [{ "!": "line-pattern" }],
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "cross-faded-data-driven"
},
"line-pattern": {
"type": "resolvedImage",
"transition": true,
"expression": {
"interpolated": false,
"parameters": ["zoom", "feature"]
},
"property-type": "cross-faded-data-driven"
},
"line-gradient": {
"type": "color",
"transition": false,
"requires": [
{ "!": "line-dasharray" },
{ "!": "line-pattern" },
{
"source": "geojson",
"has": { "lineMetrics": true }
}
],
"expression": {
"interpolated": true,
"parameters": ["line-progress"]
},
"property-type": "color-ramp"
}
},
paint_circle: {
"circle-radius": {
"type": "number",
"default": 5,
"minimum": 0,
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"circle-color": {
"type": "color",
"default": "#000000",
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"circle-blur": {
"type": "number",
"default": 0,
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"circle-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"circle-translate": {
"type": "array",
"value": "number",
"length": 2,
"default": [0, 0],
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"circle-translate-anchor": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "map",
"requires": ["circle-translate"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"circle-pitch-scale": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "map",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"circle-pitch-alignment": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "viewport",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"circle-stroke-width": {
"type": "number",
"default": 0,
"minimum": 0,
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"circle-stroke-color": {
"type": "color",
"default": "#000000",
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"circle-stroke-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
}
},
paint_heatmap: {
"heatmap-radius": {
"type": "number",
"default": 30,
"minimum": 1,
"transition": true,
"units": "pixels",
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"heatmap-weight": {
"type": "number",
"default": 1,
"minimum": 0,
"transition": false,
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"heatmap-intensity": {
"type": "number",
"default": 1,
"minimum": 0,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"heatmap-color": {
"type": "color",
"default": [
"interpolate",
["linear"],
["heatmap-density"],
0,
"rgba(0, 0, 255, 0)",
.1,
"royalblue",
.3,
"cyan",
.5,
"lime",
.7,
"yellow",
1,
"red"
],
"transition": false,
"expression": {
"interpolated": true,
"parameters": ["heatmap-density"]
},
"property-type": "color-ramp"
},
"heatmap-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
}
},
paint_symbol: {
"icon-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"icon-color": {
"type": "color",
"default": "#000000",
"transition": true,
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"icon-halo-color": {
"type": "color",
"default": "rgba(0, 0, 0, 0)",
"transition": true,
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"icon-halo-width": {
"type": "number",
"default": 0,
"minimum": 0,
"transition": true,
"units": "pixels",
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"icon-halo-blur": {
"type": "number",
"default": 0,
"minimum": 0,
"transition": true,
"units": "pixels",
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"icon-translate": {
"type": "array",
"value": "number",
"length": 2,
"default": [0, 0],
"transition": true,
"units": "pixels",
"requires": ["icon-image"],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"icon-translate-anchor": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "map",
"requires": ["icon-image", "icon-translate"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"text-color": {
"type": "color",
"default": "#000000",
"transition": true,
"overridable": true,
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"text-halo-color": {
"type": "color",
"default": "rgba(0, 0, 0, 0)",
"transition": true,
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"text-halo-width": {
"type": "number",
"default": 0,
"minimum": 0,
"transition": true,
"units": "pixels",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"text-halo-blur": {
"type": "number",
"default": 0,
"minimum": 0,
"transition": true,
"units": "pixels",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": [
"zoom",
"feature",
"feature-state"
]
},
"property-type": "data-driven"
},
"text-translate": {
"type": "array",
"value": "number",
"length": 2,
"default": [0, 0],
"transition": true,
"units": "pixels",
"requires": ["text-field"],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"text-translate-anchor": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "map",
"requires": ["text-field", "text-translate"],
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
}
},
paint_raster: {
"raster-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"raster-hue-rotate": {
"type": "number",
"default": 0,
"period": 360,
"transition": true,
"units": "degrees",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"raster-brightness-min": {
"type": "number",
"default": 0,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"raster-brightness-max": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"raster-saturation": {
"type": "number",
"default": 0,
"minimum": -1,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"raster-contrast": {
"type": "number",
"default": 0,
"minimum": -1,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"resampling": {
"type": "enum",
"values": {
"linear": {},
"nearest": {}
},
"default": "linear",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"raster-resampling": {
"type": "enum",
"values": {
"linear": {},
"nearest": {}
},
"default": "linear",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"raster-fade-duration": {
"type": "number",
"default": 300,
"minimum": 0,
"transition": false,
"units": "milliseconds",
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
}
},
paint_hillshade: {
"hillshade-illumination-direction": {
"type": "numberArray",
"default": 335,
"minimum": 0,
"maximum": 359,
"transition": false,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"hillshade-illumination-altitude": {
"type": "numberArray",
"default": 45,
"minimum": 0,
"maximum": 90,
"transition": false,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"hillshade-illumination-anchor": {
"type": "enum",
"values": {
"map": {},
"viewport": {}
},
"default": "viewport",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"hillshade-exaggeration": {
"type": "number",
"default": .5,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"hillshade-shadow-color": {
"type": "colorArray",
"default": "#000000",
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"hillshade-highlight-color": {
"type": "colorArray",
"default": "#FFFFFF",
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"hillshade-accent-color": {
"type": "color",
"default": "#000000",
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"hillshade-method": {
"type": "enum",
"values": {
"standard": {},
"basic": {},
"combined": {},
"igor": {},
"multidirectional": {}
},
"default": "standard",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"resampling": {
"type": "enum",
"values": {
"linear": {},
"nearest": {}
},
"default": "linear",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
}
},
"paint_color-relief": {
"color-relief-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"color-relief-color": {
"type": "color",
"transition": false,
"expression": {
"interpolated": true,
"parameters": ["elevation"]
},
"property-type": "color-ramp"
},
"resampling": {
"type": "enum",
"values": {
"linear": {},
"nearest": {}
},
"default": "linear",
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "data-constant"
}
},
paint_background: {
"background-color": {
"type": "color",
"default": "#000000",
"transition": true,
"requires": [{ "!": "background-pattern" }],
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
},
"background-pattern": {
"type": "resolvedImage",
"transition": true,
"expression": {
"interpolated": false,
"parameters": ["zoom"]
},
"property-type": "cross-faded"
},
"background-opacity": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 1,
"transition": true,
"expression": {
"interpolated": true,
"parameters": ["zoom"]
},
"property-type": "data-constant"
}
},
transition: {
"duration": {
"type": "number",
"default": 300,
"minimum": 0,
"units": "milliseconds"
},
"delay": {
"type": "number",
"default": 0,
"minimum": 0,
"units": "milliseconds"
}
},
"property-type": {
"data-driven": { "type": "property-type" },
"cross-faded": { "type": "property-type" },
"cross-faded-data-driven": { "type": "property-type" },
"color-ramp": { "type": "property-type" },
"data-constant": { "type": "property-type" },
"constant": { "type": "property-type" }
},
promoteId: { "*": { "type": "string" } },
interpolation: {
"type": "array",
"value": "interpolation_name",
"minimum": 1
},
interpolation_name: {
"type": "enum",
"values": {
"linear": { "syntax": {
"overloads": [{
"parameters": [],
"output-type": "interpolation"
}],
"parameters": []
} },
"exponential": { "syntax": {
"overloads": [{
"parameters": ["base"],
"output-type": "interpolation"
}],
"parameters": [{
"name": "base",
"type": "number literal"
}]
} },
"cubic-bezier": { "syntax": {
"overloads": [{
"parameters": [
"x1",
"y1",
"x2",
"y2"
],
"output-type": "interpolation"
}],
"parameters": [
{
"name": "x1",
"type": "number literal"
},
{
"name": "y1",
"type": "number literal"
},
{
"name": "x2",
"type": "number literal"
},
{
"name": "y2",
"type": "number literal"
}
]
} }
}
}
};
const refProperties = [
"type",
"source",
"source-layer",
"minzoom",
"maxzoom",
"filter",
"layout"
];
function deref(layer, parent) {
const result = {};
for (const k in layer) if (k !== "ref") result[k] = layer[k];
refProperties.forEach((k) => {
if (k in parent) result[k] = parent[k];
});
return result;
}
/**
*
* The input is not modified. The output may contain references to portions
* of the input.
*
* @param layers - array of layers, some of which may contain `ref` properties
* whose value is the `id` of another property
* @returns a new array where such layers have been augmented with the 'type', 'source', etc. properties
* from the parent layer, and the `ref` property has been removed.
*/
function derefLayers(layers) {
layers = layers.slice();
const map = Object.create(null);
for (let i = 0; i < layers.length; i++) map[layers[i].id] = layers[i];
for (let i = 0; i < layers.length; i++) if ("ref" in layers[i]) layers[i] = deref(layers[i], map[layers[i].ref]);
return layers;
}
/**
* Deeply compares two object literals.
*
* @private
*/
function deepEqual(a, b) {
if (Array.isArray(a)) {
if (!Array.isArray(b) || a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false;
return true;
}
if (typeof a === "object" && a !== null && b !== null) {
if (!(typeof b === "object")) return false;
if (Object.keys(a).length !== Object.keys(b).length) return false;
for (const key in a) if (!deepEqual(a[key], b[key])) return false;
return true;
}
return a === b;
}
/**
* The main reason for this method is to allow type check when adding a command to the array.
* @param commands - The commands array to add to
* @param command - The command to add
*/
function addCommand(commands, command) {
commands.push(command);
}
function addSource(sourceId, after, commands) {
addCommand(commands, {
command: "addSource",
args: [sourceId, after[sourceId]]
});
}
function removeSource(sourceId, commands, sourcesRemoved) {
addCommand(commands, {
command: "removeSource",
args: [sourceId]
});
sourcesRemoved[sourceId] = true;
}
function updateSource(sourceId, after, commands, sourcesRemoved) {
removeSource(sourceId, commands, sourcesRemoved);
addSource(sourceId, after, commands);
}
function canUpdateGeoJSON(before, after, sourceId) {
let prop;
for (prop in before[sourceId]) {
if (!Object.prototype.hasOwnProperty.call(before[sourceId], prop)) continue;
if (prop !== "data" && !deepEqual(before[sourceId][prop], after[sourceId][prop])) return false;
}
for (prop in after[sourceId]) {
if (!Object.prototype.hasOwnProperty.call(after[sourceId], prop)) continue;
if (prop !== "data" && !deepEqual(before[sourceId][prop], after[sourceId][prop])) return false;
}
return true;
}
function diffSources(before, after, commands, sourcesRemoved) {
before = before || {};
after = after || {};
let sourceId;
for (sourceId in before) {
if (!Object.prototype.hasOwnProperty.call(before, sourceId)) continue;
if (!Object.prototype.hasOwnProperty.call(after, sourceId)) removeSource(sourceId, commands, sourcesRemoved);
}
for (sourceId in after) {
if (!Object.prototype.hasOwnProperty.call(after, sourceId)) continue;
if (!Object.prototype.hasOwnProperty.call(before, sourceId)) addSource(sourceId, after, commands);
else if (!deepEqual(before[sourceId], after[sourceId])) if (before[sourceId].type === "geojson" && after[sourceId].type === "geojson" && canUpdateGeoJSON(before, after, sourceId)) addCommand(commands, {
command: "setGeoJSONSourceData",
args: [sourceId, after[sourceId].data]
});
else updateSource(sourceId, after, commands, sourcesRemoved);
}
}
function diffLayerPropertyChanges(before, after, commands, layerId, klass, command) {
before = before || {};
after = after || {};
for (const prop in before) {
if (!Object.prototype.hasOwnProperty.call(before, prop)) continue;
if (!deepEqual(before[prop], after[prop])) commands.push({
command,
args: [
layerId,
prop,
after[prop],
klass
]
});
}
for (const prop in after) {
if (!Object.prototype.hasOwnProperty.call(after, prop) || Object.prototype.hasOwnProperty.call(before, prop)) continue;
if (!deepEqual(before[prop], after[prop])) commands.push({
command,
args: [
layerId,
prop,
after[prop],
klass
]
});
}
}
function pluckId(layer) {
return layer.id;
}
function indexById(group, layer) {
group[layer.id] = layer;
return group;
}
function diffLayers(before, after, commands) {
before = before || [];
after = after || [];
const beforeOrder = before.map(pluckId);
const afterOrder = after.map(pluckId);
const beforeIndex = before.reduce(indexById, {});
const afterIndex = after.reduce(indexById, {});
const tracker = beforeOrder.slice();
const clean = Object.create(null);
let layerId;
let beforeLayer;
let afterLayer;
let insertBeforeLayerId;
let prop;
for (let i = 0, d = 0; i < beforeOrder.length; i++) {
layerId = beforeOrder[i];
if (!Object.prototype.hasOwnProperty.call(afterIndex, layerId)) {
addCommand(commands, {
command: "removeLayer",
args: [layerId]
});
tracker.splice(tracker.indexOf(layerId, d), 1);
} else d++;
}
for (let i = 0, d = 0; i < afterOrder.length; i++) {
layerId = afterOrder[afterOrder.length - 1 - i];
if (tracker[tracker.length - 1 - i] === layerId) continue;
if (Object.prototype.hasOwnProperty.call(beforeIndex, layerId)) {
addCommand(commands, {
command: "removeLayer",
args: [layerId]
});
tracker.splice(tracker.lastIndexOf(layerId, tracker.length - d), 1);
} else d++;
insertBeforeLayerId = tracker[tracker.length - i];
addCommand(commands, {
command: "addLayer",
args: [afterIndex[layerId], insertBeforeLayerId]
});
tracker.splice(tracker.length - i, 0, layerId);
clean[layerId] = true;
}
for (let i = 0; i < afterOrder.length; i++) {
layerId = afterOrder[i];
beforeLayer = beforeIndex[layerId];
afterLayer = afterIndex[layerId];
if (clean[layerId] || deepEqual(beforeLayer, afterLayer)) continue;
if (!deepEqual(beforeLayer.source, afterLayer.source) || !deepEqual(beforeLayer["source-layer"], afterLayer["source-layer"]) || !deepEqual(beforeLayer.type, afterLayer.type)) {
addCommand(commands, {
command: "removeLayer",
args: [layerId]
});
insertBeforeLayerId = tracker[tracker.lastIndexOf(layerId) + 1];
addCommand(commands, {
command: "addLayer",
args: [afterLayer, insertBeforeLayerId]
});
continue;
}
diffLayerPropertyChanges(beforeLayer.layout, afterLayer.layout, commands, layerId, null, "setLayoutProperty");
diffLayerPropertyChanges(beforeLayer.paint, afterLayer.paint, commands, layerId, null, "setPaintProperty");
if (!deepEqual(beforeLayer.filter, afterLayer.filter)) addCommand(commands, {
command: "setFilter",
args: [layerId, afterLayer.filter]
});
if (!deepEqual(beforeLayer.minzoom, afterLayer.minzoom) || !deepEqual(beforeLayer.maxzoom, afterLayer.maxzoom)) addCommand(commands, {
command: "setLayerZoomRange",
args: [
layerId,
afterLayer.minzoom,
afterLayer.maxzoom
]
});
for (prop in beforeLayer) {
if (!Object.prototype.hasOwnProperty.call(beforeLayer, prop)) continue;
if (prop === "layout" || prop === "paint" || prop === "filter" || prop === "metadata" || prop === "minzoom" || prop === "maxzoom") continue;
if (prop.indexOf("paint.") === 0) diffLayerPropertyChanges(beforeLayer[prop], afterLayer[prop], commands, layerId, prop.slice(6), "setPaintProperty");
else if (!deepEqual(beforeLayer[prop], afterLayer[prop])) addCommand(commands, {
command: "setLayerProperty",
args: [
layerId,
prop,
afterLayer[prop]
]
});
}
for (prop in afterLayer) {
if (!Object.prototype.hasOwnProperty.call(afterLayer, prop) || Object.prototype.hasOwnProperty.call(beforeLayer, prop)) continue;
if (prop === "layout" || prop === "paint" || prop === "filter" || prop === "metadata" || prop === "minzoom" || prop === "maxzoom") continue;
if (prop.indexOf("paint.") === 0) diffLayerPropertyChanges(beforeLayer[prop], afterLayer[prop], commands, layerId, prop.slice(6), "setPaintProperty");
else if (!deepEqual(beforeLayer[prop], afterLayer[prop])) addCommand(commands, {
command: "setLayerProperty",
args: [
layerId,
prop,
afterLayer[prop]
]
});
}
}
}
/**
* Diff two stylesheet
*
* Creates semanticly aware diffs that can easily be applied at runtime.
* Operations produced by the diff closely resemble the maplibre-gl-js API. Any
* error creating the diff will fall back to the 'setStyle' operation.
*
* Example diff:
* [
* { command: 'setConstant', args: ['@water', '#0000FF'] },
* { command: 'setPaintProperty', args: ['background', 'background-color', 'black'] }
* ]
*
* @private
* @param {*} [before] stylesheet to compare from
* @param {*} after stylesheet to compare to
* @returns Array list of changes
*/
function diff(before, after) {
if (!before) return [{
command: "setStyle",
args: [after]
}];
let commands = [];
try {
if (!deepEqual(before.version, after.version)) return [{
command: "setStyle",
args: [after]
}];
if (!deepEqual(before.center, after.center)) commands.push({
command: "setCenter",
args: [after.center]
});
if (!deepEqual(before.state, after.state)) commands.push({
command: "setGlobalState",
args: [after.state]
});
if (!deepEqual(before.centerAltitude, after.centerAltitude)) commands.push({
command: "setCenterAltitude",
args: [after.centerAltitude]
});
if (!deepEqual(before.zoom, after.zoom)) commands.push({
command: "setZoom",
args: [after.zoom]
});
if (!deepEqual(before.bearing, after.bearing)) commands.push({
command: "setBearing",
args: [after.bearing]
});
if (!deepEqual(before.pitch, after.pitch)) commands.push({
command: "setPitch",
args: [after.pitch]
});
if (!deepEqual(before.roll, after.roll)) commands.push({
command: "setRoll",
args: [after.roll]
});
if (!deepEqual(before.sprite, after.sprite)) commands.push({
command: "setSprite",
args: [after.sprite]
});
if (!deepEqual(before.glyphs, after.glyphs)) commands.push({
command: "setGlyphs",
args: [after.glyphs]
});
if (!deepEqual(before.transition, after.transition)) commands.push({
command: "setTransition",
args: [after.transition]
});
if (!deepEqual(before.light, after.light)) commands.push({
command: "setLight",
args: [after.light]
});
if (!deepEqual(before.terrain, after.terrain)) commands.push({
command: "setTerrain",
args: [after.terrain]
});
if (!deepEqual(before.sky, after.sky)) commands.push({
command: "setSky",
args: [after.sky]
});
if (!deepEqual(before.projection, after.projection)) commands.push({
command: "setProjection",
args: [after.projection]
});
const sourcesRemoved = {};
const removeOrAddSourceCommands = [];
diffSources(before.sources, after.sources, removeOrAddSourceCommands, sourcesRemoved);
const beforeLayers = [];
if (before.layers) before.layers.forEach((layer) => {
if ("source" in layer && sourcesRemoved[layer.source]) commands.push({
command: "removeLayer",
args: [layer.id]
});
else beforeLayers.push(layer);
});
commands = commands.concat(removeOrAddSourceCommands);
diffLayers(beforeLayers, after.layers, commands);
} catch (e) {
console.warn("Unable to compute style diff:", e);
commands = [{
command: "setStyle",
args: [after]
}];
}
return commands;
}
var ValidationError = class {
constructor(key, value, message, identifier, severity = "error") {
this.message = (key ? `${key}: ` : "") + message;
if (identifier) this.identifier = identifier;
this.severity = severity;
if (value !== null && value !== void 0 && value.__line__) this.line = value.__line__;
}
};
var ExpressionParsingError = class extends Error {
constructor(key, message) {
super(message);
this.message = message;
this.key = key;
}
};
/**
* 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;
}
};
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;
}
const Xn = .96422;
const Yn = 1;
const Zn = .82521;
const t0 = 4 / 29;
const t1 = 6 / 29;
const t2 = 3 * t1 * t1;
const t3 = t1 * t1 * t1;
const deg2rad = Math.PI / 180;
const 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
];
}
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;
}
/**
* 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$1(+r / maxValue, 0, 1),
clamp$1(+g / maxValue, 0, 1),
clamp$1(+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$1(+s, 0, 100),
clamp$1(+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$1(asPercentage ? a / 100 : a, 0, 1);
}
function clamp$1(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
]
};
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);
});
}
/**
* Checks whether the specified color space is one of the supported interpolation color spaces.
*
* @param colorSpace Color space key to verify.
* @returns `true` if the specified color space is one of the supported
* interpolation color spaces, `false` otherwise
*/
function isSupportedInterpolationColorSpace(colorSpace) {
return colorSpace === "rgb" || colorSpace === "hcl" || colorSpace === "lab";
}
/**
* 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);
}
}
}
};
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;
}
};
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("");
}
};
/**
* 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]
];
}
return new Padding(input);
}
toString() {
return JSON.stringify(this.values);
}
static interpolate(from, to, t) {
return new Padding(interpolateArray(from.values, to.values, t));
}
};
/**
* 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));
}
};
/**
* 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);
}
};
var RuntimeError = class extends Error {
constructor(message, path) {
super(message);
this.name = "RuntimeError";
this.path = path;
}
toJSON() {
return this.message;
}
};
/** 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, key) {
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()}`, key);
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]}`, key);
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);
}
};
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
});
}
};
var ProjectionDefinition = class ProjectionDefinition {
constructor(from, to, transition) {
this.from = from;
this.to = to;
this.transition = transition;
}
toString() {
if (this.from === this.to && this.transition === 1) return this.from;
return JSON.stringify([
this.from,
this.to,
this.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);
}
};
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);
}
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;
}
};
const types$1 = {
string: StringType,
number: NumberType,
boolean: BooleanType,
object: ObjectType
};
var Assertion = class Assertion {
constructor(type, args, key) {
this.type = type;
this.args = args;
this.key = key;
}
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, context.key);
}
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.`, this.key);
}
throw new Error();
}
eachChild(fn) {
this.args.forEach(fn);
}
outputDefined() {
return this.args.every((arg) => arg.outputDefined());
}
};
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, key) {
this.type = type;
this.args = args;
this.key = key;
}
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, context.key);
}
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)}'`, this.key);
}
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)}'`, this.key);
}
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)}'`, this.key);
}
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)}'`, this.key);
}
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)}'`, this.key);
}
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.`, this.key);
}
case "formatted": return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
case "resolvedImage": return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
case "projectionDefinition": {
const input = this.args[0].evaluate(ctx);
if (ProjectionDefinition.parse(input)) return input;
throw new RuntimeError(`Could not parse projectionDefinition from value '${typeof input === "string" ? input : JSON.stringify(input)}'`, this.key);
}
default: return valueToString(this.args[0].evaluate(ctx));
}
}
eachChild(fn) {
this.args.forEach(fn);
}
outputDefined() {
return this.args.every((arg) => arg.outputDefined());
}
};
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 ? this.feature.geometry : null;
}
canonicalID() {
return this.canonical;
}
properties() {
return this.feature && this.feature.properties || {};
}
parseColor(input) {
let cached = this._parseColorCache.get(input);
if (!cached) {
cached = Color.parse(input);
this._parseColorCache.set(input, cached);
}
return cached;
}
};
/**
* State associated parsing at a given point in an expression tree.
* @private
*/
var ParsingContext = class ParsingContext {
constructor(registry, isConstantFunc, path = [], expectedType, scope = new Scope(), errors = []) {
this.registry = registry;
this.path = path;
this.key = path.map((part) => `[${part}]`).join("");
this.scope = scope;
this.errors = errors;
this.expectedType = expectedType;
this._isConstant = isConstantFunc;
}
/**
* @param expr the JSON expression to parse
* @param index the optional argument index if this expression is an argument of a parent expression that's being parsed
* @param options
* @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.
* @private
*/
parse(expr, index, expectedType, bindings, options = {}) {
if (index) return this.concat(index, expectedType, bindings)._parse(expr, options);
return this._parse(expr, options);
}
_parse(expr, options) {
if (expr === null || typeof expr === "string" || typeof expr === "boolean" || typeof expr === "number") expr = ["literal", expr];
const key = this.key;
function annotate(parsed, type, typeAnnotation) {
if (typeAnnotation === "assert") return new Assertion(type, [parsed], key);
else if (typeAnnotation === "coerce") return new Coercion(type, [parsed], key);
else return parsed;
}
if (Array.isArray(expr)) {
if (expr.length === 0) return this.error("Expected an array with at least one element. If you wanted a literal array, use [\"literal\", []].");
const op = expr[0];
if (typeof op !== "string") {
this.error(`Expression name must be a string, but found ${typeof op} instead. If you wanted a literal array, use ["literal", [...]].`, 0);
return null;
}
const Expr = this.registry[op];
if (Expr) {
let parsed = Expr.parse(expr, this);
if (!parsed) return null;
if (this.expectedType) {
const expected = this.expectedType;
const actual = parsed.type;
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");
else if ("projectionDefinition" === expected.kind && [
"string",
"array",
"value"
].includes(actual.kind) || [
"color",
"formatted",
"resolvedImage"
].includes(expected.kind) && ["value", "string"].includes(actual.kind) || ["padding", "numberArray"].includes(expected.kind) && [
"value",
"number",
"array"
].includes(actual.kind) || "colorArray" === expected.kind && [
"value",
"string",
"array"
].includes(actual.kind) || "variableAnchorOffsetCollection" === expected.kind && ["value", "array"].includes(actual.kind)) parsed = annotate(parsed, expected, options.typeAnnotation || "coerce");
else if (this.checkSubtype(expected, actual)) return null;
}
if (!(parsed instanceof Literal) && parsed.type.kind !== "resolvedImage" && this._isConstant(parsed)) {
const ec = new EvaluationContext();
try {
parsed = new Literal(parsed.type, parsed.evaluate(ec));
} catch (e) {
this.error(e.message);
return null;
}
}
return parsed;
}
return this.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
} else if (typeof expr === "undefined") return this.error("'undefined' value invalid. Use null instead.");
else if (typeof expr === "object") return this.error("Bare objects invalid. Use [\"literal\", {...}] instead.");
else return this.error(`Expected an array, but found ${typeof expr} instead.`);
}
/**
* Returns a copy of this context suitable for parsing the subexpression at
* index `index`, optionally appending to 'let' binding map.
*
* Note that `errors` property, intended for collecting errors while
* parsing, is copied by reference rather than cloned.
* @private
*/
concat(index, expectedType, bindings) {
const path = typeof index === "number" ? this.path.concat(index) : this.path;
const scope = bindings ? this.scope.concat(bindings) : this.scope;
return new ParsingContext(this.registry, this._isConstant, path, expectedType || null, scope, this.errors);
}
/**
* Push a parsing (or type checking) error into the `this.errors`
* @param error The message
* @param keys Optionally specify the source of the error at a child
* of the current expression at `this.key`.
* @private
*/
error(error, ...keys) {
const key = `${this.key}${keys.map((k) => `[${k}]`).join("")}`;
this.errors.push(new ExpressionParsingError(key, error));
}
/**
* Returns null if `t` is a subtype of `expected`; otherwise returns an
* error message and also pushes it to `this.errors`.
* @param expected The expected type
* @param t The actual type
* @returns null if `t` is a subtype of `expected`; otherwise returns an error message
*/
checkSubtype(expected, t) {
const error = checkSubtype(expected, t);
if (error) this.error(error);
return error;
}
};
var Let = class Let {
constructor(bindings, result) {
this.type = result.type;
this.bindings = [].concat(bindings);
this.result = result;
}
evaluate(ctx) {
return this.result.evaluate(ctx);
}
eachChild(fn) {
for (const binding of this.bindings) fn(binding[1]);
fn(this.result);
}
static parse(args, context) {
if (args.length < 4) return context.error(`Expected at least 3 arguments, but found ${args.length - 1} instead.`);
const bindings = [];
for (let i = 1; i < args.length - 1; i += 2) {
const name = args[i];
if (typeof name !== "string") return context.error(`Expected string, but found ${typeof name} instead.`, i);
if (/[^a-zA-Z0-9_]/.test(name)) return context.error("Variable names must contain only alphanumeric characters or '_'.", i);
const value = context.parse(args[i + 1], i + 1);
if (!value) return null;
bindings.push([name, value]);
}
const result = context.parse(args[args.length - 1], args.length - 1, context.expectedType, bindings);
if (!result) return null;
return new Let(bindings, result);
}
outputDefined() {
return this.result.outputDefined();
}
};
var Var = class Var {
constructor(name, boundExpression) {
this.type = boundExpression.type;
this.name = name;
this.boundExpression = boundExpression;
}
static parse(args, context) {
if (args.length !== 2 || typeof args[1] !== "string") return context.error("'var' expression requires exactly one string literal argument.");
const name = args[1];
if (!context.scope.has(name)) return context.error(`Unknown variable "${name}". Make sure "${name}" has been bound in an enclosing "let" expression before using it.`, 1);
return new Var(name, context.scope.get(name));
}
evaluate(ctx) {
return this.boundExpression.evaluate(ctx);
}
eachChild() {}
outputDefined() {
return false;
}
};
var At = class At {
constructor(type, index, input, key) {
this.type = type;
this.index = index;
this.input = input;
this.key = key;
}
static parse(args, context) {
if (args.length !== 3) return context.error(`Expected 2 arguments, but found ${args.length - 1} instead.`);
const index = context.parse(args[1], 1, NumberType);
const input = context.parse(args[2], 2, array(context.expectedType || ValueType));
if (!index || !input) return null;
const t = input.type;
return new At(t.itemType, index, input, context.key);
}
evaluate(ctx) {
const index = this.index.evaluate(ctx);
const array = this.input.evaluate(ctx);
if (index < 0) throw new RuntimeError(`Array index out of bounds: ${index} < 0.`, this.key);
if (index >= array.length) throw new RuntimeError(`Array index out of bounds: ${index} > ${array.length - 1}.`, this.key);
if (index !== Math.floor(index)) throw new RuntimeError(`Array index must be an integer, but found ${index} instead.`, this.key);
return array[index];
}
eachChild(fn) {
fn(this.index);
fn(this.input);
}
outputDefined() {
return false;
}
};
var In = class In {
constructor(needle, haystack, key) {
this.needle = needle;
this.haystack = haystack;
this.key = key;
this.type = BooleanType;
}
static parse(args, context) {
if (args.length !== 3) return context.error(`Expected 2 arguments, but found ${args.length - 1} instead.`);
const needle = context.parse(args[1], 1, ValueType);
const haystack = context.parse(args[2], 2, ValueType);
if (!needle || !haystack) return null;
if (!isValidType(needle.type, [
BooleanType,
StringType,
NumberType,
NullType,
ValueType
])) return context.error(`Expected first argument to be of type boolean, string, number or null, but found ${typeToString(needle.type)} instead`);
return new In(needle, haystack, context.key);
}
evaluate(ctx) {
const needle = this.needle.evaluate(ctx);
const haystack = this.haystack.evaluate(ctx);
if (!haystack) return false;
if (!isValidNativeType(needle, [
"boolean",
"string",
"number",
"null"
])) throw new RuntimeError(`Expected first argument to be of type boolean, string, number or null, but found ${typeToString(typeOf(needle))} instead.`, this.key);
if (!isValidNativeType(haystack, ["string", "array"])) throw new RuntimeError(`Expected second argument to be of type array or string, but found ${typeToString(typeOf(haystack))} instead.`, this.key);
return haystack.indexOf(needle) >= 0;
}
eachChild(fn) {
fn(this.needle);
fn(this.haystack);
}
outputDefined() {
return true;
}
};
var IndexOf = class IndexOf {
constructor(needle, haystack, key, fromIndex) {
this.needle = needle;
this.haystack = haystack;
this.key = key;
this.fromIndex = fromIndex;
this.type = NumberType;
}
static parse(args, context) {
if (args.length <= 2 || args.length >= 5) return context.error(`Expected 2 or 3 arguments, but found ${args.length - 1} instead.`);
const needle = context.parse(args[1], 1, ValueType);
const haystack = context.parse(args[2], 2, ValueType);
if (!needle || !haystack) return null;
if (!isValidType(needle.type, [
BooleanType,
StringType,
NumberType,
NullType,
ValueType
])) return context.error(`Expected first argument to be of type boolean, string, number or null, but found ${typeToString(needle.type)} instead`);
if (args.length === 4) {
const fromIndex = context.parse(args[3], 3, NumberType);
if (!fromIndex) return null;
return new IndexOf(needle, haystack, context.key, fromIndex);
} else return new IndexOf(needle, haystack, context.key);
}
evaluate(ctx) {
const needle = this.needle.evaluate(ctx);
const haystack = this.haystack.evaluate(ctx);
if (!isValidNativeType(needle, [
"boolean",
"string",
"number",
"null"
])) throw new RuntimeError(`Expected first argument to be of type boolean, string, number or null, but found ${typeToString(typeOf(needle))} instead.`, this.key);
let fromIndex;
if (this.fromIndex) fromIndex = this.fromIndex.evaluate(ctx);
if (isValidNativeType(haystack, ["string"])) {
const rawIndex = haystack.indexOf(needle, fromIndex);
if (rawIndex === -1) return -1;
else return [...haystack.slice(0, rawIndex)].length;
} else if (isValidNativeType(haystack, ["array"])) return haystack.indexOf(needle, fromIndex);
else throw new RuntimeError(`Expected second argument to be of type array or string, but found ${typeToString(typeOf(haystack))} instead.`, this.key);
}
eachChild(fn) {
fn(this.needle);
fn(this.haystack);
if (this.fromIndex) fn(this.fromIndex);
}
outputDefined() {
return false;
}
};
var Match = class Match {
constructor(inputType, outputType, input, cases, outputs, otherwise) {
this.inputType = inputType;
this.type = outputType;
this.input = input;
this.cases = cases;
this.outputs = outputs;
this.otherwise = otherwise;
}
static parse(args, context) {
if (args.length < 5) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
if (args.length % 2 !== 1) return context.error("Expected an even number of arguments.");
let inputType;
let outputType;
if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
const cases = {};
const outputs = [];
for (let i = 2; i < args.length - 1; i += 2) {
let labels = args[i];
const value = args[i + 1];
if (!Array.isArray(labels)) labels = [labels];
const labelContext = context.concat(i);
if (labels.length === 0) return labelContext.error("Expected at least one branch label.");
for (const label of labels) {
if (typeof label !== "number" && typeof label !== "string") return labelContext.error("Branch labels must be numbers or strings.");
else if (typeof label === "number" && Math.abs(label) > Number.MAX_SAFE_INTEGER) return labelContext.error(`Branch labels must be integers no larger than ${Number.MAX_SAFE_INTEGER}.`);
else if (typeof label === "number" && Math.floor(label) !== label) return labelContext.error("Numeric branch labels must be integer values.");
else if (!inputType) inputType = typeOf(label);
else if (labelContext.checkSubtype(inputType, typeOf(label))) return null;
if (typeof cases[String(label)] !== "undefined") return labelContext.error("Branch labels must be unique.");
cases[String(label)] = outputs.length;
}
const result = context.parse(value, i, outputType);
if (!result) return null;
outputType = outputType || result.type;
outputs.push(result);
}
const input = context.parse(args[1], 1, ValueType);
if (!input) return null;
const otherwise = context.parse(args[args.length - 1], args.length - 1, outputType);
if (!otherwise) return null;
if (input.type.kind !== "value" && context.concat(1).checkSubtype(inputType, input.type)) return null;
return new Match(inputType, outputType, input, cases, outputs, otherwise);
}
evaluate(ctx) {
const input = this.input.evaluate(ctx);
return (typeOf(input) === this.inputType && this.outputs[this.cases[input]] || this.otherwise).evaluate(ctx);
}
eachChild(fn) {
fn(this.input);
this.outputs.forEach(fn);
fn(this.otherwise);
}
outputDefined() {
return this.outputs.every((out) => out.outputDefined()) && this.otherwise.outputDefined();
}
};
var Case = class Case {
constructor(type, branches, otherwise) {
this.type = type;
this.branches = branches;
this.otherwise = otherwise;
}
static parse(args, context) {
if (args.length < 4) return context.error(`Expected at least 3 arguments, but found only ${args.length - 1}.`);
if (args.length % 2 !== 0) return context.error("Expected an odd number of arguments.");
let outputType;
if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
const branches = [];
for (let i = 1; i < args.length - 1; i += 2) {
const test = context.parse(args[i], i, BooleanType);
if (!test) return null;
const result = context.parse(args[i + 1], i + 1, outputType);
if (!result) return null;
branches.push([test, result]);
outputType = outputType || result.type;
}
const otherwise = context.parse(args[args.length - 1], args.length - 1, outputType);
if (!otherwise) return null;
if (!outputType) throw new Error("Can't infer output type");
return new Case(outputType, branches, otherwise);
}
evaluate(ctx) {
for (const [test, expression] of this.branches) if (test.evaluate(ctx)) return expression.evaluate(ctx);
return this.otherwise.evaluate(ctx);
}
eachChild(fn) {
for (const [test, expression] of this.branches) {
fn(test);
fn(expression);
}
fn(this.otherwise);
}
outputDefined() {
return this.branches.every(([_, out]) => out.outputDefined()) && this.otherwise.outputDefined();
}
};
var Slice = class Slice {
constructor(type, input, beginIndex, key, endIndex) {
this.type = type;
this.input = input;
this.beginIndex = beginIndex;
this.key = key;
this.endIndex = endIndex;
}
static parse(args, context) {
if (args.length <= 2 || args.length >= 5) return context.error(`Expected 2 or 3 arguments, but found ${args.length - 1} instead.`);
const input = context.parse(args[1], 1, ValueType);
const beginIndex = context.parse(args[2], 2, NumberType);
if (!input || !beginIndex) return null;
if (!isValidType(input.type, [
array(ValueType),
StringType,
ValueType
])) return context.error(`Expected first argument to be of type array or string, but found ${typeToString(input.type)} instead`);
if (args.length === 4) {
const endIndex = context.parse(args[3], 3, NumberType);
if (!endIndex) return null;
return new Slice(input.type, input, beginIndex, context.key, endIndex);
} else return new Slice(input.type, input, beginIndex, context.key);
}
evaluate(ctx) {
const input = this.input.evaluate(ctx);
const beginIndex = this.beginIndex.evaluate(ctx);
let endIndex;
if (this.endIndex) endIndex = this.endIndex.evaluate(ctx);
if (isValidNativeType(input, ["string"])) return [...input].slice(beginIndex, endIndex).join("");
else if (isValidNativeType(input, ["array"])) return input.slice(beginIndex, endIndex);
else throw new RuntimeError(`Expected first argument to be of type array or string, but found ${typeToString(typeOf(input))} instead.`, this.key);
}
eachChild(fn) {
fn(this.input);
fn(this.beginIndex);
if (this.endIndex) fn(this.endIndex);
}
outputDefined() {
return false;
}
};
/**
* Returns the index of the last stop <= input, or 0 if it doesn't exist.
* @private
*/
function findStopLessThanOrEqualTo(stops, input, key) {
const lastIndex = stops.length - 1;
let lowerIndex = 0;
let upperIndex = lastIndex;
let currentIndex = 0;
let currentValue, nextValue;
while (lowerIndex <= upperIndex) {
currentIndex = Math.floor((lowerIndex + upperIndex) / 2);
currentValue = stops[currentIndex];
nextValue = stops[currentIndex + 1];
if (currentValue <= input) {
if (currentIndex === lastIndex || input < nextValue) return currentIndex;
lowerIndex = currentIndex + 1;
} else if (currentValue > input) upperIndex = currentIndex - 1;
else throw new RuntimeError("Input is not a number.", key);
}
return 0;
}
var Step = class Step {
constructor(type, input, stops, key) {
this.type = type;
this.input = input;
this.key = key;
this.labels = [];
this.outputs = [];
for (const [label, expression] of stops) {
this.labels.push(label);
this.outputs.push(expression);
}
}
static parse(args, context) {
if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
const input = context.parse(args[1], 1, NumberType);
if (!input) return null;
const stops = [];
let outputType = null;
if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
for (let i = 1; i < args.length; i += 2) {
const label = i === 1 ? -Infinity : args[i];
const value = args[i + 1];
const labelKey = i;
const valueKey = i + 1;
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);
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);
const parsed = context.parse(value, valueKey, outputType);
if (!parsed) return null;
outputType = outputType || parsed.type;
stops.push([label, parsed]);
}
return new Step(outputType, input, stops, context.key);
}
evaluate(ctx) {
const labels = this.labels;
const outputs = this.outputs;
if (labels.length === 1) return outputs[0].evaluate(ctx);
const value = this.input.evaluate(ctx);
if (value <= labels[0]) return outputs[0].evaluate(ctx);
const stopCount = labels.length;
if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
return outputs[findStopLessThanOrEqualTo(labels, value, this.key)].evaluate(ctx);
}
eachChild(fn) {
fn(this.input);
for (const expression of this.outputs) fn(expression);
}
outputDefined() {
return this.outputs.every((out) => out.outputDefined());
}
};
function unitBezier(p1x, p1y, p2x, p2y) {
const cx = 3 * p1x;
const bx = 3 * (p2x - p1x) - cx;
const ax = 1 - cx - bx;
const cy = 3 * p1y;
const by = 3 * (p2y - p1y) - cy;
const ay = 1 - cy - by;
return function solve(x, epsilon = 1e-6) {
if (x <= 0) return 0;
if (x >= 1) return 1;
let t = x;
for (let i = 0; i < 8; i++) {
const x2 = ((ax * t + bx) * t + cx) * t - x;
if (Math.abs(x2) < epsilon) return ((ay * t + by) * t + cy) * t;
const d2 = (3 * ax * t + 2 * bx) * t + cx;
if (Math.abs(d2) < 1e-6) break;
t -= x2 / d2;
}
let t0 = 0;
let t1 = 1;
t = x;
for (let i = 0; i < 20; i++) {
const x2 = ((ax * t + bx) * t + cx) * t;
if (Math.abs(x2 - x) < epsilon) break;
if (x > x2) t0 = t;
else t1 = t;
t = (t0 + t1) * .5;
}
return ((ay * t + by) * t + cy) * t;
};
}
var Interpolate = class Interpolate {
constructor(type, operator, interpolation, input, stops, key) {
this.type = type;
this.operator = operator;
this.interpolation = interpolation;
this.input = input;
this.key = key;
this.labels = [];
this.outputs = [];
for (const [label, expression] of stops) {
this.labels.push(label);
this.outputs.push(expression);
}
}
static interpolationFactor(interpolation, input, lower, upper) {
let t = 0;
if (interpolation.name === "exponential") t = exponentialInterpolation(input, interpolation.base, lower, upper);
else if (interpolation.name === "linear") t = exponentialInterpolation(input, 1, lower, upper);
else if (interpolation.name === "cubic-bezier") {
const c = interpolation.controlPoints;
t = unitBezier(c[0], c[1], c[2], c[3])(exponentialInterpolation(input, 1, lower, upper));
}
return t;
}
static parse(args, context) {
let [operator, interpolation, input, ...rest] = args;
if (!Array.isArray(interpolation) || interpolation.length === 0) return context.error("Expected an interpolation type expression.", 1);
if (interpolation[0] === "linear") interpolation = { name: "linear" };
else if (interpolation[0] === "exponential") {
const base = interpolation[1];
if (typeof base !== "number") return context.error("Exponential interpolation requires a numeric base.", 1, 1);
interpolation = {
name: "exponential",
base
};
} else if (interpolation[0] === "cubic-bezier") {
const controlPoints = interpolation.slice(1);
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);
interpolation = {
name: "cubic-bezier",
controlPoints
};
} else return context.error(`Unknown interpolation type ${String(interpolation[0])}`, 1, 0);
if (args.length - 1 < 4) return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
if ((args.length - 1) % 2 !== 0) return context.error("Expected an even number of arguments.");
input = context.parse(input, 2, NumberType);
if (!input) return null;
const stops = [];
let outputType = null;
if ((operator === "interpolate-hcl" || operator === "interpolate-lab") && context.expectedType != ColorArrayType) outputType = ColorType;
else if (context.expectedType && context.expectedType.kind !== "value") outputType = context.expectedType;
for (let i = 0; i < rest.length; i += 2) {
const label = rest[i];
const value = rest[i + 1];
const labelKey = i + 3;
const valueKey = i + 4;
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);
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);
const parsed = context.parse(value, valueKey, outputType);
if (!parsed) return null;
outputType = outputType || parsed.type;
stops.push([label, parsed]);
}
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.`);
return new Interpolate(outputType, operator, interpolation, input, stops, context.key);
}
evaluate(ctx) {
const labels = this.labels;
const outputs = this.outputs;
if (labels.length === 1) return outputs[0].evaluate(ctx);
const value = this.input.evaluate(ctx);
if (value <= labels[0]) return outputs[0].evaluate(ctx);
const stopCount = labels.length;
if (value >= labels[stopCount - 1]) return outputs[stopCount - 1].evaluate(ctx);
const index = findStopLessThanOrEqualTo(labels, value, this.key);
const lower = labels[index];
const upper = labels[index + 1];
const t = Interpolate.interpolationFactor(this.interpolation, value, lower, upper);
const outputLower = outputs[index].evaluate(ctx);
const outputUpper = outputs[index + 1].evaluate(ctx);
switch (this.operator) {
case "interpolate": switch (this.type.kind) {
case "number": return interpolateNumber(outputLower, outputUpper, t);
case "color": return Color.interpolate(outputLower, outputUpper, t);
case "padding": return Padding.interpolate(outputLower, outputUpper, t);
case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t);
case "numberArray": return NumberArray.interpolate(outputLower, outputUpper, t);
case "variableAnchorOffsetCollection": return VariableAnchorOffsetCollection.interpolate(outputLower, outputUpper, t, this.key);
case "array": return interpolateArray(outputLower, outputUpper, t);
case "projectionDefinition": return ProjectionDefinition.interpolate(outputLower, outputUpper, t);
}
case "interpolate-hcl": switch (this.type.kind) {
case "color": return Color.interpolate(outputLower, outputUpper, t, "hcl");
case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "hcl");
}
case "interpolate-lab": switch (this.type.kind) {
case "color": return Color.interpolate(outputLower, outputUpper, t, "lab");
case "colorArray": return ColorArray.interpolate(outputLower, outputUpper, t, "lab");
}
}
}
eachChild(fn) {
fn(this.input);
for (const expression of this.outputs) fn(expression);
}
outputDefined() {
return this.outputs.every((out) => out.outputDefined());
}
};
/**
* Returns a ratio that can be used to interpolate between exponential function
* stops.
* 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,
* and `a` and `b` are constants affording sufficient degrees of freedom to fit
* the function to the given stops.
*
* Here's a bit of algebra that lets us compute `f(x)` directly from the stop
* values without explicitly solving for `a` and `b`:
*
* First stop value: `f(x0) = y0 = a * base^x0 + b`
* Second stop value: `f(x1) = y1 = a * base^x1 + b`
* => `y1 - y0 = a(base^x1 - base^x0)`
* => `a = (y1 - y0)/(base^x1 - base^x0)`
*
* Desired value: `f(x) = y = a * base^x + b`
* => `f(x) = y0 + a * (base^x - base^x0)`
*
* From the above, we can replace the `a` in `a * (base^x - base^x0)` and do a
* little algebra:
* ```
* a * (base^x - base^x0) = (y1 - y0)/(base^x1 - base^x0) * (base^x - base^x0)
* = (y1 - y0) * (base^x - base^x0) / (base^x1 - base^x0)
* ```
*
* If we let `(base^x - base^x0) / (base^x1 base^x0)`, then we have
* `f(x) = y0 + (y1 - y0) * ratio`. In other words, `ratio` may be treated as
* an interpolation factor between the two stops' output values.
*
* (Note: a slightly different form for `ratio`,
* `(base^(x-x0) - 1) / (base^(x1-x0) - 1) `, is equivalent, but requires fewer
* expensive `Math.pow()` operations.)
*
* @private
*/
function exponentialInterpolation(input, base, lowerValue, upperValue) {
const difference = upperValue - lowerValue;
const progress = input - lowerValue;
if (difference === 0) return 0;
else if (base === 1) return progress / difference;
else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
}
const interpolateFactory = {
color: Color.interpolate,
number: interpolateNumber,
padding: Padding.interpolate,
numberArray: NumberArray.interpolate,
colorArray: ColorArray.interpolate,
variableAnchorOffsetCollection: VariableAnchorOffsetCollection.interpolate,
array: interpolateArray
};
var Coalesce = class Coalesce {
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 outputType = null;
const expectedType = context.expectedType;
if (expectedType && expectedType.kind !== "value") outputType = expectedType;
const parsedArgs = [];
for (const arg of args.slice(1)) {
const parsed = context.parse(arg, 1 + parsedArgs.length, outputType, void 0, { typeAnnotation: "omit" });
if (!parsed) return null;
outputType = outputType || parsed.type;
parsedArgs.push(parsed);
}
if (!outputType) throw new Error("No output type");
return expectedType && parsedArgs.some((arg) => checkSubtype(expectedType, arg.type)) ? new Coalesce(ValueType, parsedArgs) : new Coalesce(outputType, parsedArgs);
}
evaluate(ctx) {
let result = null;
let argCount = 0;
let requestedImageName;
for (const arg of this.args) {
argCount++;
result = arg.evaluate(ctx);
if (result && result instanceof ResolvedImage && !result.available) {
if (!requestedImageName) requestedImageName = result.name;
result = null;
if (argCount === this.args.length) result = requestedImageName;
}
if (result !== null) break;
}
return result;
}
eachChild(fn) {
this.args.forEach(fn);
}
outputDefined() {
return this.args.every((arg) => arg.outputDefined());
}
};
function isComparableType(op, type) {
if (op === "==" || op === "!=") return type.kind === "boolean" || type.kind === "string" || type.kind === "number" || type.kind === "null" || type.kind === "value";
else return type.kind === "string" || type.kind === "number" || type.kind === "value";
}
function eq(ctx, a, b) {
return a === b;
}
function neq(ctx, a, b) {
return a !== b;
}
function lt(ctx, a, b) {
return a < b;
}
function gt(ctx, a, b) {
return a > b;
}
function lteq(ctx, a, b) {
return a <= b;
}
function gteq(ctx, a, b) {
return a >= b;
}
function eqCollate(ctx, a, b, c) {
return c.compare(a, b) === 0;
}
function neqCollate(ctx, a, b, c) {
return !eqCollate(ctx, a, b, c);
}
function ltCollate(ctx, a, b, c) {
return c.compare(a, b) < 0;
}
function gtCollate(ctx, a, b, c) {
return c.compare(a, b) > 0;
}
function lteqCollate(ctx, a, b, c) {
return c.compare(a, b) <= 0;
}
function gteqCollate(ctx, a, b, c) {
return c.compare(a, b) >= 0;
}
/**
* Special form for comparison operators, implementing the signatures:
* - (T, T, ?Collator) => boolean
* - (T, value, ?Collator) => boolean
* - (value, T, ?Collator) => boolean
*
* For inequalities, T must be either value, string, or number. For ==/!=, it
* can also be boolean or null.
*
* Equality semantics are equivalent to Javascript's strict equality (===/!==)
* -- i.e., when the arguments' types don't match, == evaluates to false, != to
* true.
*
* When types don't match in an ordering comparison, a runtime error is thrown.
*
* @private
*/
function makeComparison(op, compareBasic, compareWithCollator) {
const isOrderComparison = op !== "==" && op !== "!=";
return class Comparison {
constructor(lhs, rhs, key, collator) {
this.lhs = lhs;
this.rhs = rhs;
this.key = key;
this.collator = collator;
this.type = BooleanType;
this.hasUntypedArgument = lhs.type.kind === "value" || rhs.type.kind === "value";
}
static parse(args, context) {
if (args.length !== 3 && args.length !== 4) return context.error("Expected two or three arguments.");
const op = args[0];
let lhs = context.parse(args[1], 1, ValueType);
if (!lhs) return null;
if (!isComparableType(op, lhs.type)) return context.concat(1).error(`"${op}" comparisons are not supported for type '${typeToString(lhs.type)}'.`);
let rhs = context.parse(args[2], 2, ValueType);
if (!rhs) return null;
if (!isComparableType(op, rhs.type)) return context.concat(2).error(`"${op}" comparisons are not supported for type '${typeToString(rhs.type)}'.`);
if (lhs.type.kind !== rhs.type.kind && lhs.type.kind !== "value" && rhs.type.kind !== "value") return context.error(`Cannot compare types '${typeToString(lhs.type)}' and '${typeToString(rhs.type)}'.`);
if (isOrderComparison) {
if (lhs.type.kind === "value" && rhs.type.kind !== "value") lhs = new Assertion(rhs.type, [lhs], context.key);
else if (lhs.type.kind !== "value" && rhs.type.kind === "value") rhs = new Assertion(lhs.type, [rhs], context.key);
}
let collator = null;
if (args.length === 4) {
if (lhs.type.kind !== "string" && rhs.type.kind !== "string" && lhs.type.kind !== "value" && rhs.type.kind !== "value") return context.error("Cannot use collator to compare non-string types.");
collator = context.parse(args[3], 3, CollatorType);
if (!collator) return null;
}
return new Comparison(lhs, rhs, context.key, collator);
}
evaluate(ctx) {
const lhs = this.lhs.evaluate(ctx);
const rhs = this.rhs.evaluate(ctx);
if (isOrderComparison && this.hasUntypedArgument) {
const lt = typeOf(lhs);
const rt = typeOf(rhs);
if (lt.kind !== rt.kind || !(lt.kind === "string" || lt.kind === "number")) throw new RuntimeError(`Expected arguments for "${op}" to be (string, string) or (number, number), but found (${lt.kind}, ${rt.kind}) instead.`, this.key);
}
if (this.collator && !isOrderComparison && this.hasUntypedArgument) {
const lt = typeOf(lhs);
const rt = typeOf(rhs);
if (lt.kind !== "string" || rt.kind !== "string") return compareBasic(ctx, lhs, rhs);
}
return this.collator ? compareWithCollator(ctx, lhs, rhs, this.collator.evaluate(ctx)) : compareBasic(ctx, lhs, rhs);
}
eachChild(fn) {
fn(this.lhs);
fn(this.rhs);
if (this.collator) fn(this.collator);
}
outputDefined() {
return true;
}
};
}
const Equals = makeComparison("==", eq, eqCollate);
const NotEquals = makeComparison("!=", neq, neqCollate);
const LessThan = makeComparison("<", lt, ltCollate);
const GreaterThan = makeComparison(">", gt, gtCollate);
const LessThanOrEqual = makeComparison("<=", lteq, lteqCollate);
const GreaterThanOrEqual = makeComparison(">=", gteq, gteqCollate);
var CollatorExpression = class CollatorExpression {
constructor(caseSensitive, diacriticSensitive, locale) {
this.type = CollatorType;
this.locale = locale;
this.caseSensitive = caseSensitive;
this.diacriticSensitive = diacriticSensitive;
}
static parse(args, context) {
if (args.length !== 2) return context.error("Expected one argument.");
const options = args[1];
if (typeof options !== "object" || Array.isArray(options)) return context.error("Collator options argument must be an object.");
const caseSensitive = context.parse(options["case-sensitive"] === void 0 ? false : options["case-sensitive"], 1, BooleanType);
if (!caseSensitive) return null;
const diacriticSensitive = context.parse(options["diacritic-sensitive"] === void 0 ? false : options["diacritic-sensitive"], 1, BooleanType);
if (!diacriticSensitive) return null;
let locale = null;
if (options["locale"]) {
locale = context.parse(options["locale"], 1, StringType);
if (!locale) return null;
}
return new CollatorExpression(caseSensitive, diacriticSensitive, locale);
}
evaluate(ctx) {
return new Collator(this.caseSensitive.evaluate(ctx), this.diacriticSensitive.evaluate(ctx), this.locale ? this.locale.evaluate(ctx) : null);
}
eachChild(fn) {
fn(this.caseSensitive);
fn(this.diacriticSensitive);
if (this.locale) fn(this.locale);
}
outputDefined() {
return false;
}
};
var NumberFormat = class NumberFormat {
constructor(number, locale, currency, unit, minFractionDigits, maxFractionDigits) {
this.type = StringType;
this.number = number;
this.locale = locale;
this.currency = currency;
this.unit = unit;
this.minFractionDigits = minFractionDigits;
this.maxFractionDigits = maxFractionDigits;
}
static parse(args, context) {
if (args.length !== 3) return context.error("Expected two arguments.");
const number = context.parse(args[1], 1, NumberType);
if (!number) return null;
const options = args[2];
if (typeof options !== "object" || Array.isArray(options)) return context.error("NumberFormat options argument must be an object.");
let locale = null;
if (options["locale"]) {
locale = context.parse(options["locale"], 1, StringType);
if (!locale) return null;
}
let currency = null;
if (options["currency"]) {
currency = context.parse(options["currency"], 1, StringType);
if (!currency) return null;
}
let unit = null;
if (options["unit"]) {
unit = context.parse(options["unit"], 1, StringType);
if (!unit) return null;
}
if (currency && unit) return context.error("NumberFormat options `currency` and `unit` are mutually exclusive");
let minFractionDigits = null;
if (options["min-fraction-digits"]) {
minFractionDigits = context.parse(options["min-fraction-digits"], 1, NumberType);
if (!minFractionDigits) return null;
}
let maxFractionDigits = null;
if (options["max-fraction-digits"]) {
maxFractionDigits = context.parse(options["max-fraction-digits"], 1, NumberType);
if (!maxFractionDigits) return null;
}
return new NumberFormat(number, locale, currency, unit, minFractionDigits, maxFractionDigits);
}
evaluate(ctx) {
return new Intl.NumberFormat(this.locale ? this.locale.evaluate(ctx) : [], {
style: this.currency ? "currency" : this.unit ? "unit" : "decimal",
currency: this.currency ? this.currency.evaluate(ctx) : void 0,
unit: this.unit ? this.unit.evaluate(ctx) : void 0,
minimumFractionDigits: this.minFractionDigits ? this.minFractionDigits.evaluate(ctx) : void 0,
maximumFractionDigits: this.maxFractionDigits ? this.maxFractionDigits.evaluate(ctx) : void 0
}).format(this.number.evaluate(ctx));
}
eachChild(fn) {
fn(this.number);
if (this.locale) fn(this.locale);
if (this.currency) fn(this.currency);
if (this.unit) fn(this.unit);
if (this.minFractionDigits) fn(this.minFractionDigits);
if (this.maxFractionDigits) fn(this.maxFractionDigits);
}
outputDefined() {
return false;
}
};
var FormatExpression = class FormatExpression {
constructor(sections) {
this.type = FormattedType;
this.sections = sections;
}
static parse(args, context) {
if (args.length < 2) return context.error("Expected at least one argument.");
const firstArg = args[1];
if (!Array.isArray(firstArg) && typeof firstArg === "object") return context.error("First argument must be an image or text section.");
const sections = [];
let nextTokenMayBeObject = false;
for (let i = 1; i <= args.length - 1; ++i) {
const arg = args[i];
if (nextTokenMayBeObject && typeof arg === "object" && !Array.isArray(arg)) {
nextTokenMayBeObject = false;
let scale = null;
if (arg["font-scale"]) {
scale = context.parse(arg["font-scale"], 1, NumberType);
if (!scale) return null;
}
let font = null;
if (arg["text-font"]) {
font = context.parse(arg["text-font"], 1, array(StringType));
if (!font) return null;
}
let textColor = null;
if (arg["text-color"]) {
textColor = context.parse(arg["text-color"], 1, ColorType);
if (!textColor) return null;
}
let verticalAlign = null;
if (arg["vertical-align"]) {
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.`);
verticalAlign = context.parse(arg["vertical-align"], 1, StringType);
if (!verticalAlign) return null;
}
const lastExpression = sections[sections.length - 1];
lastExpression.scale = scale;
lastExpression.font = font;
lastExpression.textColor = textColor;
lastExpression.verticalAlign = verticalAlign;
} else {
const content = context.parse(args[i], 1, ValueType);
if (!content) return null;
const kind = content.type.kind;
if (kind !== "string" && kind !== "value" && kind !== "null" && kind !== "resolvedImage") return context.error("Formatted text type must be 'string', 'value', 'image' or 'null'.");
nextTokenMayBeObject = true;
sections.push({
content,
scale: null,
font: null,
textColor: null,
verticalAlign: null
});
}
}
return new FormatExpression(sections);
}
evaluate(ctx) {
const evaluateSection = (section) => {
const evaluatedContent = section.content.evaluate(ctx);
if (typeOf(evaluatedContent) === ResolvedImageType) return new FormattedSection("", evaluatedContent, null, null, null, section.verticalAlign ? section.verticalAlign.evaluate(ctx) : null);
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);
};
return new Formatted(this.sections.map(evaluateSection));
}
eachChild(fn) {
for (const section of this.sections) {
fn(section.content);
if (section.scale) fn(section.scale);
if (section.font) fn(section.font);
if (section.textColor) fn(section.textColor);
if (section.verticalAlign) fn(section.verticalAlign);
}
}
outputDefined() {
return false;
}
};
var ImageExpression = class ImageExpression {
constructor(input) {
this.type = ResolvedImageType;
this.input = input;
}
static parse(args, context) {
if (args.length !== 2) return context.error("Expected two arguments.");
const name = context.parse(args[1], 1, StringType);
if (!name) return context.error("No image name provided.");
return new ImageExpression(name);
}
evaluate(ctx) {
const evaluatedImageName = this.input.evaluate(ctx);
const value = ResolvedImage.fromString(evaluatedImageName);
if (value && ctx.availableImages) value.available = ctx.availableImages.indexOf(evaluatedImageName) > -1;
return value;
}
eachChild(fn) {
fn(this.input);
}
outputDefined() {
return false;
}
};
var Length = class Length {
constructor(input, key) {
this.input = input;
this.key = key;
this.type = NumberType;
}
static parse(args, context) {
if (args.length !== 2) return context.error(`Expected 1 argument, but found ${args.length - 1} instead.`);
const input = context.parse(args[1], 1);
if (!input) return null;
if (input.type.kind !== "array" && input.type.kind !== "string" && input.type.kind !== "value") return context.error(`Expected argument of type string or array, but found ${typeToString(input.type)} instead.`);
return new Length(input, context.key);
}
evaluate(ctx) {
const input = this.input.evaluate(ctx);
if (typeof input === "string") return [...input].length;
else if (Array.isArray(input)) return input.length;
else throw new RuntimeError(`Expected value to be of type string or array, but found ${typeToString(typeOf(input))} instead.`, this.key);
}
eachChild(fn) {
fn(this.input);
}
outputDefined() {
return false;
}
};
const EXTENT = 8192;
function getTileCoordinates(p, canonical) {
const x = mercatorXfromLng$1(p[0]);
const y = mercatorYfromLat$1(p[1]);
const tilesAtZoom = Math.pow(2, canonical.z);
return [Math.round(x * tilesAtZoom * EXTENT), Math.round(y * tilesAtZoom * EXTENT)];
}
function getLngLatFromTileCoord(coord, canonical) {
const tilesAtZoom = Math.pow(2, canonical.z);
const x = (coord[0] / EXTENT + canonical.x) / tilesAtZoom;
const y = (coord[1] / EXTENT + canonical.y) / tilesAtZoom;
return [lngFromMercatorXfromLng(x), latFromMercatorY$1(y)];
}
function mercatorXfromLng$1(lng) {
return (180 + lng) / 360;
}
function lngFromMercatorXfromLng(mercatorX) {
return mercatorX * 360 - 180;
}
function mercatorYfromLat$1(lat) {
return (180 - 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360))) / 360;
}
function latFromMercatorY$1(mercatorY) {
return 360 / Math.PI * Math.atan(Math.exp((180 - mercatorY * 360) * Math.PI / 180)) - 90;
}
function updateBBox(bbox, coord) {
bbox[0] = Math.min(bbox[0], coord[0]);
bbox[1] = Math.min(bbox[1], coord[1]);
bbox[2] = Math.max(bbox[2], coord[0]);
bbox[3] = Math.max(bbox[3], coord[1]);
}
function boxWithinBox(bbox1, bbox2) {
if (bbox1[0] <= bbox2[0]) return false;
if (bbox1[2] >= bbox2[2]) return false;
if (bbox1[1] <= bbox2[1]) return false;
if (bbox1[3] >= bbox2[3]) return false;
return true;
}
function rayIntersect(p, p1, p2) {
return p1[1] > p[1] !== p2[1] > p[1] && p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0];
}
function pointOnBoundary(p, p1, p2) {
const x1 = p[0] - p1[0];
const y1 = p[1] - p1[1];
const x2 = p[0] - p2[0];
const y2 = p[1] - p2[1];
return x1 * y2 - x2 * y1 === 0 && x1 * x2 <= 0 && y1 * y2 <= 0;
}
function segmentIntersectSegment(a, b, c, d) {
const vectorP = [b[0] - a[0], b[1] - a[1]];
if (perp([d[0] - c[0], d[1] - c[1]], vectorP) === 0) return false;
if (twoSided(a, b, c, d) && twoSided(c, d, a, b)) return true;
return false;
}
function lineIntersectPolygon(p1, p2, polygon) {
for (const ring of polygon) for (let j = 0; j < ring.length - 1; ++j) if (segmentIntersectSegment(p1, p2, ring[j], ring[j + 1])) return true;
return false;
}
function pointWithinPolygon(point, rings, trueIfOnBoundary = false) {
let inside = false;
for (const ring of rings) for (let j = 0; j < ring.length - 1; j++) {
if (pointOnBoundary(point, ring[j], ring[j + 1])) return trueIfOnBoundary;
if (rayIntersect(point, ring[j], ring[j + 1])) inside = !inside;
}
return inside;
}
function pointWithinPolygons(point, polygons) {
for (const polygon of polygons) if (pointWithinPolygon(point, polygon)) return true;
return false;
}
function lineStringWithinPolygon(line, polygon) {
for (const point of line) if (!pointWithinPolygon(point, polygon)) return false;
for (let i = 0; i < line.length - 1; ++i) if (lineIntersectPolygon(line[i], line[i + 1], polygon)) return false;
return true;
}
function lineStringWithinPolygons(line, polygons) {
for (const polygon of polygons) if (lineStringWithinPolygon(line, polygon)) return true;
return false;
}
function perp(v1, v2) {
return v1[0] * v2[1] - v1[1] * v2[0];
}
function twoSided(p1, p2, q1, q2) {
const x1 = p1[0] - q1[0];
const y1 = p1[1] - q1[1];
const x2 = p2[0] - q1[0];
const y2 = p2[1] - q1[1];
const x3 = q2[0] - q1[0];
const y3 = q2[1] - q1[1];
const det1 = x1 * y3 - x3 * y1;
const det2 = x2 * y3 - x3 * y2;
if (det1 > 0 && det2 < 0 || det1 < 0 && det2 > 0) return true;
return false;
}
function getTilePolygon(coordinates, bbox, canonical) {
const polygon = [];
for (let i = 0; i < coordinates.length; i++) {
const ring = [];
for (let j = 0; j < coordinates[i].length; j++) {
const coord = getTileCoordinates(coordinates[i][j], canonical);
updateBBox(bbox, coord);
ring.push(coord);
}
polygon.push(ring);
}
return polygon;
}
function getTilePolygons(coordinates, bbox, canonical) {
const polygons = [];
for (let i = 0; i < coordinates.length; i++) {
const polygon = getTilePolygon(coordinates[i], bbox, canonical);
polygons.push(polygon);
}
return polygons;
}
function updatePoint(p, bbox, polyBBox, worldSize) {
if (p[0] < polyBBox[0] || p[0] > polyBBox[2]) {
const halfWorldSize = worldSize * .5;
let shift = p[0] - polyBBox[0] > halfWorldSize ? -worldSize : polyBBox[0] - p[0] > halfWorldSize ? worldSize : 0;
if (shift === 0) shift = p[0] - polyBBox[2] > halfWorldSize ? -worldSize : polyBBox[2] - p[0] > halfWorldSize ? worldSize : 0;
p[0] += shift;
}
updateBBox(bbox, p);
}
function resetBBox(bbox) {
bbox[0] = bbox[1] = Infinity;
bbox[2] = bbox[3] = -Infinity;
}
function getTilePoints(geometry, pointBBox, polyBBox, canonical) {
const worldSize = Math.pow(2, canonical.z) * EXTENT;
const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];
const tilePoints = [];
for (const points of geometry) for (const point of points) {
const p = [point.x + shifts[0], point.y + shifts[1]];
updatePoint(p, pointBBox, polyBBox, worldSize);
tilePoints.push(p);
}
return tilePoints;
}
function getTileLines(geometry, lineBBox, polyBBox, canonical) {
const worldSize = Math.pow(2, canonical.z) * EXTENT;
const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];
const tileLines = [];
for (const line of geometry) {
const tileLine = [];
for (const point of line) {
const p = [point.x + shifts[0], point.y + shifts[1]];
updateBBox(lineBBox, p);
tileLine.push(p);
}
tileLines.push(tileLine);
}
if (lineBBox[2] - lineBBox[0] <= worldSize / 2) {
resetBBox(lineBBox);
for (const line of tileLines) for (const p of line) updatePoint(p, lineBBox, polyBBox, worldSize);
}
return tileLines;
}
function pointsWithinPolygons(ctx, polygonGeometry) {
const pointBBox = [
Infinity,
Infinity,
-Infinity,
-Infinity
];
const polyBBox = [
Infinity,
Infinity,
-Infinity,
-Infinity
];
const canonical = ctx.canonicalID();
if (polygonGeometry.type === "Polygon") {
const tilePolygon = getTilePolygon(polygonGeometry.coordinates, polyBBox, canonical);
const tilePoints = getTilePoints(ctx.geometry(), pointBBox, polyBBox, canonical);
if (!boxWithinBox(pointBBox, polyBBox)) return false;
for (const point of tilePoints) if (!pointWithinPolygon(point, tilePolygon)) return false;
}
if (polygonGeometry.type === "MultiPolygon") {
const tilePolygons = getTilePolygons(polygonGeometry.coordinates, polyBBox, canonical);
const tilePoints = getTilePoints(ctx.geometry(), pointBBox, polyBBox, canonical);
if (!boxWithinBox(pointBBox, polyBBox)) return false;
for (const point of tilePoints) if (!pointWithinPolygons(point, tilePolygons)) return false;
}
return true;
}
function linesWithinPolygons(ctx, polygonGeometry) {
const lineBBox = [
Infinity,
Infinity,
-Infinity,
-Infinity
];
const polyBBox = [
Infinity,
Infinity,
-Infinity,
-Infinity
];
const canonical = ctx.canonicalID();
if (polygonGeometry.type === "Polygon") {
const tilePolygon = getTilePolygon(polygonGeometry.coordinates, polyBBox, canonical);
const tileLines = getTileLines(ctx.geometry(), lineBBox, polyBBox, canonical);
if (!boxWithinBox(lineBBox, polyBBox)) return false;
for (const line of tileLines) if (!lineStringWithinPolygon(line, tilePolygon)) return false;
}
if (polygonGeometry.type === "MultiPolygon") {
const tilePolygons = getTilePolygons(polygonGeometry.coordinates, polyBBox, canonical);
const tileLines = getTileLines(ctx.geometry(), lineBBox, polyBBox, canonical);
if (!boxWithinBox(lineBBox, polyBBox)) return false;
for (const line of tileLines) if (!lineStringWithinPolygons(line, tilePolygons)) return false;
}
return true;
}
var Within = class Within {
constructor(geojson, geometries) {
this.type = BooleanType;
this.geojson = geojson;
this.geometries = geometries;
}
static parse(args, context) {
if (args.length !== 2) return context.error(`'within' expression requires exactly one argument, but found ${args.length - 1} instead.`);
if (isValue(args[1])) {
const geojson = args[1];
if (geojson.type === "FeatureCollection") {
const polygonsCoords = [];
for (const polygon of geojson.features) {
const { type, coordinates } = polygon.geometry;
if (type === "Polygon") polygonsCoords.push(coordinates);
if (type === "MultiPolygon") polygonsCoords.push(...coordinates);
}
if (polygonsCoords.length) return new Within(geojson, {
type: "MultiPolygon",
coordinates: polygonsCoords
});
} else if (geojson.type === "Feature") {
const type = geojson.geometry.type;
if (type === "Polygon" || type === "MultiPolygon") return new Within(geojson, geojson.geometry);
} else if (geojson.type === "Polygon" || geojson.type === "MultiPolygon") return new Within(geojson, geojson);
}
return context.error("'within' expression requires valid geojson object that contains polygon geometry type.");
}
evaluate(ctx) {
if (ctx.geometry() != null && ctx.canonicalID() != null) {
if (ctx.geometryType() === "Point") return pointsWithinPolygons(ctx, this.geometries);
else if (ctx.geometryType() === "LineString") return linesWithinPolygons(ctx, this.geometries);
}
return false;
}
eachChild() {}
outputDefined() {
return true;
}
};
var TinyQueue$1 = class {
constructor(data = [], compare = (a, b) => a < b ? -1 : a > b ? 1 : 0) {
this.data = data;
this.length = this.data.length;
this.compare = compare;
if (this.length > 0) for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
}
push(item) {
this.data.push(item);
this._up(this.length++);
}
pop() {
if (this.length === 0) return void 0;
const top = this.data[0];
const bottom = this.data.pop();
if (--this.length > 0) {
this.data[0] = bottom;
this._down(0);
}
return top;
}
peek() {
return this.data[0];
}
_up(pos) {
const { data, compare } = this;
const item = data[pos];
while (pos > 0) {
const parent = pos - 1 >> 1;
const current = data[parent];
if (compare(item, current) >= 0) break;
data[pos] = current;
pos = parent;
}
data[pos] = item;
}
_down(pos) {
const { data, compare } = this;
const halfLength = this.length >> 1;
const item = data[pos];
while (pos < halfLength) {
let bestChild = (pos << 1) + 1;
const right = bestChild + 1;
if (right < this.length && compare(data[right], data[bestChild]) < 0) bestChild = right;
if (compare(data[bestChild], item) >= 0) break;
data[pos] = data[bestChild];
pos = bestChild;
}
data[pos] = item;
}
};
/**
* Rearranges items so that all items in the [left, k] are the smallest.
* The k-th element will have the (k - left + 1)-th smallest value in [left, right].
*
* @template T
* @param {T[]} arr the array to partially sort (in place)
* @param {number} k middle index for partial sorting (as defined above)
* @param {number} [left=0] left index of the range to sort
* @param {number} [right=arr.length-1] right index
* @param {(a: T, b: T) => number} [compare = (a, b) => a - b] compare function
*/
function quickselect(arr, k, left = 0, right = arr.length - 1, compare = defaultCompare) {
while (right > left) {
if (right - left > 600) {
const n = right - left + 1;
const m = k - left + 1;
const z = Math.log(n);
const s = .5 * Math.exp(2 * z / 3);
const sd = .5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
quickselect(arr, k, Math.max(left, Math.floor(k - m * s / n + sd)), Math.min(right, Math.floor(k + (n - m) * s / n + sd)), compare);
}
const t = arr[k];
let i = left;
/** @type {number} */
let j = right;
swap$2(arr, left, k);
if (compare(arr[right], t) > 0) swap$2(arr, left, right);
while (i < j) {
swap$2(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap$2(arr, left, j);
else {
j++;
swap$2(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
/**
* @template T
* @param {T[]} arr
* @param {number} i
* @param {number} j
*/
function swap$2(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
/**
* @template T
* @param {T} a
* @param {T} b
* @returns {number}
*/
function defaultCompare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
/**
* Classifies an array of rings into polygons with outer rings and holes
* @param rings - the rings to classify
* @param maxRings - the maximum number of rings to include in a polygon, use 0 to include all rings
* @returns an array of polygons with internal rings as holes
*/
function classifyRings$1(rings, maxRings) {
if (rings.length <= 1) return [rings];
const polygons = [];
let polygon;
let ccw;
for (const ring of rings) {
const area = calculateSignedArea(ring);
if (area === 0) continue;
ring.area = Math.abs(area);
if (ccw === void 0) ccw = area < 0;
if (ccw === area < 0) {
if (polygon) polygons.push(polygon);
polygon = [ring];
} else polygon.push(ring);
}
if (polygon) polygons.push(polygon);
if (maxRings > 1) for (let j = 0; j < polygons.length; j++) {
if (polygons[j].length <= maxRings) continue;
quickselect(polygons[j], maxRings, 1, polygons[j].length - 1, compareAreas);
polygons[j] = polygons[j].slice(0, maxRings);
}
return polygons;
}
function compareAreas(a, b) {
return b.area - a.area;
}
/**
* Returns the signed area for the polygon ring. Positive areas are exterior rings and
* have a clockwise winding. Negative areas are interior rings and have a counter clockwise
* ordering.
*
* @param ring - Exterior or interior ring
* @returns Signed area
*/
function calculateSignedArea(ring) {
let sum = 0;
for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
p1 = ring[i];
p2 = ring[j];
sum += (p2.x - p1.x) * (p1.y + p2.y);
}
return sum;
}
const RE = 6378.137;
const E2 = .0066943799901413165;
const RAD = Math.PI / 180;
var CheapRuler = class {
constructor(lat) {
const m = RAD * RE * 1e3;
const coslat = Math.cos(lat * RAD);
const w2 = 1 / (1 - E2 * (1 - coslat * coslat));
const w = Math.sqrt(w2);
this.kx = m * w * coslat;
this.ky = m * w * w2 * .9933056200098587;
}
/**
* Given two points of the form [longitude, latitude], returns the distance.
*
* @param a - point [longitude, latitude]
* @param b - point [longitude, latitude]
* @returns distance
* @example
* const distance = ruler.distance([30.5, 50.5], [30.51, 50.49]);
* //=distance
*/
distance(a, b) {
const dx = this.wrap(a[0] - b[0]) * this.kx;
const dy = (a[1] - b[1]) * this.ky;
return Math.sqrt(dx * dx + dy * dy);
}
/**
* Returns an object of the form {point, index, t}, where point is closest point on the line
* from the given point, index is the start index of the segment with the closest point,
* and t is a parameter from 0 to 1 that indicates where the closest point is on that segment.
*
* @param line - an array of points that form the line
* @param p - point [longitude, latitude]
* @returns the nearest point, its index in the array and the proportion along the line
* @example
* const point = ruler.pointOnLine(line, [-67.04, 50.5]).point;
* //=point
*/
pointOnLine(line, p) {
let minDist = Infinity;
let minX, minY, minI, minT;
for (let i = 0; i < line.length - 1; i++) {
let x = line[i][0];
let y = line[i][1];
let dx = this.wrap(line[i + 1][0] - x) * this.kx;
let dy = (line[i + 1][1] - y) * this.ky;
let t = 0;
if (dx !== 0 || dy !== 0) {
t = (this.wrap(p[0] - x) * this.kx * dx + (p[1] - y) * this.ky * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = line[i + 1][0];
y = line[i + 1][1];
} else if (t > 0) {
x += dx / this.kx * t;
y += dy / this.ky * t;
}
}
dx = this.wrap(p[0] - x) * this.kx;
dy = (p[1] - y) * this.ky;
const sqDist = dx * dx + dy * dy;
if (sqDist < minDist) {
minDist = sqDist;
minX = x;
minY = y;
minI = i;
minT = t;
}
}
return {
point: [minX, minY],
index: minI,
t: Math.max(0, Math.min(1, minT))
};
}
wrap(deg) {
while (deg < -180) deg += 360;
while (deg > 180) deg -= 360;
return deg;
}
};
const MinPointsSize = 100;
const MinLinePointsSize = 50;
function compareDistPair(a, b) {
return b[0] - a[0];
}
function getRangeSize(range) {
return range[1] - range[0] + 1;
}
function isRangeSafe(range, threshold) {
return range[1] >= range[0] && range[1] < threshold;
}
function splitRange(range, isLine) {
if (range[0] > range[1]) return [null, null];
const size = getRangeSize(range);
if (isLine) {
if (size === 2) return [range, null];
const size1 = Math.floor(size / 2);
return [[range[0], range[0] + size1], [range[0] + size1, range[1]]];
}
if (size === 1) return [range, null];
const size1 = Math.floor(size / 2) - 1;
return [[range[0], range[0] + size1], [range[0] + size1 + 1, range[1]]];
}
function getBBox(coords, range) {
if (!isRangeSafe(range, coords.length)) return [
Infinity,
Infinity,
-Infinity,
-Infinity
];
const bbox = [
Infinity,
Infinity,
-Infinity,
-Infinity
];
for (let i = range[0]; i <= range[1]; ++i) updateBBox(bbox, coords[i]);
return bbox;
}
function getPolygonBBox(polygon) {
const bbox = [
Infinity,
Infinity,
-Infinity,
-Infinity
];
for (const ring of polygon) for (const coord of ring) updateBBox(bbox, coord);
return bbox;
}
function isValidBBox(bbox) {
return bbox[0] !== -Infinity && bbox[1] !== -Infinity && bbox[2] !== Infinity && bbox[3] !== Infinity;
}
function bboxToBBoxDistance(bbox1, bbox2, ruler) {
if (!isValidBBox(bbox1) || !isValidBBox(bbox2)) return NaN;
let dx = 0;
let dy = 0;
if (bbox1[2] < bbox2[0]) dx = bbox2[0] - bbox1[2];
if (bbox1[0] > bbox2[2]) dx = bbox1[0] - bbox2[2];
if (bbox1[1] > bbox2[3]) dy = bbox1[1] - bbox2[3];
if (bbox1[3] < bbox2[1]) dy = bbox2[1] - bbox1[3];
return ruler.distance([0, 0], [dx, dy]);
}
function pointToLineDistance(point, line, ruler) {
const nearestPoint = ruler.pointOnLine(line, point);
return ruler.distance(point, nearestPoint.point);
}
function segmentToSegmentDistance(p1, p2, q1, q2, ruler) {
const dist1 = Math.min(pointToLineDistance(p1, [q1, q2], ruler), pointToLineDistance(p2, [q1, q2], ruler));
const dist2 = Math.min(pointToLineDistance(q1, [p1, p2], ruler), pointToLineDistance(q2, [p1, p2], ruler));
return Math.min(dist1, dist2);
}
function lineToLineDistance(line1, range1, line2, range2, ruler) {
if (!(isRangeSafe(range1, line1.length) && isRangeSafe(range2, line2.length))) return Infinity;
let dist = Infinity;
for (let i = range1[0]; i < range1[1]; ++i) {
const p1 = line1[i];
const p2 = line1[i + 1];
for (let j = range2[0]; j < range2[1]; ++j) {
const q1 = line2[j];
const q2 = line2[j + 1];
if (segmentIntersectSegment(p1, p2, q1, q2)) return 0;
dist = Math.min(dist, segmentToSegmentDistance(p1, p2, q1, q2, ruler));
}
}
return dist;
}
function pointsToPointsDistance(points1, range1, points2, range2, ruler) {
if (!(isRangeSafe(range1, points1.length) && isRangeSafe(range2, points2.length))) return NaN;
let dist = Infinity;
for (let i = range1[0]; i <= range1[1]; ++i) for (let j = range2[0]; j <= range2[1]; ++j) {
dist = Math.min(dist, ruler.distance(points1[i], points2[j]));
if (dist === 0) return dist;
}
return dist;
}
function pointToPolygonDistance(point, polygon, ruler) {
if (pointWithinPolygon(point, polygon, true)) return 0;
let dist = Infinity;
for (const ring of polygon) {
const front = ring[0];
const back = ring[ring.length - 1];
if (front !== back) {
dist = Math.min(dist, pointToLineDistance(point, [back, front], ruler));
if (dist === 0) return dist;
}
const nearestPoint = ruler.pointOnLine(ring, point);
dist = Math.min(dist, ruler.distance(point, nearestPoint.point));
if (dist === 0) return dist;
}
return dist;
}
function lineToPolygonDistance(line, range, polygon, ruler) {
if (!isRangeSafe(range, line.length)) return NaN;
for (let i = range[0]; i <= range[1]; ++i) if (pointWithinPolygon(line[i], polygon, true)) return 0;
let dist = Infinity;
for (let i = range[0]; i < range[1]; ++i) {
const p1 = line[i];
const p2 = line[i + 1];
for (const ring of polygon) for (let j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
const q1 = ring[k];
const q2 = ring[j];
if (segmentIntersectSegment(p1, p2, q1, q2)) return 0;
dist = Math.min(dist, segmentToSegmentDistance(p1, p2, q1, q2, ruler));
}
}
return dist;
}
function polygonIntersect(poly1, poly2) {
for (const ring of poly1) for (const point of ring) if (pointWithinPolygon(point, poly2, true)) return true;
return false;
}
function polygonToPolygonDistance(polygon1, polygon2, ruler, currentMiniDist = Infinity) {
const bbox1 = getPolygonBBox(polygon1);
const bbox2 = getPolygonBBox(polygon2);
if (currentMiniDist !== Infinity && bboxToBBoxDistance(bbox1, bbox2, ruler) >= currentMiniDist) return currentMiniDist;
if (boxWithinBox(bbox1, bbox2)) {
if (polygonIntersect(polygon1, polygon2)) return 0;
} else if (polygonIntersect(polygon2, polygon1)) return 0;
let dist = Infinity;
for (const ring1 of polygon1) for (let i = 0, len1 = ring1.length, l = len1 - 1; i < len1; l = i++) {
const p1 = ring1[l];
const p2 = ring1[i];
for (const ring2 of polygon2) for (let j = 0, len2 = ring2.length, k = len2 - 1; j < len2; k = j++) {
const q1 = ring2[k];
const q2 = ring2[j];
if (segmentIntersectSegment(p1, p2, q1, q2)) return 0;
dist = Math.min(dist, segmentToSegmentDistance(p1, p2, q1, q2, ruler));
}
}
return dist;
}
function updateQueue(distQueue, miniDist, ruler, points, polyBBox, rangeA) {
if (!rangeA) return;
const tempDist = bboxToBBoxDistance(getBBox(points, rangeA), polyBBox, ruler);
if (tempDist < miniDist) distQueue.push([
tempDist,
rangeA,
[0, 0]
]);
}
function updateQueueTwoSets(distQueue, miniDist, ruler, pointSet1, pointSet2, range1, range2) {
if (!range1 || !range2) return;
const tempDist = bboxToBBoxDistance(getBBox(pointSet1, range1), getBBox(pointSet2, range2), ruler);
if (tempDist < miniDist) distQueue.push([
tempDist,
range1,
range2
]);
}
function pointsToPolygonDistance(points, isLine, polygon, ruler, currentMiniDist = Infinity) {
let miniDist = Math.min(ruler.distance(points[0], polygon[0][0]), currentMiniDist);
if (miniDist === 0) return miniDist;
const distQueue = new TinyQueue$1([[
0,
[0, points.length - 1],
[0, 0]
]], compareDistPair);
const polyBBox = getPolygonBBox(polygon);
while (distQueue.length > 0) {
const distPair = distQueue.pop();
if (distPair[0] >= miniDist) continue;
const range = distPair[1];
const threshold = isLine ? MinLinePointsSize : MinPointsSize;
if (getRangeSize(range) <= threshold) {
if (!isRangeSafe(range, points.length)) return NaN;
if (isLine) {
const tempDist = lineToPolygonDistance(points, range, polygon, ruler);
if (isNaN(tempDist) || tempDist === 0) return tempDist;
miniDist = Math.min(miniDist, tempDist);
} else for (let i = range[0]; i <= range[1]; ++i) {
const tempDist = pointToPolygonDistance(points[i], polygon, ruler);
miniDist = Math.min(miniDist, tempDist);
if (miniDist === 0) return 0;
}
} else {
const newRangesA = splitRange(range, isLine);
updateQueue(distQueue, miniDist, ruler, points, polyBBox, newRangesA[0]);
updateQueue(distQueue, miniDist, ruler, points, polyBBox, newRangesA[1]);
}
}
return miniDist;
}
function pointSetToPointSetDistance(pointSet1, isLine1, pointSet2, isLine2, ruler, currentMiniDist = Infinity) {
let miniDist = Math.min(currentMiniDist, ruler.distance(pointSet1[0], pointSet2[0]));
if (miniDist === 0) return miniDist;
const distQueue = new TinyQueue$1([[
0,
[0, pointSet1.length - 1],
[0, pointSet2.length - 1]
]], compareDistPair);
while (distQueue.length > 0) {
const distPair = distQueue.pop();
if (distPair[0] >= miniDist) continue;
const rangeA = distPair[1];
const rangeB = distPair[2];
const threshold1 = isLine1 ? MinLinePointsSize : MinPointsSize;
const threshold2 = isLine2 ? MinLinePointsSize : MinPointsSize;
if (getRangeSize(rangeA) <= threshold1 && getRangeSize(rangeB) <= threshold2) {
if (!isRangeSafe(rangeA, pointSet1.length) && isRangeSafe(rangeB, pointSet2.length)) return NaN;
let tempDist;
if (isLine1 && isLine2) {
tempDist = lineToLineDistance(pointSet1, rangeA, pointSet2, rangeB, ruler);
miniDist = Math.min(miniDist, tempDist);
} else if (isLine1 && !isLine2) {
const sublibe = pointSet1.slice(rangeA[0], rangeA[1] + 1);
for (let i = rangeB[0]; i <= rangeB[1]; ++i) {
tempDist = pointToLineDistance(pointSet2[i], sublibe, ruler);
miniDist = Math.min(miniDist, tempDist);
if (miniDist === 0) return miniDist;
}
} else if (!isLine1 && isLine2) {
const sublibe = pointSet2.slice(rangeB[0], rangeB[1] + 1);
for (let i = rangeA[0]; i <= rangeA[1]; ++i) {
tempDist = pointToLineDistance(pointSet1[i], sublibe, ruler);
miniDist = Math.min(miniDist, tempDist);
if (miniDist === 0) return miniDist;
}
} else {
tempDist = pointsToPointsDistance(pointSet1, rangeA, pointSet2, rangeB, ruler);
miniDist = Math.min(miniDist, tempDist);
}
} else {
const newRangesA = splitRange(rangeA, isLine1);
const newRangesB = splitRange(rangeB, isLine2);
updateQueueTwoSets(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[0], newRangesB[0]);
updateQueueTwoSets(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[0], newRangesB[1]);
updateQueueTwoSets(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[1], newRangesB[0]);
updateQueueTwoSets(distQueue, miniDist, ruler, pointSet1, pointSet2, newRangesA[1], newRangesB[1]);
}
}
return miniDist;
}
function pointToGeometryDistance(ctx, geometries) {
const tilePoints = ctx.geometry();
const pointPosition = tilePoints.flat().map((p) => getLngLatFromTileCoord([p.x, p.y], ctx.canonical));
if (tilePoints.length === 0) return NaN;
const ruler = new CheapRuler(pointPosition[0][1]);
let dist = Infinity;
for (const geometry of geometries) {
switch (geometry.type) {
case "Point":
dist = Math.min(dist, pointSetToPointSetDistance(pointPosition, false, [geometry.coordinates], false, ruler, dist));
break;
case "LineString":
dist = Math.min(dist, pointSetToPointSetDistance(pointPosition, false, geometry.coordinates, true, ruler, dist));
break;
case "Polygon": dist = Math.min(dist, pointsToPolygonDistance(pointPosition, false, geometry.coordinates, ruler, dist));
}
if (dist === 0) return dist;
}
return dist;
}
function lineStringToGeometryDistance(ctx, geometries) {
const tileLine = ctx.geometry();
const linePositions = tileLine.flat().map((p) => getLngLatFromTileCoord([p.x, p.y], ctx.canonical));
if (tileLine.length === 0) return NaN;
const ruler = new CheapRuler(linePositions[0][1]);
let dist = Infinity;
for (const geometry of geometries) {
switch (geometry.type) {
case "Point":
dist = Math.min(dist, pointSetToPointSetDistance(linePositions, true, [geometry.coordinates], false, ruler, dist));
break;
case "LineString":
dist = Math.min(dist, pointSetToPointSetDistance(linePositions, true, geometry.coordinates, true, ruler, dist));
break;
case "Polygon": dist = Math.min(dist, pointsToPolygonDistance(linePositions, true, geometry.coordinates, ruler, dist));
}
if (dist === 0) return dist;
}
return dist;
}
function polygonToGeometryDistance(ctx, geometries) {
const tilePolygon = ctx.geometry();
if (tilePolygon.length === 0 || tilePolygon[0].length === 0) return NaN;
const polygons = classifyRings$1(tilePolygon, 0).map((polygon) => {
return polygon.map((ring) => {
return ring.map((p) => getLngLatFromTileCoord([p.x, p.y], ctx.canonical));
});
});
const ruler = new CheapRuler(polygons[0][0][0][1]);
let dist = Infinity;
for (const geometry of geometries) for (const polygon of polygons) {
switch (geometry.type) {
case "Point":
dist = Math.min(dist, pointsToPolygonDistance([geometry.coordinates], false, polygon, ruler, dist));
break;
case "LineString":
dist = Math.min(dist, pointsToPolygonDistance(geometry.coordinates, true, polygon, ruler, dist));
break;
case "Polygon": dist = Math.min(dist, polygonToPolygonDistance(polygon, geometry.coordinates, ruler, dist));
}
if (dist === 0) return dist;
}
return dist;
}
function toSimpleGeometry(geometry) {
if (geometry.type === "MultiPolygon") return geometry.coordinates.map((polygon) => {
return {
type: "Polygon",
coordinates: polygon
};
});
if (geometry.type === "MultiLineString") return geometry.coordinates.map((lineString) => {
return {
type: "LineString",
coordinates: lineString
};
});
if (geometry.type === "MultiPoint") return geometry.coordinates.map((point) => {
return {
type: "Point",
coordinates: point
};
});
return [geometry];
}
var Distance = class Distance {
constructor(geojson, geometries) {
this.type = NumberType;
this.geojson = geojson;
this.geometries = geometries;
}
static parse(args, context) {
if (args.length !== 2) return context.error(`'distance' expression requires exactly one argument, but found ${args.length - 1} instead.`);
if (isValue(args[1])) {
const geojson = args[1];
if (geojson.type === "FeatureCollection") return new Distance(geojson, geojson.features.map((feature) => toSimpleGeometry(feature.geometry)).flat());
else if (geojson.type === "Feature") return new Distance(geojson, toSimpleGeometry(geojson.geometry));
else if ("type" in geojson && "coordinates" in geojson) return new Distance(geojson, toSimpleGeometry(geojson));
}
return context.error("'distance' expression requires valid geojson object that contains polygon geometry type.");
}
evaluate(ctx) {
if (ctx.geometry() != null && ctx.canonicalID() != null) {
if (ctx.geometryType() === "Point") return pointToGeometryDistance(ctx, this.geometries);
else if (ctx.geometryType() === "LineString") return lineStringToGeometryDistance(ctx, this.geometries);
else if (ctx.geometryType() === "Polygon") return polygonToGeometryDistance(ctx, this.geometries);
}
return NaN;
}
eachChild() {}
outputDefined() {
return true;
}
};
var GlobalState = class GlobalState {
constructor(key) {
this.key = key;
this.type = ValueType;
}
static parse(args, context) {
if (args.length !== 2) return context.error(`Expected 1 argument, but found ${args.length - 1} instead.`);
const key = args[1];
if (key === void 0 || key === null) return context.error("Global state property must be defined.");
if (typeof key !== "string") return context.error(`Global state property must be string, but found ${typeof args[1]} instead.`);
return new GlobalState(key);
}
evaluate(ctx) {
const globalState = ctx.globals?.globalState;
if (!globalState || Object.keys(globalState).length === 0) return null;
return getOwn(globalState, this.key) ?? null;
}
eachChild() {}
outputDefined() {
return false;
}
};
const expressions = {
"==": Equals,
"!=": NotEquals,
">": GreaterThan,
"<": LessThan,
">=": GreaterThanOrEqual,
"<=": LessThanOrEqual,
array: Assertion,
at: At,
boolean: Assertion,
case: Case,
coalesce: Coalesce,
collator: CollatorExpression,
format: FormatExpression,
image: ImageExpression,
in: In,
"index-of": IndexOf,
interpolate: Interpolate,
"interpolate-hcl": Interpolate,
"interpolate-lab": Interpolate,
length: Length,
let: Let,
literal: Literal,
match: Match,
number: Assertion,
"number-format": NumberFormat,
object: Assertion,
slice: Slice,
step: Step,
string: Assertion,
"to-boolean": Coercion,
"to-color": Coercion,
"to-number": Coercion,
"to-string": Coercion,
var: Var,
within: Within,
distance: Distance,
"global-state": GlobalState
};
var CompoundExpression = class CompoundExpression {
constructor(name, type, evaluate, args, key) {
this.name = name;
this.type = type;
this._evaluate = evaluate;
this.args = args;
this.key = key;
}
evaluate(ctx) {
return this._evaluate(ctx, this.args, this.key);
}
eachChild(fn) {
this.args.forEach(fn);
}
outputDefined() {
return false;
}
static parse(args, context) {
const op = args[0];
const definition = CompoundExpression.definitions[op];
if (!definition) return context.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
const type = Array.isArray(definition) ? definition[0] : definition.type;
const availableOverloads = Array.isArray(definition) ? [[definition[1], definition[2]]] : definition.overloads;
const overloads = availableOverloads.filter(([signature]) => !Array.isArray(signature) || signature.length === args.length - 1);
let signatureContext = null;
for (const [params, evaluate] of overloads) {
signatureContext = new ParsingContext(context.registry, isExpressionConstant, context.path, null, context.scope);
const parsedArgs = [];
let argParseFailed = false;
for (let i = 1; i < args.length; i++) {
const arg = args[i];
const expectedType = Array.isArray(params) ? params[i - 1] : params.type;
const parsed = signatureContext.parse(arg, 1 + parsedArgs.length, expectedType);
if (!parsed) {
argParseFailed = true;
break;
}
parsedArgs.push(parsed);
}
if (argParseFailed) continue;
if (Array.isArray(params)) {
if (params.length !== parsedArgs.length) {
signatureContext.error(`Expected ${params.length} arguments, but found ${parsedArgs.length} instead.`);
continue;
}
}
for (let i = 0; i < parsedArgs.length; i++) {
const expected = Array.isArray(params) ? params[i] : params.type;
const arg = parsedArgs[i];
signatureContext.concat(i + 1).checkSubtype(expected, arg.type);
}
if (signatureContext.errors.length === 0) return new CompoundExpression(op, type, evaluate, parsedArgs, context.key);
}
if (overloads.length === 1) context.errors.push(...signatureContext.errors);
else {
const signatures = (overloads.length ? overloads : availableOverloads).map(([params]) => stringifySignature(params)).join(" | ");
const actualTypes = [];
for (let i = 1; i < args.length; i++) {
const parsed = context.parse(args[i], 1 + actualTypes.length);
if (!parsed) return null;
actualTypes.push(typeToString(parsed.type));
}
context.error(`Expected arguments of type ${signatures}, but found (${actualTypes.join(", ")}) instead.`);
}
return null;
}
static register(registry, definitions) {
CompoundExpression.definitions = definitions;
for (const name in definitions) registry[name] = CompoundExpression;
}
};
function rgba(ctx, [r, g, b, a], key) {
r = r.evaluate(ctx);
g = g.evaluate(ctx);
b = b.evaluate(ctx);
const alpha = a ? a.evaluate(ctx) : 1;
const error = validateRGBA(r, g, b, alpha);
if (error) throw new RuntimeError(error, key);
return new Color(r / 255, g / 255, b / 255, alpha, false);
}
function has(key, obj) {
return key in obj && obj[key] !== void 0;
}
function get(key, obj) {
const v = obj[key];
return typeof v === "undefined" ? null : v;
}
function binarySearch(v, a, i, j) {
while (i <= j) {
const m = i + j >> 1;
if (a[m] === v) return true;
if (a[m] > v) j = m - 1;
else i = m + 1;
}
return false;
}
function varargs(type) {
return { type };
}
CompoundExpression.register(expressions, {
error: [
ErrorType,
[StringType],
(ctx, [v], key) => {
throw new RuntimeError(v.evaluate(ctx), key);
}
],
typeof: [
StringType,
[ValueType],
(ctx, [v]) => typeToString(typeOf(v.evaluate(ctx)))
],
"to-rgba": [
array(NumberType, 4),
[ColorType],
(ctx, [v]) => {
const [r, g, b, a] = v.evaluate(ctx).rgb;
return [
r * 255,
g * 255,
b * 255,
a
];
}
],
rgb: [
ColorType,
[
NumberType,
NumberType,
NumberType
],
rgba
],
rgba: [
ColorType,
[
NumberType,
NumberType,
NumberType,
NumberType
],
rgba
],
has: {
type: BooleanType,
overloads: [[[StringType], (ctx, [key]) => has(key.evaluate(ctx), ctx.properties())], [[StringType, ObjectType], (ctx, [key, obj]) => has(key.evaluate(ctx), obj.evaluate(ctx))]]
},
get: {
type: ValueType,
overloads: [[[StringType], (ctx, [key]) => get(key.evaluate(ctx), ctx.properties())], [[StringType, ObjectType], (ctx, [key, obj]) => get(key.evaluate(ctx), obj.evaluate(ctx))]]
},
"feature-state": [
ValueType,
[StringType],
(ctx, [key]) => get(key.evaluate(ctx), ctx.featureState || {})
],
properties: [
ObjectType,
[],
(ctx) => ctx.properties()
],
"geometry-type": [
StringType,
[],
(ctx) => ctx.geometryType()
],
id: [
ValueType,
[],
(ctx) => ctx.id()
],
zoom: [
NumberType,
[],
(ctx) => ctx.globals.zoom
],
"heatmap-density": [
NumberType,
[],
(ctx) => ctx.globals.heatmapDensity || 0
],
elevation: [
NumberType,
[],
(ctx) => ctx.globals.elevation || 0
],
"line-progress": [
NumberType,
[],
(ctx) => ctx.globals.lineProgress || 0
],
accumulated: [
ValueType,
[],
(ctx) => ctx.globals.accumulated === void 0 ? null : ctx.globals.accumulated
],
"+": [
NumberType,
varargs(NumberType),
(ctx, args) => {
let result = 0;
for (const arg of args) result += arg.evaluate(ctx);
return result;
}
],
"*": [
NumberType,
varargs(NumberType),
(ctx, args) => {
let result = 1;
for (const arg of args) result *= arg.evaluate(ctx);
return result;
}
],
"-": {
type: NumberType,
overloads: [[[NumberType, NumberType], (ctx, [a, b]) => a.evaluate(ctx) - b.evaluate(ctx)], [[NumberType], (ctx, [a]) => -a.evaluate(ctx)]]
},
"/": [
NumberType,
[NumberType, NumberType],
(ctx, [a, b]) => a.evaluate(ctx) / b.evaluate(ctx)
],
"%": [
NumberType,
[NumberType, NumberType],
(ctx, [a, b]) => a.evaluate(ctx) % b.evaluate(ctx)
],
ln2: [
NumberType,
[],
() => Math.LN2
],
pi: [
NumberType,
[],
() => Math.PI
],
e: [
NumberType,
[],
() => Math.E
],
"^": [
NumberType,
[NumberType, NumberType],
(ctx, [b, e]) => Math.pow(b.evaluate(ctx), e.evaluate(ctx))
],
sqrt: [
NumberType,
[NumberType],
(ctx, [x]) => Math.sqrt(x.evaluate(ctx))
],
log10: [
NumberType,
[NumberType],
(ctx, [n]) => Math.log(n.evaluate(ctx)) / Math.LN10
],
ln: [
NumberType,
[NumberType],
(ctx, [n]) => Math.log(n.evaluate(ctx))
],
log2: [
NumberType,
[NumberType],
(ctx, [n]) => Math.log(n.evaluate(ctx)) / Math.LN2
],
sin: [
NumberType,
[NumberType],
(ctx, [n]) => Math.sin(n.evaluate(ctx))
],
cos: [
NumberType,
[NumberType],
(ctx, [n]) => Math.cos(n.evaluate(ctx))
],
tan: [
NumberType,
[NumberType],
(ctx, [n]) => Math.tan(n.evaluate(ctx))
],
asin: [
NumberType,
[NumberType],
(ctx, [n]) => Math.asin(n.evaluate(ctx))
],
acos: [
NumberType,
[NumberType],
(ctx, [n]) => Math.acos(n.evaluate(ctx))
],
atan: [
NumberType,
[NumberType],
(ctx, [n]) => Math.atan(n.evaluate(ctx))
],
min: [
NumberType,
varargs(NumberType),
(ctx, args) => Math.min(...args.map((arg) => arg.evaluate(ctx)))
],
max: [
NumberType,
varargs(NumberType),
(ctx, args) => Math.max(...args.map((arg) => arg.evaluate(ctx)))
],
abs: [
NumberType,
[NumberType],
(ctx, [n]) => Math.abs(n.evaluate(ctx))
],
round: [
NumberType,
[NumberType],
(ctx, [n]) => {
const v = n.evaluate(ctx);
return v < 0 ? -Math.round(-v) : Math.round(v);
}
],
floor: [
NumberType,
[NumberType],
(ctx, [n]) => Math.floor(n.evaluate(ctx))
],
ceil: [
NumberType,
[NumberType],
(ctx, [n]) => Math.ceil(n.evaluate(ctx))
],
"filter-==": [
BooleanType,
[StringType, ValueType],
(ctx, [k, v]) => ctx.properties()[k.value] === v.value
],
"filter-id-==": [
BooleanType,
[ValueType],
(ctx, [v]) => ctx.id() === v.value
],
"filter-type-==": [
BooleanType,
[StringType],
(ctx, [v]) => ctx.geometryType() === v.value
],
"filter-<": [
BooleanType,
[StringType, ValueType],
(ctx, [k, v]) => {
const a = ctx.properties()[k.value];
const b = v.value;
return typeof a === typeof b && a < b;
}
],
"filter-id-<": [
BooleanType,
[ValueType],
(ctx, [v]) => {
const a = ctx.id();
const b = v.value;
return typeof a === typeof b && a < b;
}
],
"filter->": [
BooleanType,
[StringType, ValueType],
(ctx, [k, v]) => {
const a = ctx.properties()[k.value];
const b = v.value;
return typeof a === typeof b && a > b;
}
],
"filter-id->": [
BooleanType,
[ValueType],
(ctx, [v]) => {
const a = ctx.id();
const b = v.value;
return typeof a === typeof b && a > b;
}
],
"filter-<=": [
BooleanType,
[StringType, ValueType],
(ctx, [k, v]) => {
const a = ctx.properties()[k.value];
const b = v.value;
return typeof a === typeof b && a <= b;
}
],
"filter-id-<=": [
BooleanType,
[ValueType],
(ctx, [v]) => {
const a = ctx.id();
const b = v.value;
return typeof a === typeof b && a <= b;
}
],
"filter->=": [
BooleanType,
[StringType, ValueType],
(ctx, [k, v]) => {
const a = ctx.properties()[k.value];
const b = v.value;
return typeof a === typeof b && a >= b;
}
],
"filter-id->=": [
BooleanType,
[ValueType],
(ctx, [v]) => {
const a = ctx.id();
const b = v.value;
return typeof a === typeof b && a >= b;
}
],
"filter-has": [
BooleanType,
[ValueType],
(ctx, [k]) => {
const key = k.value;
const props = ctx.properties();
return key in props && props[key] !== void 0;
}
],
"filter-has-id": [
BooleanType,
[],
(ctx) => ctx.id() !== null && ctx.id() !== void 0
],
"filter-type-in": [
BooleanType,
[array(StringType)],
(ctx, [v]) => v.value.indexOf(ctx.geometryType()) >= 0
],
"filter-id-in": [
BooleanType,
[array(ValueType)],
(ctx, [v]) => v.value.indexOf(ctx.id()) >= 0
],
"filter-in-small": [
BooleanType,
[StringType, array(ValueType)],
(ctx, [k, v]) => v.value.indexOf(ctx.properties()[k.value]) >= 0
],
"filter-in-large": [
BooleanType,
[StringType, array(ValueType)],
(ctx, [k, v]) => binarySearch(ctx.properties()[k.value], v.value, 0, v.value.length - 1)
],
all: {
type: BooleanType,
overloads: [[[BooleanType, BooleanType], (ctx, [a, b]) => a.evaluate(ctx) && b.evaluate(ctx)], [varargs(BooleanType), (ctx, args) => {
for (const arg of args) if (!arg.evaluate(ctx)) return false;
return true;
}]]
},
any: {
type: BooleanType,
overloads: [[[BooleanType, BooleanType], (ctx, [a, b]) => a.evaluate(ctx) || b.evaluate(ctx)], [varargs(BooleanType), (ctx, args) => {
for (const arg of args) if (arg.evaluate(ctx)) return true;
return false;
}]]
},
"!": [
BooleanType,
[BooleanType],
(ctx, [b]) => !b.evaluate(ctx)
],
"is-supported-script": [
BooleanType,
[StringType],
(ctx, [s]) => {
const isSupportedScript = ctx.globals && ctx.globals.isSupportedScript;
if (isSupportedScript) return isSupportedScript(s.evaluate(ctx));
return true;
}
],
upcase: [
StringType,
[StringType],
(ctx, [s]) => s.evaluate(ctx).toUpperCase()
],
downcase: [
StringType,
[StringType],
(ctx, [s]) => s.evaluate(ctx).toLowerCase()
],
concat: [
StringType,
varargs(ValueType),
(ctx, args) => args.map((arg) => valueToString(arg.evaluate(ctx))).join("")
],
split: [
array(StringType),
[StringType, StringType],
(ctx, [s, delim]) => s.evaluate(ctx).split(delim.evaluate(ctx))
],
join: [
StringType,
[array(StringType), StringType],
(ctx, [arr, delim]) => arr.evaluate(ctx).join(delim.evaluate(ctx))
],
"resolved-locale": [
StringType,
[CollatorType],
(ctx, [collator]) => collator.evaluate(ctx).resolvedLocale()
]
});
function stringifySignature(signature) {
if (Array.isArray(signature)) return `(${signature.map(typeToString).join(", ")})`;
else return `(${typeToString(signature.type)}...)`;
}
function isExpressionConstant(expression) {
if (expression instanceof Var) return isExpressionConstant(expression.boundExpression);
else if (expression instanceof CompoundExpression && expression.name === "error") return false;
else if (expression instanceof CollatorExpression) return false;
else if (expression instanceof Within) return false;
else if (expression instanceof Distance) return false;
else if (expression instanceof GlobalState) return false;
const isTypeAnnotation = expression instanceof Coercion || expression instanceof Assertion;
let childrenConstant = true;
expression.eachChild((child) => {
if (isTypeAnnotation) childrenConstant = childrenConstant && isExpressionConstant(child);
else childrenConstant = childrenConstant && child instanceof Literal;
});
if (!childrenConstant) return false;
return isFeatureConstant(expression) && isGlobalPropertyConstant(expression, [
"zoom",
"heatmap-density",
"elevation",
"line-progress",
"accumulated",
"is-supported-script"
]);
}
function isFeatureConstant(e) {
if (e instanceof CompoundExpression) {
if (e.name === "get" && e.args.length === 1) return false;
else if (e.name === "feature-state") return false;
else if (e.name === "has" && e.args.length === 1) return false;
else if (e.name === "properties" || e.name === "geometry-type" || e.name === "id") return false;
else if (/^filter-/.test(e.name)) return false;
}
if (e instanceof Within) return false;
if (e instanceof Distance) return false;
let result = true;
e.eachChild((arg) => {
if (result && !isFeatureConstant(arg)) result = false;
});
return result;
}
function isStateConstant(e) {
if (e instanceof CompoundExpression) {
if (e.name === "feature-state") return false;
}
let result = true;
e.eachChild((arg) => {
if (result && !isStateConstant(arg)) result = false;
});
return result;
}
function isGlobalPropertyConstant(e, properties) {
if (e instanceof CompoundExpression && properties.indexOf(e.name) >= 0) return false;
let result = true;
e.eachChild((arg) => {
if (result && !isGlobalPropertyConstant(arg, properties)) result = false;
});
return result;
}
function success(value) {
return {
result: "success",
value
};
}
function error(value) {
return {
result: "error",
value
};
}
function supportsPropertyExpression(spec) {
return spec["property-type"] === "data-driven" || spec["property-type"] === "cross-faded-data-driven";
}
function supportsZoomExpression(spec) {
return !!spec.expression && spec.expression.parameters.indexOf("zoom") > -1;
}
function supportsInterpolation(spec) {
return !!spec.expression && spec.expression.interpolated;
}
function extendBy(output, ...inputs) {
for (const input of inputs) for (const k in input) output[k] = input[k];
return output;
}
function getType(val) {
if (val instanceof Number) return "number";
else if (val instanceof String) return "string";
else if (val instanceof Boolean) return "boolean";
else if (Array.isArray(val)) return "array";
else if (val === null) return "null";
else return typeof val;
}
function isFunction(value) {
return typeof value === "object" && value !== null && !Array.isArray(value) && typeOf(value) === ObjectType;
}
function identityFunction(x) {
return x;
}
function getParseFunction(propertySpec) {
switch (propertySpec.type) {
case "color": return Color.parse;
case "padding": return Padding.parse;
case "numberArray": return NumberArray.parse;
case "colorArray": return ColorArray.parse;
default: return null;
}
}
function getInnerFunction(type) {
switch (type) {
case "exponential": return evaluateExponentialFunction;
case "interval": return evaluateIntervalFunction;
case "categorical": return evaluateCategoricalFunction;
case "identity": return evaluateIdentityFunction;
default: throw new Error(`Unknown function type "${type}"`);
}
}
function createFunction(parameters, propertySpec) {
const zoomAndFeatureDependent = parameters.stops && typeof parameters.stops[0][0] === "object";
const featureDependent = zoomAndFeatureDependent || parameters.property !== void 0;
const zoomDependent = zoomAndFeatureDependent || !featureDependent;
const type = parameters.type || (supportsInterpolation(propertySpec) ? "exponential" : "interval");
const parseFn = getParseFunction(propertySpec);
if (parseFn) {
parameters = extendBy({}, parameters);
if (parameters.stops) parameters.stops = parameters.stops.map((stop) => {
return [stop[0], parseFn(stop[1])];
});
if (parameters.default) parameters.default = parseFn(parameters.default);
else parameters.default = parseFn(propertySpec.default);
}
if (parameters.colorSpace && !isSupportedInterpolationColorSpace(parameters.colorSpace)) throw new Error(`Unknown color space: "${parameters.colorSpace}"`);
const innerFun = getInnerFunction(type);
let hashedStops;
let categoricalKeyType;
if (type === "categorical") {
hashedStops = Object.create(null);
for (const stop of parameters.stops) hashedStops[stop[0]] = stop[1];
categoricalKeyType = typeof parameters.stops[0][0];
}
if (zoomAndFeatureDependent) {
const featureFunctions = {};
const zoomStops = [];
for (let s = 0; s < parameters.stops.length; s++) {
const stop = parameters.stops[s];
const zoom = stop[0].zoom;
if (featureFunctions[zoom] === void 0) {
featureFunctions[zoom] = {
zoom,
type: parameters.type,
property: parameters.property,
default: parameters.default,
stops: []
};
zoomStops.push(zoom);
}
featureFunctions[zoom].stops.push([stop[0].value, stop[1]]);
}
const featureFunctionStops = [];
for (const z of zoomStops) featureFunctionStops.push([featureFunctions[z].zoom, createFunction(featureFunctions[z], propertySpec)]);
const interpolationType = { name: "linear" };
return {
kind: "composite",
interpolationType,
interpolationFactor: Interpolate.interpolationFactor.bind(void 0, interpolationType),
zoomStops: featureFunctionStops.map((s) => s[0]),
evaluate({ zoom }, properties) {
return evaluateExponentialFunction({
stops: featureFunctionStops,
base: parameters.base
}, propertySpec, zoom).evaluate(zoom, properties);
}
};
} else if (zoomDependent) {
const interpolationType = type === "exponential" ? {
name: "exponential",
base: parameters.base !== void 0 ? parameters.base : 1
} : null;
return {
kind: "camera",
interpolationType,
interpolationFactor: Interpolate.interpolationFactor.bind(void 0, interpolationType),
zoomStops: parameters.stops.map((s) => s[0]),
evaluate: ({ zoom }) => innerFun(parameters, propertySpec, zoom, hashedStops, categoricalKeyType)
};
} else return {
kind: "source",
evaluate(_, feature) {
const value = feature && feature.properties ? feature.properties[parameters.property] : void 0;
if (value === void 0) return coalesce$1(parameters.default, propertySpec.default);
return innerFun(parameters, propertySpec, value, hashedStops, categoricalKeyType);
}
};
}
function coalesce$1(a, b, c) {
if (a !== void 0) return a;
if (b !== void 0) return b;
if (c !== void 0) return c;
}
function evaluateCategoricalFunction(parameters, propertySpec, input, hashedStops, keyType) {
return coalesce$1(typeof input === keyType ? hashedStops[input] : void 0, parameters.default, propertySpec.default);
}
function evaluateIntervalFunction(parameters, propertySpec, input) {
if (getType(input) !== "number") return coalesce$1(parameters.default, propertySpec.default);
const n = parameters.stops.length;
if (n === 1) return parameters.stops[0][1];
if (input <= parameters.stops[0][0]) return parameters.stops[0][1];
if (input >= parameters.stops[n - 1][0]) return parameters.stops[n - 1][1];
const index = findStopLessThanOrEqualTo(parameters.stops.map((stop) => stop[0]), input, "");
return parameters.stops[index][1];
}
function evaluateExponentialFunction(parameters, propertySpec, input) {
const base = parameters.base !== void 0 ? parameters.base : 1;
if (getType(input) !== "number") return coalesce$1(parameters.default, propertySpec.default);
const n = parameters.stops.length;
if (n === 1) return parameters.stops[0][1];
if (input <= parameters.stops[0][0]) return parameters.stops[0][1];
if (input >= parameters.stops[n - 1][0]) return parameters.stops[n - 1][1];
const index = findStopLessThanOrEqualTo(parameters.stops.map((stop) => stop[0]), input, "");
const t = interpolationFactor(input, base, parameters.stops[index][0], parameters.stops[index + 1][0]);
const outputLower = parameters.stops[index][1];
const outputUpper = parameters.stops[index + 1][1];
const interp = interpolateFactory[propertySpec.type] || identityFunction;
if (typeof outputLower.evaluate === "function") return { evaluate(...args) {
const evaluatedLower = outputLower.evaluate.apply(void 0, args);
const evaluatedUpper = outputUpper.evaluate.apply(void 0, args);
if (evaluatedLower === void 0 || evaluatedUpper === void 0) return;
return interp(evaluatedLower, evaluatedUpper, t, parameters.colorSpace);
} };
return interp(outputLower, outputUpper, t, parameters.colorSpace);
}
function evaluateIdentityFunction(parameters, propertySpec, input) {
switch (propertySpec.type) {
case "color":
input = Color.parse(input);
break;
case "formatted":
input = Formatted.fromString(input.toString());
break;
case "resolvedImage":
input = ResolvedImage.fromString(input.toString());
break;
case "padding":
input = Padding.parse(input);
break;
case "colorArray":
input = ColorArray.parse(input);
break;
case "numberArray":
input = NumberArray.parse(input);
break;
default: if (getType(input) !== propertySpec.type && (propertySpec.type !== "enum" || !propertySpec.values[input])) input = void 0;
}
return coalesce$1(input, parameters.default, propertySpec.default);
}
/**
* Returns a ratio that can be used to interpolate between exponential function
* stops.
*
* 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,
* and `a` and `b` are constants affording sufficient degrees of freedom to fit
* the function to the given stops.
*
* Here's a bit of algebra that lets us compute `f(x)` directly from the stop
* values without explicitly solving for `a` and `b`:
*
* First stop value: `f(x0) = y0 = a * base^x0 + b`
* Second stop value: `f(x1) = y1 = a * base^x1 + b`
* => `y1 - y0 = a(base^x1 - base^x0)`
* => `a = (y1 - y0)/(base^x1 - base^x0)`
*
* Desired value: `f(x) = y = a * base^x + b`
* => `f(x) = y0 + a * (base^x - base^x0)`
*
* From the above, we can replace the `a` in `a * (base^x - base^x0)` and do a
* little algebra:
* ```
* a * (base^x - base^x0) = (y1 - y0)/(base^x1 - base^x0) * (base^x - base^x0)
* = (y1 - y0) * (base^x - base^x0) / (base^x1 - base^x0)
* ```
*
* If we let `(base^x - base^x0) / (base^x1 base^x0)`, then we have
* `f(x) = y0 + (y1 - y0) * ratio`. In other words, `ratio` may be treated as
* an interpolation factor between the two stops' output values.
*
* (Note: a slightly different form for `ratio`,
* `(base^(x-x0) - 1) / (base^(x1-x0) - 1) `, is equivalent, but requires fewer
* expensive `Math.pow()` operations.)
*
* @private
*/
function interpolationFactor(input, base, lowerValue, upperValue) {
const difference = upperValue - lowerValue;
const progress = input - lowerValue;
if (difference === 0) return 0;
else if (base === 1) return progress / difference;
else return (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1);
}
var StyleExpression = class {
constructor(expression, rootKey, propertySpec, globalState) {
this.expression = expression;
this._warningHistory = {};
this._evaluator = new EvaluationContext();
this._defaultValue = propertySpec ? getDefaultValue(propertySpec) : null;
this._enumValues = propertySpec && propertySpec.type === "enum" ? propertySpec.values : null;
this._globalState = globalState;
this._rootKey = rootKey;
}
evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection) {
if (this._globalState) globals = addGlobalState(globals, this._globalState);
this._evaluator.globals = globals;
this._evaluator.feature = feature;
this._evaluator.featureState = featureState;
this._evaluator.canonical = canonical;
this._evaluator.availableImages = availableImages || null;
this._evaluator.formattedSection = formattedSection;
return this.expression.evaluate(this._evaluator);
}
evaluate(globals, feature, featureState, canonical, availableImages, formattedSection) {
if (this._globalState) globals = addGlobalState(globals, this._globalState);
this._evaluator.globals = globals;
this._evaluator.feature = feature || null;
this._evaluator.featureState = featureState || null;
this._evaluator.canonical = canonical;
this._evaluator.availableImages = availableImages || null;
this._evaluator.formattedSection = formattedSection || null;
try {
const val = this.expression.evaluate(this._evaluator);
if (val === null || val === void 0 || typeof val === "number" && val !== val) return this._defaultValue;
if (this._enumValues && !(val in this._enumValues)) throw new RuntimeError(`Expected value to be one of ${Object.keys(this._enumValues).map((v) => JSON.stringify(v)).join(", ")}, but found ${JSON.stringify(val)} instead.`, "");
return val;
} catch (e) {
const path = e instanceof RuntimeError ? e.path : "";
const dedupKey = `${path}|${e.message}`;
if (!this._warningHistory[dedupKey]) {
this._warningHistory[dedupKey] = true;
if (typeof console !== "undefined") console.warn(formatRuntimeWarning(this._rootKey, path, e.message, this._defaultValue));
}
return this._defaultValue;
}
}
};
/**
* Builds the warning logged when an expression or legacy function fails at
* evaluation: a `rootKey + index path` location prefix, plus the fallback
* value being used.
* @param rootKey Caller-supplied location of the expression in the style JSON
* @param path Index path of the throwing sub-expression ('' for the root)
* @param message The error message from the failed evaluation
* @param defaultValue The value being fallen back to
* @returns The formatted warning string
*/
function formatRuntimeWarning(rootKey, path, message, defaultValue) {
return `${rootKey}${path}: ${message}${defaultValue == null ? "" : ` Falling back to ${String(defaultValue)}.`}`;
}
/**
* Rejects a missing or empty root key. The location prefix is what makes
* runtime warnings actionable, so callers must always supply one; failing
* here surfaces the programmer error at style load instead of producing
* unattributable warnings at render time.
* @param rootKey The root key to check
*/
function assertRootKey(rootKey) {
if (!rootKey) throw new Error("rootKey must identify the location of the expression in the style JSON, e.g. \"layers[3].paint.line-width\".");
}
function isExpression(expression) {
return Array.isArray(expression) && expression.length > 0 && typeof expression[0] === "string" && expression[0] in expressions;
}
/**
* Parse and typecheck the given style spec JSON expression. If
* options.defaultValue is provided, then the resulting StyleExpression's
* `evaluate()` method will handle errors by logging a warning (once per
* message) and returning the default value. Otherwise, it will throw
* evaluation errors.
*
* @private
*/
function createExpression(expression, rootKey, propertySpec, globalState) {
assertRootKey(rootKey);
const parser = new ParsingContext(expressions, isExpressionConstant, [], propertySpec ? getExpectedType(propertySpec) : void 0);
const parsed = parser.parse(expression, void 0, void 0, void 0, propertySpec && propertySpec.type === "string" ? { typeAnnotation: "coerce" } : void 0);
if (!parsed) return error(parser.errors);
return success(new StyleExpression(parsed, rootKey, propertySpec, globalState));
}
var ZoomConstantExpression = class {
constructor(kind, expression, globalState) {
this.kind = kind;
this._styleExpression = expression;
this.isStateDependent = kind !== "constant" && !isStateConstant(expression.expression);
this.globalStateRefs = findGlobalStateRefs(expression.expression);
this._globalState = globalState;
}
evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection) {
if (this._globalState) globals = addGlobalState(globals, this._globalState);
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
}
evaluate(globals, feature, featureState, canonical, availableImages, formattedSection) {
if (this._globalState) globals = addGlobalState(globals, this._globalState);
return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
}
};
var ZoomDependentExpression = class {
constructor(kind, expression, zoomStops, interpolationType, globalState) {
this.kind = kind;
this.zoomStops = zoomStops;
this._styleExpression = expression;
this.isStateDependent = kind !== "camera" && !isStateConstant(expression.expression);
this.globalStateRefs = findGlobalStateRefs(expression.expression);
this.interpolationType = interpolationType;
this._globalState = globalState;
}
evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection) {
if (this._globalState) globals = addGlobalState(globals, this._globalState);
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
}
evaluate(globals, feature, featureState, canonical, availableImages, formattedSection) {
if (this._globalState) globals = addGlobalState(globals, this._globalState);
return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
}
interpolationFactor(input, lower, upper) {
if (this.interpolationType) return Interpolate.interpolationFactor(this.interpolationType, input, lower, upper);
else return 0;
}
};
function isZoomExpression(expression) {
return expression._styleExpression !== void 0;
}
function createPropertyExpression(expressionInput, rootKey, propertySpec, globalState) {
const expression = createExpression(expressionInput, rootKey, propertySpec, globalState);
if (expression.result === "error") return expression;
const parsed = expression.value.expression;
const isFeatureConstantResult = isFeatureConstant(parsed);
if (!isFeatureConstantResult && !supportsPropertyExpression(propertySpec)) return error([new ExpressionParsingError("", "data expressions not supported")]);
const isZoomConstant = isGlobalPropertyConstant(parsed, ["zoom"]);
if (!isZoomConstant && !supportsZoomExpression(propertySpec)) return error([new ExpressionParsingError("", "zoom expressions not supported")]);
const zoomCurve = findZoomCurve(parsed);
if (!zoomCurve && !isZoomConstant) return error([new ExpressionParsingError("", "\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression.")]);
else if (zoomCurve instanceof ExpressionParsingError) return error([zoomCurve]);
else if (zoomCurve instanceof Interpolate && !supportsInterpolation(propertySpec)) return error([new ExpressionParsingError("", "\"interpolate\" expressions cannot be used with this property")]);
if (!zoomCurve) return success(isFeatureConstantResult ? new ZoomConstantExpression("constant", expression.value, globalState) : new ZoomConstantExpression("source", expression.value, globalState));
const interpolationType = zoomCurve instanceof Interpolate ? zoomCurve.interpolation : void 0;
return success(isFeatureConstantResult ? new ZoomDependentExpression("camera", expression.value, zoomCurve.labels, interpolationType, globalState) : new ZoomDependentExpression("composite", expression.value, zoomCurve.labels, interpolationType, globalState));
}
var StylePropertyFunction = class StylePropertyFunction {
constructor(parameters, rootKey, specification) {
this.isStateDependent = false;
this.globalStateRefs = /* @__PURE__ */ new Set();
this._globalState = null;
assertRootKey(rootKey);
this._parameters = parameters;
this._specification = specification;
this._rootKey = rootKey;
this._defaultValue = getDefaultValue(specification);
this._warningHistory = {};
const fn = createFunction(this._parameters, this._specification);
this.kind = fn.kind;
this.interpolationFactor = fn.interpolationFactor;
this.zoomStops = fn.zoomStops;
this.interpolationType = fn.interpolationType;
this._innerEvaluate = fn.evaluate;
}
/**
* Evaluates the legacy function, handling a runtime throw (e.g. interpolating
* mismatched value types) by warning with the property location and falling
* back to the spec default, mirroring {@link StyleExpression.evaluate}.
* @param globals Global evaluation properties (e.g. zoom)
* @param feature The feature being evaluated, if any
* @returns The function result, or the spec default if evaluation throws
*/
evaluate(globals, feature) {
try {
return this._innerEvaluate(globals, feature);
} catch (e) {
const message = e instanceof Error ? e.message : String(e);
const dedupKey = `|${message}`;
if (!this._warningHistory[dedupKey]) {
this._warningHistory[dedupKey] = true;
if (typeof console !== "undefined") console.warn(formatRuntimeWarning(this._rootKey, "", message, this._defaultValue));
}
return this._defaultValue;
}
}
static deserialize(serialized) {
return new StylePropertyFunction(serialized._parameters, serialized._rootKey, serialized._specification);
}
static serialize(input) {
return {
_parameters: input._parameters,
_specification: input._specification,
_rootKey: input._rootKey
};
}
};
function normalizePropertyExpression(value, rootKey, specification, globalState) {
if (isFunction(value)) return new StylePropertyFunction(value, rootKey, specification);
else if (isExpression(value)) {
const expression = createPropertyExpression(value, rootKey, specification, globalState);
if (expression.result === "error") throw new Error(expression.value.map((err) => `${err.key}: ${err.message}`).join(", "));
return expression.value;
} else {
let constant = value;
if (specification.type === "color" && typeof value === "string") constant = Color.parse(value);
else if (specification.type === "padding" && (typeof value === "number" || Array.isArray(value))) constant = Padding.parse(value);
else if (specification.type === "numberArray" && (typeof value === "number" || Array.isArray(value))) constant = NumberArray.parse(value);
else if (specification.type === "colorArray" && (typeof value === "string" || Array.isArray(value))) constant = ColorArray.parse(value);
else if (specification.type === "variableAnchorOffsetCollection" && Array.isArray(value)) constant = VariableAnchorOffsetCollection.parse(value);
else if (specification.type === "projectionDefinition" && typeof value === "string") constant = ProjectionDefinition.parse(value);
return {
globalStateRefs: /* @__PURE__ */ new Set(),
_globalState: null,
kind: "constant",
evaluate: () => constant
};
}
}
function findZoomCurve(expression) {
let result = null;
if (expression instanceof Let) result = findZoomCurve(expression.result);
else if (expression instanceof Coalesce) for (const arg of expression.args) {
result = findZoomCurve(arg);
if (result) break;
}
else if ((expression instanceof Step || expression instanceof Interpolate) && expression.input instanceof CompoundExpression && expression.input.name === "zoom") result = expression;
if (result instanceof ExpressionParsingError) return result;
expression.eachChild((child) => {
const childResult = findZoomCurve(child);
if (childResult instanceof ExpressionParsingError) result = childResult;
else if (!result && childResult) result = new ExpressionParsingError("", "\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression.");
else if (result && childResult && result !== childResult) result = new ExpressionParsingError("", "Only one zoom-based \"step\" or \"interpolate\" subexpression may be used in an expression.");
});
return result;
}
function findGlobalStateRefs(expression, results = /* @__PURE__ */ new Set()) {
if (expression instanceof GlobalState) results.add(expression.key);
expression.eachChild((childExpression) => {
findGlobalStateRefs(childExpression, results);
});
return results;
}
function getExpectedType(spec) {
const types = {
color: ColorType,
string: StringType,
number: NumberType,
enum: StringType,
boolean: BooleanType,
formatted: FormattedType,
padding: PaddingType,
numberArray: NumberArrayType,
colorArray: ColorArrayType,
projectionDefinition: ProjectionDefinitionType,
resolvedImage: ResolvedImageType,
variableAnchorOffsetCollection: VariableAnchorOffsetCollectionType
};
if (spec.type === "array") return array(types[spec.value] || ValueType, spec.length);
return types[spec.type];
}
function getDefaultValue(spec) {
if (spec.type === "color" && isFunction(spec.default)) return new Color(0, 0, 0, 0);
switch (spec.type) {
case "color": return Color.parse(spec.default) || null;
case "padding": return Padding.parse(spec.default) || null;
case "numberArray": return NumberArray.parse(spec.default) || null;
case "colorArray": return ColorArray.parse(spec.default) || null;
case "variableAnchorOffsetCollection": return VariableAnchorOffsetCollection.parse(spec.default) || null;
case "projectionDefinition": return ProjectionDefinition.parse(spec.default) || null;
default: return spec.default === void 0 ? null : spec.default;
}
}
function addGlobalState(globals, globalState) {
const { zoom, heatmapDensity, elevation, lineProgress, isSupportedScript, accumulated } = globals ?? {};
return {
zoom,
heatmapDensity,
elevation,
lineProgress,
isSupportedScript,
accumulated,
globalState
};
}
function classifyChildren(children) {
let sawLegacy = false;
for (const child of children) {
const classification = classifyFilter(child);
if (classification === "expression") return "expression";
if (classification === "legacy") sawLegacy = true;
}
return sawLegacy ? "legacy" : "neutral";
}
function classifyFilter(filter) {
if (typeof filter === "boolean") return "neutral";
if (!Array.isArray(filter) || filter.length === 0) return "legacy";
switch (filter[0]) {
case "has":
if (filter.length < 2 || filter[1] === "$id" || filter[1] === "$type") return "legacy";
return filter.length === 2 ? "neutral" : "expression";
case "in": return filter.length >= 3 && (typeof filter[1] !== "string" || Array.isArray(filter[2])) ? "expression" : "legacy";
case "!in":
case "!has": return "legacy";
case "==":
case "!=":
case ">":
case ">=":
case "<":
case "<=": return filter.length !== 3 || Array.isArray(filter[1]) || Array.isArray(filter[2]) ? "expression" : "legacy";
case "none": return "legacy";
case "any":
case "all": return classifyChildren(filter.slice(1));
default: return "expression";
}
}
function isExpressionFilter(filter) {
return classifyFilter(filter) !== "legacy";
}
function getFilterPropertyExpression(property) {
if (property === "$type") return ["geometry-type"];
if (property === "$id") return ["id"];
return ["get", property];
}
function getLegacyFilterExpressionSuggestion(filter) {
switch (filter[0]) {
case "==":
case "!=":
case "<":
case "<=":
case ">":
case ">=":
if (filter.length !== 3 || typeof filter[1] !== "string") return null;
return [
filter[0],
getFilterPropertyExpression(filter[1]),
filter[2]
];
case "in":
case "!in": {
if (filter.length < 2 || typeof filter[1] !== "string") return null;
const expression = [
"in",
getFilterPropertyExpression(filter[1]),
["literal", filter.slice(2)]
];
return filter[0] === "!in" ? ["!", expression] : expression;
}
case "has":
case "!has": {
if (filter.length !== 2 || typeof filter[1] !== "string") return null;
if (filter[1] === "$type" || filter[1] === "$id") return null;
const expression = ["has", filter[1]];
return filter[0] === "!has" ? ["!", expression] : expression;
}
default: return null;
}
}
function getMixedFilterMessage(filter) {
if ((filter[0] === "<" || filter[0] === "<=" || filter[0] === ">" || filter[0] === ">=") && filter[1] === "$type") return `"$type" cannot be use with operator "${filter[0]}"`;
const suggestion = getLegacyFilterExpressionSuggestion(filter);
if (suggestion) return `Mixing deprecated filter syntax with expression syntax is not supported. Replace ${JSON.stringify(filter)} with ${JSON.stringify(suggestion)}.`;
return `Mixing deprecated filter syntax with expression syntax is not supported. Convert ${JSON.stringify(filter)} to expression syntax.`;
}
function checkChild(index, path, filter) {
const child = filter[index];
if (!Array.isArray(child)) return null;
if (!isExpressionFilter(child)) return {
path: path.concat(index),
legacyFilter: child
};
return findMixedLegacyFilter(child, path.concat(index));
}
function findMixedLegacyFilter(filter, path = []) {
if (!Array.isArray(filter) || filter.length < 1) return null;
switch (filter[0]) {
case "all":
case "any":
case "none":
for (let i = 1; i < filter.length; i++) {
const diagnostic = checkChild(i, path, filter);
if (diagnostic) return diagnostic;
}
break;
case "!": {
const diagnostic = checkChild(1, path, filter);
if (diagnostic) return diagnostic;
break;
}
case "case": for (let i = 1; i < filter.length - 1; i += 2) {
const diagnostic = checkChild(i, path, filter);
if (diagnostic) return diagnostic;
}
}
return null;
}
function warnAboutMixedLegacyFilter(filter, rootKey) {
const diagnostic = findMixedLegacyFilter(filter);
if (!diagnostic || typeof console === "undefined") return;
const path = diagnostic.path.map((index) => `[${index}]`).join("");
console.warn(`${rootKey}${path}: ${getMixedFilterMessage(diagnostic.legacyFilter)}`);
}
const filterSpec = {
type: "boolean",
default: false,
transition: false,
"property-type": "data-driven",
expression: {
interpolated: false,
parameters: ["zoom", "feature"]
}
};
/**
* Given a filter expressed as nested arrays, return a new function
* that evaluates whether a given feature (with a .properties or .tags property)
* passes its test.
*
* @private
* @param filter MapLibre filter
* @param rootKey Location of the filter in the style JSON (e.g. `layers[3].filter`),
* used to prefix runtime warnings
* @param [globalState] Global state object to be used for evaluating 'global-state' expressions
* @returns filter-evaluating function
*/
function featureFilter(filter, rootKey, globalState) {
if (filter === null || filter === void 0) return {
filter: () => true,
needGeometry: false,
getGlobalStateRefs: () => /* @__PURE__ */ new Set()
};
if (!isExpressionFilter(filter)) filter = convertFilter$1(filter);
else warnAboutMixedLegacyFilter(filter, rootKey);
const compiled = createExpression(filter, rootKey, filterSpec, globalState);
if (compiled.result === "error") throw new Error(compiled.value.map((err) => `${err.key}: ${err.message}`).join(", "));
else return {
filter: (globalProperties, feature, canonical) => compiled.value.evaluate(globalProperties, feature, {}, canonical),
needGeometry: geometryNeeded(filter),
getGlobalStateRefs: () => findGlobalStateRefs(compiled.value.expression)
};
}
function compare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
function geometryNeeded(filter) {
if (!Array.isArray(filter)) return false;
if (filter[0] === "within" || filter[0] === "distance") return true;
for (let index = 1; index < filter.length; index++) if (geometryNeeded(filter[index])) return true;
return false;
}
function convertFilter$1(filter) {
if (!filter) return true;
const op = filter[0];
if (filter.length <= 1) return op !== "any";
return op === "==" ? convertComparisonOp$1(filter[1], filter[2], "==") : op === "!=" ? convertNegation(convertComparisonOp$1(filter[1], filter[2], "==")) : op === "<" || op === ">" || op === "<=" || op === ">=" ? convertComparisonOp$1(filter[1], filter[2], op) : op === "any" ? convertDisjunctionOp(filter.slice(1)) : op === "all" ? ["all"].concat(filter.slice(1).map(convertFilter$1)) : op === "none" ? ["all"].concat(filter.slice(1).map(convertFilter$1).map(convertNegation)) : op === "in" ? convertInOp$1(filter[1], filter.slice(2)) : op === "!in" ? convertNegation(convertInOp$1(filter[1], filter.slice(2))) : op === "has" ? convertHasOp$1(filter[1]) : op === "!has" ? convertNegation(convertHasOp$1(filter[1])) : true;
}
function convertComparisonOp$1(property, value, op) {
switch (property) {
case "$type": return [`filter-type-${op}`, value];
case "$id": return [`filter-id-${op}`, value];
default: return [
`filter-${op}`,
property,
value
];
}
}
function convertDisjunctionOp(filters) {
return ["any"].concat(filters.map(convertFilter$1));
}
function convertInOp$1(property, values) {
if (values.length === 0) return false;
switch (property) {
case "$type": return ["filter-type-in", ["literal", values]];
case "$id": return ["filter-id-in", ["literal", values]];
default: if (values.length > 200 && !values.some((v) => typeof v !== typeof values[0])) return [
"filter-in-large",
property,
["literal", values.sort(compare)]
];
else return [
"filter-in-small",
property,
["literal", values]
];
}
}
function convertHasOp$1(property) {
switch (property) {
case "$type": return true;
case "$id": return ["filter-has-id"];
default: return ["filter-has", property];
}
}
function convertNegation(filter) {
return ["!", filter];
}
function stringify$1(obj) {
const type = typeof obj;
if (type === "number" || type === "boolean" || type === "string" || obj === void 0 || obj === null) return JSON.stringify(obj);
if (Array.isArray(obj)) {
let str = "[";
for (const val of obj) str += `${stringify$1(val)},`;
return `${str}]`;
}
const keys = Object.keys(obj).sort();
let str = "{";
for (let i = 0; i < keys.length; i++) str += `${JSON.stringify(keys[i])}:${stringify$1(obj[keys[i]])},`;
return `${str}}`;
}
function getKey(layer) {
let key = "";
for (const k of refProperties) key += `/${stringify$1(layer[k])}`;
return key;
}
/**
* Groups layers by their layout-affecting properties.
* These are the properties that were formerly used by explicit `ref` mechanism
* for layers: 'type', 'source', 'source-layer', 'minzoom', 'maxzoom',
* 'filter', and 'layout'.
*
* The input is not modified. The output layers are references to the
* input layers.
*
* @param layers - an array of {@link LayerSpecification}.
* @param cachedKeys - an object to keep already calculated keys.
* @returns an array of arrays of {@link LayerSpecification} objects, where each inner array
* contains layers that share the same layout-affecting properties.
*/
function groupByLayout(layers, cachedKeys) {
const groups = {};
for (let i = 0; i < layers.length; i++) {
const k = cachedKeys && cachedKeys[layers[i].id] || getKey(layers[i]);
if (cachedKeys) cachedKeys[layers[i].id] = k;
let group = groups[k];
if (!group) group = groups[k] = [];
group.push(layers[i]);
}
const result = [];
for (const k in groups) result.push(groups[k]);
return result;
}
function emptyStyle() {
const style = {};
const version = latest["$version"];
for (const styleKey in latest["$root"]) {
const specification = latest["$root"][styleKey];
if (specification.required) {
let value = null;
if (styleKey === "version") value = version;
else if (specification.type === "array") value = [];
else value = {};
if (value != null) style[styleKey] = value;
}
}
return style;
}
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 [];
}
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);
}
function validateObject(options) {
const key = options.key;
const object = options.value;
const elementSpecs = options.valueSpec || {};
const elementValidators = options.objectElementValidators || {};
const style = options.style;
const styleSpec = options.styleSpec;
const validateSpec = options.validateSpec;
let errors = [];
const type = getType(object);
if (type !== "object") return [new ValidationError(key, object, `object expected, ${type} found`)];
for (const objectKey in object) {
const elementSpecKey = objectKey.split(".")[0];
const elementSpec = getOwn(elementSpecs, elementSpecKey) || elementSpecs["*"];
let validateElement;
if (getOwn(elementValidators, elementSpecKey)) validateElement = elementValidators[elementSpecKey];
else if (getOwn(elementSpecs, elementSpecKey)) {
if (object[objectKey] === void 0) continue;
validateElement = validateSpec;
} else if (elementValidators["*"]) validateElement = elementValidators["*"];
else if (elementSpecs["*"]) validateElement = validateSpec;
else {
errors.push(new ValidationError(key, object[objectKey], `unknown property "${objectKey}"`));
continue;
}
errors = errors.concat(validateElement({
key: (key ? `${key}.` : key) + objectKey,
value: object[objectKey],
valueSpec: elementSpec,
style,
styleSpec,
object,
objectKey,
validateSpec
}, object));
}
for (const elementSpecKey in elementSpecs) {
if (elementValidators[elementSpecKey]) continue;
if (elementSpecs[elementSpecKey].required && elementSpecs[elementSpecKey]["default"] === void 0 && object[elementSpecKey] === void 0) errors.push(new ValidationError(key, object, `missing required property "${elementSpecKey}"`));
}
return errors;
}
function validateArray(options) {
const array = options.value;
const arraySpec = options.valueSpec;
const validateSpec = options.validateSpec;
const style = options.style;
const styleSpec = options.styleSpec;
const key = options.key;
const validateArrayElement = options.arrayElementValidator || validateSpec;
if (getType(array) !== "array") return [new ValidationError(key, array, `array expected, ${getType(array)} found`)];
if (arraySpec.length && array.length !== arraySpec.length) return [new ValidationError(key, array, `array length ${arraySpec.length} expected, length ${array.length} found`)];
let arrayElementSpec = {
type: arraySpec.value,
values: arraySpec.values
};
if (styleSpec.$version < 7) arrayElementSpec["function"] = arraySpec.function;
if (getType(arraySpec.value) === "object") arrayElementSpec = arraySpec.value;
let errors = [];
for (let i = 0; i < array.length; i++) errors = errors.concat(validateArrayElement({
array,
arrayIndex: i,
value: array[i],
valueSpec: arrayElementSpec,
validateSpec: options.validateSpec,
style,
styleSpec,
key: `${key}[${i}]`
}));
return errors;
}
function validateNumber(options) {
const key = options.key;
const value = options.value;
const valueSpec = options.valueSpec;
let type = getType(value);
if (type === "number" && value !== value) type = "NaN";
if (type !== "number") return [new ValidationError(key, value, `number expected, ${type} found`)];
if ("minimum" in valueSpec && value < valueSpec.minimum) return [new ValidationError(key, value, `${value} is less than the minimum value ${valueSpec.minimum}`)];
if ("maximum" in valueSpec && value > valueSpec.maximum) return [new ValidationError(key, value, `${value} is greater than the maximum value ${valueSpec.maximum}`)];
return [];
}
function validateFunction(options) {
const functionValueSpec = options.valueSpec;
const functionType = unbundle(options.value.type);
let stopKeyType;
let stopDomainValues = {};
let previousStopDomainValue;
let previousStopDomainZoom;
const isZoomFunction = functionType !== "categorical" && options.value.property === void 0;
const isPropertyFunction = !isZoomFunction;
const isZoomAndPropertyFunction = getType(options.value.stops) === "array" && getType(options.value.stops[0]) === "array" && getType(options.value.stops[0][0]) === "object";
const errors = validateObject({
key: options.key,
value: options.value,
valueSpec: options.styleSpec.function,
validateSpec: options.validateSpec,
style: options.style,
styleSpec: options.styleSpec,
objectElementValidators: {
stops: validateFunctionStops,
default: validateFunctionDefault
}
});
if (functionType === "identity" && isZoomFunction) errors.push(new ValidationError(options.key, options.value, "missing required property \"property\""));
if (functionType !== "identity" && !options.value.stops) errors.push(new ValidationError(options.key, options.value, "missing required property \"stops\""));
if (functionType === "exponential" && options.valueSpec.expression && !supportsInterpolation(options.valueSpec)) errors.push(new ValidationError(options.key, options.value, "exponential functions not supported"));
if (options.styleSpec.$version >= 8) {
if (isPropertyFunction && !supportsPropertyExpression(options.valueSpec)) errors.push(new ValidationError(options.key, options.value, "property functions not supported"));
else if (isZoomFunction && !supportsZoomExpression(options.valueSpec)) errors.push(new ValidationError(options.key, options.value, "zoom functions not supported"));
}
if ((functionType === "categorical" || isZoomAndPropertyFunction) && options.value.property === void 0) errors.push(new ValidationError(options.key, options.value, "\"property\" property is required"));
return errors;
function validateFunctionStops(options) {
if (functionType === "identity") return [new ValidationError(options.key, options.value, "identity function may not have a \"stops\" property")];
let errors = [];
const value = options.value;
errors = errors.concat(validateArray({
key: options.key,
value,
valueSpec: options.valueSpec,
validateSpec: options.validateSpec,
style: options.style,
styleSpec: options.styleSpec,
arrayElementValidator: validateFunctionStop
}));
if (getType(value) === "array" && value.length === 0) errors.push(new ValidationError(options.key, value, "array must have at least one stop"));
return errors;
}
function validateFunctionStop(options) {
let errors = [];
const value = options.value;
const key = options.key;
if (getType(value) !== "array") return [new ValidationError(key, value, `array expected, ${getType(value)} found`)];
if (value.length !== 2) return [new ValidationError(key, value, `array length 2 expected, length ${value.length} found`)];
if (isZoomAndPropertyFunction) {
if (getType(value[0]) !== "object") return [new ValidationError(key, value, `object expected, ${getType(value[0])} found`)];
if (value[0].zoom === void 0) return [new ValidationError(key, value, "object stop key must have zoom")];
if (value[0].value === void 0) return [new ValidationError(key, value, "object stop key must have value")];
if (previousStopDomainZoom && previousStopDomainZoom > unbundle(value[0].zoom)) return [new ValidationError(key, value[0].zoom, "stop zoom values must appear in ascending order")];
if (unbundle(value[0].zoom) !== previousStopDomainZoom) {
previousStopDomainZoom = unbundle(value[0].zoom);
previousStopDomainValue = void 0;
stopDomainValues = {};
}
errors = errors.concat(validateObject({
key: `${key}[0]`,
value: value[0],
valueSpec: { zoom: {} },
validateSpec: options.validateSpec,
style: options.style,
styleSpec: options.styleSpec,
objectElementValidators: {
zoom: validateNumber,
value: validateStopDomainValue
}
}));
} else errors = errors.concat(validateStopDomainValue({
key: `${key}[0]`,
value: value[0],
valueSpec: {},
validateSpec: options.validateSpec,
style: options.style,
styleSpec: options.styleSpec
}, value));
if (isExpression(deepUnbundle(value[1]))) return errors.concat([new ValidationError(`${key}[1]`, value[1], "expressions are not allowed in function stops.")]);
return errors.concat(options.validateSpec({
key: `${key}[1]`,
value: value[1],
valueSpec: functionValueSpec,
validateSpec: options.validateSpec,
style: options.style,
styleSpec: options.styleSpec
}));
}
function validateStopDomainValue(options, stop) {
const type = getType(options.value);
const value = unbundle(options.value);
const reportValue = options.value !== null ? options.value : stop;
if (!stopKeyType) stopKeyType = type;
else if (type !== stopKeyType) return [new ValidationError(options.key, reportValue, `${type} stop domain type must match previous stop domain type ${stopKeyType}`)];
if (type !== "number" && type !== "string" && type !== "boolean") return [new ValidationError(options.key, reportValue, "stop domain value must be a number, string, or boolean")];
if (type !== "number" && functionType !== "categorical") {
let message = `number expected, ${type} found`;
if (supportsPropertyExpression(functionValueSpec) && functionType === void 0) message += "\nIf you intended to use a categorical function, specify `\"type\": \"categorical\"`.";
return [new ValidationError(options.key, reportValue, message)];
}
if (functionType === "categorical" && type === "number" && (!isFinite(value) || Math.floor(value) !== value)) return [new ValidationError(options.key, reportValue, `integer expected, found ${value}`)];
if (functionType !== "categorical" && type === "number" && previousStopDomainValue !== void 0 && value < previousStopDomainValue) return [new ValidationError(options.key, reportValue, "stop domain values must appear in ascending order")];
else previousStopDomainValue = value;
if (functionType === "categorical" && value in stopDomainValues) return [new ValidationError(options.key, reportValue, "stop domain values must be unique")];
else stopDomainValues[value] = true;
return [];
}
function validateFunctionDefault(options) {
return options.validateSpec({
key: options.key,
value: options.value,
valueSpec: functionValueSpec,
validateSpec: options.validateSpec,
style: options.style,
styleSpec: options.styleSpec
});
}
}
function validateExpression(options) {
const expression = (options.expressionContext === "property" ? createPropertyExpression : createExpression)(deepUnbundle(options.value), options.key, options.valueSpec);
if (expression.result === "error") return expression.value.map((error) => {
return new ValidationError(`${options.key}${error.key}`, options.value, error.message);
});
const expressionObj = expression.value.expression || expression.value._styleExpression.expression;
if (options.expressionContext === "property" && options.propertyKey === "text-font" && !expressionObj.outputDefined()) return [new ValidationError(options.key, options.value, `Invalid data expression for "${options.propertyKey}". Output values must be contained as literals within the expression.`)];
if (options.expressionContext === "property" && options.propertyType === "layout" && !isStateConstant(expressionObj)) return [new ValidationError(options.key, options.value, "\"feature-state\" data expressions are not supported with layout properties.")];
if (options.expressionContext === "filter" && !isStateConstant(expressionObj)) return [new ValidationError(options.key, options.value, "\"feature-state\" data expressions are not supported with filters.")];
if (options.expressionContext && options.expressionContext.indexOf("cluster") === 0) {
if (!isGlobalPropertyConstant(expressionObj, ["zoom", "feature-state"])) return [new ValidationError(options.key, options.value, "\"zoom\" and \"feature-state\" expressions are not supported with cluster properties.")];
if (options.expressionContext === "cluster-initial" && !isFeatureConstant(expressionObj)) return [new ValidationError(options.key, options.value, "Feature data expressions are not supported with initial expression part of cluster properties.")];
}
return [];
}
function validateBoolean(options) {
const value = options.value;
const key = options.key;
const type = getType(value);
if (type !== "boolean") return [new ValidationError(key, value, `boolean expected, ${type} found`)];
return [];
}
function validateColor(options) {
const key = options.key;
const value = options.value;
const type = getType(value);
if (type !== "string") return [new ValidationError(key, value, `color expected, ${type} found`)];
if (!Color.parse(String(value))) return [new ValidationError(key, value, `color expected, "${value}" found`)];
return [];
}
function validateEnum(options) {
const key = options.key;
const value = options.value;
const valueSpec = options.valueSpec;
const errors = [];
if (Array.isArray(valueSpec.values)) {
if (valueSpec.values.indexOf(unbundle(value)) === -1) errors.push(new ValidationError(key, value, `expected one of [${valueSpec.values.join(", ")}], ${JSON.stringify(value)} found`));
} else if (Object.keys(valueSpec.values).indexOf(unbundle(value)) === -1) errors.push(new ValidationError(key, value, `expected one of [${Object.keys(valueSpec.values).join(", ")}], ${JSON.stringify(value)} found`));
return errors;
}
function getValueAtPath(value, path) {
let current = value;
for (const index of path) current = current[index];
return current;
}
/**
* Reports a filter that mixes deprecated syntax into an expression tree as a *warning*.
* @param options The validation options, used for the key and the un-unbundled value
* @param value The unbundled filter to inspect
* @returns A single warning, or an empty array when nothing is mixed
*/
function validateNoMixedLegacyFilter(options, value) {
const diagnostic = findMixedLegacyFilter(value);
if (!diagnostic) return [];
return [new ValidationError(`${options.key}${diagnostic.path.map((index) => `[${index}]`).join("")}`, getValueAtPath(options.value, diagnostic.path), getMixedFilterMessage(diagnostic.legacyFilter), null, "warning")];
}
function validateFilter(options) {
const value = deepUnbundle(options.value);
if (!isExpressionFilter(value)) return validateNonExpressionFilter(options);
return [...validateNoMixedLegacyFilter(options, value), ...validateExpression(extendBy({}, options, {
expressionContext: "filter",
valueSpec: { value: "boolean" }
}))];
}
function validateNonExpressionFilter(options) {
const value = options.value;
const key = options.key;
if (getType(value) !== "array") return [new ValidationError(key, value, `array expected, ${getType(value)} found`)];
const styleSpec = options.styleSpec;
let type;
let errors = [];
if (value.length < 1) return [new ValidationError(key, value, "filter array must have at least 1 element")];
errors = errors.concat(validateEnum({
key: `${key}[0]`,
value: value[0],
valueSpec: styleSpec.filter_operator,
style: options.style,
styleSpec: options.styleSpec
}));
switch (unbundle(value[0])) {
case "<":
case "<=":
case ">":
case ">=": if (value.length >= 2 && unbundle(value[1]) === "$type") errors.push(new ValidationError(key, value, `"$type" cannot be use with operator "${value[0]}"`));
case "==":
case "!=": if (value.length !== 3) errors.push(new ValidationError(key, value, `filter array for operator "${value[0]}" must have 3 elements`));
case "in":
case "!in":
if (value.length >= 2) {
type = getType(value[1]);
if (type !== "string") errors.push(new ValidationError(`${key}[1]`, value[1], `string expected, ${type} found`));
}
for (let i = 2; i < value.length; i++) {
type = getType(value[i]);
if (unbundle(value[1]) === "$type") errors = errors.concat(validateEnum({
key: `${key}[${i}]`,
value: value[i],
valueSpec: styleSpec.geometry_type,
style: options.style,
styleSpec: options.styleSpec
}));
else if (type !== "string" && type !== "number" && type !== "boolean") errors.push(new ValidationError(`${key}[${i}]`, value[i], `string, number, or boolean expected, ${type} found`));
}
break;
case "any":
case "all":
case "none":
for (let i = 1; i < value.length; i++) errors = errors.concat(validateNonExpressionFilter({
key: `${key}[${i}]`,
value: value[i],
style: options.style,
styleSpec: options.styleSpec
}));
break;
case "has":
case "!has":
type = getType(value[1]);
if (value.length !== 2) errors.push(new ValidationError(key, value, `filter array for "${value[0]}" operator must have 2 elements`));
else if (type !== "string") errors.push(new ValidationError(`${key}[1]`, value[1], `string expected, ${type} found`));
}
return errors;
}
function validateProperty(options, propertyType) {
const key = options.key;
const validateSpec = options.validateSpec;
const style = options.style;
const styleSpec = options.styleSpec;
const value = options.value;
const propertyKey = options.objectKey;
const layerSpec = styleSpec[`${propertyType}_${options.layerType}`];
if (!layerSpec) return [];
const transitionMatch = propertyKey.match(/^(.*)-transition$/);
if (propertyType === "paint" && transitionMatch && layerSpec[transitionMatch[1]] && layerSpec[transitionMatch[1]].transition) return validateSpec({
key,
value,
valueSpec: styleSpec.transition,
style,
styleSpec
});
const valueSpec = options.valueSpec || layerSpec[propertyKey];
if (!valueSpec) return [new ValidationError(key, value, `unknown property "${propertyKey}"`)];
let tokenMatch;
if (getType(value) === "string" && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) return [new ValidationError(key, value, `"${propertyKey}" does not support interpolation syntax\nUse an identity property function instead: \`{ "type": "identity", "property": ${JSON.stringify(tokenMatch[1])} }\`.`)];
const errors = [];
if (options.layerType === "symbol") {
if (propertyKey === "text-font" && isFunction(deepUnbundle(value)) && unbundle(value.type) === "identity") errors.push(new ValidationError(key, value, "\"text-font\" does not support identity functions"));
}
return errors.concat(validateSpec({
key: options.key,
value,
valueSpec,
style,
styleSpec,
expressionContext: "property",
propertyType,
propertyKey
}));
}
function validatePaintProperty(options) {
return validateProperty(options, "paint");
}
function validateLayoutProperty(options) {
return validateProperty(options, "layout");
}
function validateLayer(options) {
let errors = [];
const layer = options.value;
const key = options.key;
const style = options.style;
const styleSpec = options.styleSpec;
if (getType(layer) !== "object") return [new ValidationError(key, layer, `object expected, ${getType(layer)} found`)];
if (!layer.type && !layer.ref) errors.push(new ValidationError(key, layer, "either \"type\" or \"ref\" is required"));
let type = unbundle(layer.type);
const ref = unbundle(layer.ref);
if (layer.id) {
const layerId = unbundle(layer.id);
for (let i = 0; i < options.arrayIndex; i++) {
const otherLayer = style.layers[i];
if (unbundle(otherLayer.id) === layerId) errors.push(new ValidationError(key, layer.id, `duplicate layer id "${layer.id}", previously used at line ${otherLayer.id.__line__}`));
}
}
if ("ref" in layer) {
[
"type",
"source",
"source-layer",
"filter",
"layout"
].forEach((p) => {
if (p in layer) errors.push(new ValidationError(key, layer[p], `"${p}" is prohibited for ref layers`));
});
let parent;
style.layers.forEach((layer) => {
if (unbundle(layer.id) === ref) parent = layer;
});
if (!parent) errors.push(new ValidationError(key, layer.ref, `ref layer "${ref}" not found`));
else if (parent.ref) errors.push(new ValidationError(key, layer.ref, "ref cannot reference another ref layer"));
else type = unbundle(parent.type);
} else if (type !== "background") if (!layer.source) errors.push(new ValidationError(key, layer, "missing required property \"source\""));
else {
const source = style.sources && style.sources[layer.source];
const sourceType = source && unbundle(source.type);
if (!source) errors.push(new ValidationError(key, layer.source, `source "${layer.source}" not found`));
else if (sourceType === "vector" && type === "raster") errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a raster source`));
else if (sourceType !== "raster-dem" && type === "hillshade") errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a raster-dem source`));
else if (sourceType !== "raster-dem" && type === "color-relief") errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a raster-dem source`));
else if (sourceType === "raster" && type !== "raster") errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a vector source`));
else if (sourceType === "vector" && !layer["source-layer"]) errors.push(new ValidationError(key, layer, `layer "${layer.id}" must specify a "source-layer"`));
else if (sourceType === "raster-dem" && type !== "hillshade" && type !== "color-relief") errors.push(new ValidationError(key, layer.source, "raster-dem source can only be used with layer type 'hillshade' or 'color-relief'."));
else if (type === "line" && layer.paint && layer.paint["line-gradient"] && (sourceType !== "geojson" || !source.lineMetrics)) errors.push(new ValidationError(key, layer, `layer "${layer.id}" specifies a line-gradient, which requires a GeoJSON source with \`lineMetrics\` enabled.`));
}
if (type === "raster" && layer.paint?.resampling && layer.paint?.["raster-resampling"]) errors.push(new ValidationError(key, layer.paint, `layer "${layer.id}" redundantly specifies "resampling" and "raster-resampling" paint properties, but only one is allowed. It is advised to use "resampling".`));
errors = errors.concat(validateObject({
key,
value: layer,
valueSpec: styleSpec.layer,
style: options.style,
styleSpec: options.styleSpec,
validateSpec: options.validateSpec,
objectElementValidators: {
"*"() {
return [];
},
type() {
return options.validateSpec({
key: `${key}.type`,
value: layer.type,
valueSpec: styleSpec.layer.type,
style: options.style,
styleSpec: options.styleSpec,
validateSpec: options.validateSpec,
object: layer,
objectKey: "type"
});
},
filter: validateFilter,
layout(options) {
return validateObject({
layer,
key: options.key,
value: options.value,
style: options.style,
styleSpec: options.styleSpec,
validateSpec: options.validateSpec,
objectElementValidators: { "*"(options) {
return validateLayoutProperty(extendBy({ layerType: type }, options));
} }
});
},
paint(options) {
return validateObject({
layer,
key: options.key,
value: options.value,
style: options.style,
styleSpec: options.styleSpec,
validateSpec: options.validateSpec,
objectElementValidators: { "*"(options) {
return validatePaintProperty(extendBy({ layerType: type }, options));
} }
});
}
}
}));
return errors;
}
function validateString(options) {
const value = options.value;
const key = options.key;
const type = getType(value);
if (type !== "string") return [new ValidationError(key, value, `string expected, ${type} found`)];
return [];
}
function validateRasterDEMSource(options) {
const sourceName = options.sourceName ?? "";
const rasterDEM = options.value;
const styleSpec = options.styleSpec;
const rasterDEMSpec = styleSpec.source_raster_dem;
const style = options.style;
let errors = [];
const rootType = getType(rasterDEM);
if (rasterDEM === void 0) return errors;
else if (rootType !== "object") {
errors.push(new ValidationError("source_raster_dem", rasterDEM, `object expected, ${rootType} found`));
return errors;
}
const isCustomEncoding = unbundle(rasterDEM.encoding) === "custom";
const customEncodingKeys = [
"redFactor",
"greenFactor",
"blueFactor",
"baseShift"
];
const encodingName = options.value.encoding ? `"${options.value.encoding}"` : "Default";
for (const key in rasterDEM) if (!isCustomEncoding && customEncodingKeys.includes(key)) errors.push(new ValidationError(key, rasterDEM[key], `In "${sourceName}": "${key}" is only valid when "encoding" is set to "custom". ${encodingName} encoding found`));
else if (rasterDEMSpec[key]) errors = errors.concat(options.validateSpec({
key,
value: rasterDEM[key],
valueSpec: rasterDEMSpec[key],
validateSpec: options.validateSpec,
style,
styleSpec
}));
else errors.push(new ValidationError(key, rasterDEM[key], `unknown property "${key}"`));
return errors;
}
const objectElementValidators = { promoteId: validatePromoteId };
function validateSource(options) {
const value = options.value;
const key = options.key;
const styleSpec = options.styleSpec;
const style = options.style;
const validateSpec = options.validateSpec;
if (!value.type) return [new ValidationError(key, value, "\"type\" is required")];
const type = unbundle(value.type);
let errors;
switch (type) {
case "vector":
case "raster":
errors = validateObject({
key,
value,
valueSpec: styleSpec[`source_${type.replace("-", "_")}`],
style: options.style,
styleSpec,
objectElementValidators,
validateSpec
});
return errors;
case "raster-dem":
errors = validateRasterDEMSource({
sourceName: key,
value,
style: options.style,
styleSpec,
validateSpec
});
return errors;
case "geojson":
errors = validateObject({
key,
value,
valueSpec: styleSpec.source_geojson,
style,
styleSpec,
validateSpec,
objectElementValidators
});
if (value.cluster) for (const prop in value.clusterProperties) {
const [operator, mapExpr] = value.clusterProperties[prop];
const reduceExpr = typeof operator === "string" ? [
operator,
["accumulated"],
["get", prop]
] : operator;
errors.push(...validateExpression({
key: `${key}.${prop}.map`,
value: mapExpr,
validateSpec,
expressionContext: "cluster-map"
}));
errors.push(...validateExpression({
key: `${key}.${prop}.reduce`,
value: reduceExpr,
validateSpec,
expressionContext: "cluster-reduce"
}));
}
return errors;
case "video": return validateObject({
key,
value,
valueSpec: styleSpec.source_video,
style,
validateSpec,
styleSpec
});
case "image": return validateObject({
key,
value,
valueSpec: styleSpec.source_image,
style,
validateSpec,
styleSpec
});
case "canvas": return [new ValidationError(key, null, "Please use runtime APIs to add canvas sources, rather than including them in stylesheets.", "source.canvas")];
default: return validateEnum({
key: `${key}.type`,
value: value.type,
valueSpec: { values: [
"vector",
"raster",
"raster-dem",
"geojson",
"video",
"image"
] },
style,
validateSpec,
styleSpec
});
}
}
function validatePromoteId({ key, value }) {
if (getType(value) === "string") return validateString({
key,
value
});
else {
const errors = [];
for (const prop in value) errors.push(...validateString({
key: `${key}.${prop}`,
value: value[prop]
}));
return errors;
}
}
function validateLight(options) {
const light = options.value;
const styleSpec = options.styleSpec;
const lightSpec = styleSpec.light;
const style = options.style;
let errors = [];
const rootType = getType(light);
if (light === void 0) return errors;
else if (rootType !== "object") {
errors = errors.concat([new ValidationError("light", light, `object expected, ${rootType} found`)]);
return errors;
}
for (const key in light) {
const transitionMatch = key.match(/^(.*)-transition$/);
if (transitionMatch && lightSpec[transitionMatch[1]] && lightSpec[transitionMatch[1]].transition) errors = errors.concat(options.validateSpec({
key,
value: light[key],
valueSpec: styleSpec.transition,
validateSpec: options.validateSpec,
style,
styleSpec
}));
else if (lightSpec[key]) errors = errors.concat(options.validateSpec({
key,
value: light[key],
valueSpec: lightSpec[key],
validateSpec: options.validateSpec,
style,
styleSpec
}));
else errors = errors.concat([new ValidationError(key, light[key], `unknown property "${key}"`)]);
}
return errors;
}
function validateSky(options) {
const sky = options.value;
const styleSpec = options.styleSpec;
const skySpec = styleSpec.sky;
const style = options.style;
const rootType = getType(sky);
if (sky === void 0) return [];
else if (rootType !== "object") return [new ValidationError("sky", sky, `object expected, ${rootType} found`)];
let errors = [];
for (const key in sky) if (skySpec[key]) errors = errors.concat(options.validateSpec({
key,
value: sky[key],
valueSpec: skySpec[key],
style,
styleSpec
}));
else errors = errors.concat([new ValidationError(key, sky[key], `unknown property "${key}"`)]);
return errors;
}
function validateTerrain(options) {
const terrain = options.value;
const styleSpec = options.styleSpec;
const terrainSpec = styleSpec.terrain;
const style = options.style;
let errors = [];
const rootType = getType(terrain);
if (terrain === void 0) return errors;
else if (rootType !== "object") {
errors = errors.concat([new ValidationError("terrain", terrain, `object expected, ${rootType} found`)]);
return errors;
}
for (const key in terrain) if (terrainSpec[key]) errors = errors.concat(options.validateSpec({
key,
value: terrain[key],
valueSpec: terrainSpec[key],
validateSpec: options.validateSpec,
style,
styleSpec
}));
else errors = errors.concat([new ValidationError(key, terrain[key], `unknown property "${key}"`)]);
return errors;
}
function validateFormatted(options) {
if (validateString(options).length === 0) return [];
return validateExpression(options);
}
function validateImage(options) {
if (validateString(options).length === 0) return [];
return validateExpression(options);
}
function validatePadding(options) {
const key = options.key;
const value = options.value;
if (getType(value) === "array") {
if (value.length < 1 || value.length > 4) return [new ValidationError(key, value, `padding requires 1 to 4 values; ${value.length} values found`)];
const arrayElementSpec = { type: "number" };
let errors = [];
for (let i = 0; i < value.length; i++) errors = errors.concat(options.validateSpec({
key: `${key}[${i}]`,
value: value[i],
validateSpec: options.validateSpec,
valueSpec: arrayElementSpec
}));
return errors;
} else return validateNumber({
key,
value,
valueSpec: {}
});
}
function validateNumberArray(options) {
const key = options.key;
const value = options.value;
if (getType(value) === "array") {
const arrayElementSpec = { type: "number" };
if (value.length < 1) return [new ValidationError(key, value, "array length at least 1 expected, length 0 found")];
let errors = [];
for (let i = 0; i < value.length; i++) errors = errors.concat(options.validateSpec({
key: `${key}[${i}]`,
value: value[i],
validateSpec: options.validateSpec,
valueSpec: arrayElementSpec
}));
return errors;
} else return validateNumber({
key,
value,
valueSpec: {}
});
}
function validateColorArray(options) {
const key = options.key;
const value = options.value;
if (getType(value) === "array") {
if (value.length < 1) return [new ValidationError(key, value, "array length at least 1 expected, length 0 found")];
let errors = [];
for (let i = 0; i < value.length; i++) errors = errors.concat(validateColor({
key: `${key}[${i}]`,
value: value[i],
valueSpec: {}
}));
return errors;
} else return validateColor({
key,
value,
valueSpec: {}
});
}
function validateVariableAnchorOffsetCollection(options) {
const key = options.key;
const value = options.value;
const type = getType(value);
const styleSpec = options.styleSpec;
if (type !== "array" || value.length < 1 || value.length % 2 !== 0) return [new ValidationError(key, value, "variableAnchorOffsetCollection requires a non-empty array of even length")];
let errors = [];
for (let i = 0; i < value.length; i += 2) {
errors = errors.concat(validateEnum({
key: `${key}[${i}]`,
value: value[i],
valueSpec: styleSpec["layout_symbol"]["text-anchor"]
}));
errors = errors.concat(validateArray({
key: `${key}[${i + 1}]`,
value: value[i + 1],
valueSpec: {
length: 2,
value: "number"
},
validateSpec: options.validateSpec,
style: options.style,
styleSpec
}));
}
return errors;
}
function validateSprite(options) {
let errors = [];
const sprite = options.value;
const key = options.key;
if (!Array.isArray(sprite)) return validateString({
key,
value: sprite
});
else {
const allSpriteIds = [];
const allSpriteURLs = [];
for (const i in sprite) {
if (sprite[i].id && allSpriteIds.includes(sprite[i].id)) errors.push(new ValidationError(key, sprite, `all the sprites' ids must be unique, but ${sprite[i].id} is duplicated`));
allSpriteIds.push(sprite[i].id);
if (sprite[i].url && allSpriteURLs.includes(sprite[i].url)) errors.push(new ValidationError(key, sprite, `all the sprites' URLs must be unique, but ${sprite[i].url} is duplicated`));
allSpriteURLs.push(sprite[i].url);
errors = errors.concat(validateObject({
key: `${key}[${i}]`,
value: sprite[i],
valueSpec: {
id: {
type: "string",
required: true
},
url: {
type: "string",
required: true
}
},
validateSpec: options.validateSpec
}));
}
return errors;
}
}
function validateProjection(options) {
const projection = options.value;
const styleSpec = options.styleSpec;
const projectionSpec = styleSpec.projection;
const style = options.style;
const rootType = getType(projection);
if (projection === void 0) return [];
else if (rootType !== "object") return [new ValidationError("projection", projection, `object expected, ${rootType} found`)];
let errors = [];
for (const key in projection) if (projectionSpec[key]) errors = errors.concat(options.validateSpec({
key,
value: projection[key],
valueSpec: projectionSpec[key],
style,
styleSpec
}));
else errors = errors.concat([new ValidationError(key, projection[key], `unknown property "${key}"`)]);
return errors;
}
function validateProjectionDefinition(options) {
const key = options.key;
let value = options.value;
value = value instanceof String ? value.valueOf() : value;
const type = getType(value);
if (type === "array" && !isProjectionDefinitionValue(value) && !isPropertyValueSpecification(value)) return [new ValidationError(key, value, `projection expected, invalid array ${JSON.stringify(value)} found`)];
else if (!["array", "string"].includes(type)) return [new ValidationError(key, value, `projection expected, invalid type "${type}" found`)];
return [];
}
function isPropertyValueSpecification(value) {
if ([
"interpolate",
"step",
"literal"
].includes(value[0])) return true;
return false;
}
function isProjectionDefinitionValue(value) {
return Array.isArray(value) && value.length === 3 && typeof value[0] === "string" && typeof value[1] === "string" && typeof value[2] === "number";
}
function isObjectLiteral(anything) {
return Boolean(anything) && anything.constructor === Object;
}
function validateState(options) {
if (!isObjectLiteral(options.value)) return [new ValidationError(options.key, options.value, `object expected, ${getType(options.value)} found`)];
return [];
}
function validateFontFaces(options) {
const key = options.key;
const value = options.value;
const validateSpec = options.validateSpec;
const styleSpec = options.styleSpec;
const style = options.style;
if (!isObjectLiteral(value)) return [new ValidationError(key, value, `object expected, ${getType(value)} found`)];
const errors = [];
for (const fontName in value) {
const fontValue = value[fontName];
const fontValueType = getType(fontValue);
if (fontValueType === "string") errors.push(...validateString({
key: `${key}.${fontName}`,
value: fontValue
}));
else if (fontValueType === "array") {
const fontFaceSpec = {
url: {
type: "string",
required: true
},
"unicode-range": {
type: "array",
value: "string"
}
};
for (const [i, fontFace] of fontValue.entries()) errors.push(...validateObject({
key: `${key}.${fontName}[${i}]`,
value: fontFace,
valueSpec: fontFaceSpec,
styleSpec,
style,
validateSpec
}));
} else errors.push(new ValidationError(`${key}.${fontName}`, fontValue, `string or array expected, ${fontValueType} found`));
}
return errors;
}
const VALIDATORS = {
"*"() {
return [];
},
array: validateArray,
boolean: validateBoolean,
number: validateNumber,
color: validateColor,
constants: validateConstants,
enum: validateEnum,
filter: validateFilter,
function: validateFunction,
layer: validateLayer,
object: validateObject,
source: validateSource,
light: validateLight,
sky: validateSky,
terrain: validateTerrain,
projection: validateProjection,
projectionDefinition: validateProjectionDefinition,
string: validateString,
formatted: validateFormatted,
resolvedImage: validateImage,
padding: validatePadding,
numberArray: validateNumberArray,
colorArray: validateColorArray,
variableAnchorOffsetCollection: validateVariableAnchorOffsetCollection,
sprite: validateSprite,
state: validateState,
fontFaces: validateFontFaces
};
/**
* Main recursive validation function used internally.
* You should use `validateStyleMin` in the browser or `validateStyle` in node env.
* @param options - the options object
* @param options.key - string representing location of validation in style tree. Used only
* for more informative error reporting.
* @param options.value - current value from style being evaluated. May be anything from a
* high level object that needs to be descended into deeper or a simple
* scalar value.
* @param options.valueSpec - current spec being evaluated. Tracks value.
* @param options.styleSpec - current full spec being evaluated.
* @param options.validateSpec - the validate function itself
* @param options.style - the style object
* @param options.objectElementValidators - optional object of functions that will be called
* @returns an array of errors, or an empty array if no errors are found.
*/
function validate(options) {
const value = options.value;
const valueSpec = options.valueSpec;
const styleSpec = options.styleSpec;
options.validateSpec = validate;
if (valueSpec.expression && isFunction(unbundle(value))) return validateFunction(options);
else if (valueSpec.expression && isExpression(deepUnbundle(value))) return validateExpression(options);
else if (valueSpec.type && VALIDATORS[valueSpec.type]) return VALIDATORS[valueSpec.type](options);
else return validateObject(extendBy({}, options, { valueSpec: valueSpec.type ? styleSpec[valueSpec.type] : valueSpec }));
}
function validateGlyphsUrl(options) {
const value = options.value;
const key = options.key;
const errors = validateString(options);
if (errors.length) return errors;
if (value.indexOf("{fontstack}") === -1) errors.push(new ValidationError(key, value, "\"glyphs\" url must include a \"{fontstack}\" token"));
if (value.indexOf("{range}") === -1) errors.push(new ValidationError(key, value, "\"glyphs\" url must include a \"{range}\" token"));
return errors;
}
/**
* Validate a MapLibre style against the style specification.
* Use this when running in the browser.
*
* @param style - The style to be validated.
* @param styleSpec - The style specification to validate against.
* If omitted, the latest style spec is used.
* @returns an array of errors, or an empty array if no errors are found.
* @example
* const validate = require('@maplibre/maplibre-gl-style-spec/').validateStyleMin;
* const errors = validate(style);
*/
function validateStyleMin(style, styleSpec = latest) {
let errors = [];
errors = errors.concat(validate({
key: "",
value: style,
valueSpec: styleSpec.$root,
styleSpec,
style,
validateSpec: validate,
objectElementValidators: {
glyphs: validateGlyphsUrl,
"*"() {
return [];
}
}
}));
if (style["constants"]) errors = errors.concat(validateConstants({
key: "constants",
value: style["constants"],
style,
styleSpec,
validateSpec: validate
}));
return sortErrors(errors);
}
validateStyleMin.source = wrapCleanErrors(injectValidateSpec(validateSource));
validateStyleMin.sprite = wrapCleanErrors(injectValidateSpec(validateSprite));
validateStyleMin.glyphs = wrapCleanErrors(injectValidateSpec(validateGlyphsUrl));
validateStyleMin.light = wrapCleanErrors(injectValidateSpec(validateLight));
validateStyleMin.sky = wrapCleanErrors(injectValidateSpec(validateSky));
validateStyleMin.terrain = wrapCleanErrors(injectValidateSpec(validateTerrain));
validateStyleMin.state = wrapCleanErrors(injectValidateSpec(validateState));
validateStyleMin.layer = wrapCleanErrors(injectValidateSpec(validateLayer));
validateStyleMin.filter = wrapCleanErrors(injectValidateSpec(validateFilter));
validateStyleMin.paintProperty = wrapCleanErrors(injectValidateSpec(validatePaintProperty));
validateStyleMin.layoutProperty = wrapCleanErrors(injectValidateSpec(validateLayoutProperty));
function injectValidateSpec(validator) {
return function(options) {
return validator(Object.assign({}, options, { validateSpec: validate }));
};
}
function sortErrors(errors) {
return [].concat(errors).sort((a, b) => {
return a.line - b.line;
});
}
function wrapCleanErrors(inner) {
return function(...args) {
return sortErrors(inner.apply(this, args));
};
}
const visibilitySpec = {
type: "enum",
"property-type": "data-constant",
expression: {
interpolated: false,
parameters: ["global-state"]
},
values: {
visible: {},
none: {}
},
transition: false,
default: "visible"
};
var VisibilityExpressionClass = class {
constructor(visibility, rootKey, globalState) {
this._rootKey = rootKey;
this._globalState = globalState;
this.setValue(visibility);
}
evaluate() {
return this._literalValue ?? this._compiledValue.evaluate({});
}
setValue(visibility) {
if (visibility === null || visibility === void 0 || visibility === "visible" || visibility === "none") {
this._literalValue = visibility === "none" ? "none" : "visible";
this._compiledValue = void 0;
this._globalStateRefs = /* @__PURE__ */ new Set();
return;
}
const compiled = createExpression(visibility, this._rootKey, visibilitySpec, this._globalState);
if (compiled.result === "error") {
this._literalValue = "visible";
this._compiledValue = void 0;
throw new Error(compiled.value.map((err) => `${err.key}: ${err.message}`).join(", "));
}
this._literalValue = void 0;
this._compiledValue = compiled.value;
this._globalStateRefs = findGlobalStateRefs(compiled.value.expression);
}
getGlobalStateRefs() {
return this._globalStateRefs;
}
};
/**
* Creates a visibility expression from a visibility specification.
* @param visibility - the visibility specification, literal or expression
* @param rootKey - location of the visibility value in the style JSON
* (e.g. `layers[3].layout.visibility`), used to prefix runtime warnings
* @param globalState - the global state object
* @returns visibility expression object
*/
function createVisibility(visibility, rootKey, globalState) {
return new VisibilityExpressionClass(visibility, rootKey, globalState);
}
//#endregion
//#region src/style/validate_style.ts
const validateStyle = validateStyleMin;
/**
* The source types the spec has a schema for, and therefore the only ones it can judge. Taken from
* the spec itself so the two cannot drift apart.
*/
const SPEC_SOURCE_TYPES = new Set(Object.keys(latest).filter((key) => key.startsWith("source_")).map((key) => key.slice(7).replaceAll("_", "-")));
/**
* The sources whose type the spec has no schema for, so it rejects them outright even though we
* render them: `canvas`, and anything registered with {@link addSourceType}. They are the renderer's
* business rather than the spec's, so the spec's complaints about them are dropped -- otherwise
* `map.setStyle(map.getStyle())` would fail on a source the user added correctly.
*
* Each such source produces a single error keyed by `sources.<id>`, which is what is matched here.
* @param style - the style about to be validated
* @returns the `sources.<id>` key prefixes whose errors should be ignored
*/
function unjudgeableSourceKeys(style) {
return Object.entries(style.sources ?? {}).filter(([, source]) => !SPEC_SOURCE_TYPES.has(source.type)).map(([id]) => `sources.${id}`);
}
/**
* Validates a whole style and emits what it finds, ignoring the sources the spec cannot judge.
*
* @param emitter - the object to fire {@link ErrorEvent}s on
* @param style - the style to validate
* @returns whether validation failed, i.e. whether the caller should give up on the style
*/
function validateStyleAndEmit(emitter, style) {
const ignored = unjudgeableSourceKeys(style);
return emitValidationErrors(emitter, validateStyle(style).filter(({ message }) => !ignored.some((key) => message.startsWith(`${key}:`) || message.startsWith(`${key}.`))));
}
/**
* Emits everything a validator found, and reports whether any of it was severe enough to abort.
*
* Warnings are logged rather than emitted as errors: the style still renders, just not necessarily
* as its author intended (e.g. a filter mixing deprecated syntax into an expression tree). Treating
* them as errors would abort the whole style load and leave a blank map.
* See https://github.com/maplibre/maplibre-style-spec/issues/1751
*
* @param emitter - the object to fire {@link ErrorEvent}s on
* @param errors - what validation turned up, if anything
* @returns whether validation failed, i.e. whether the caller should give up on the value
*/
function emitValidationErrors(emitter, errors) {
let hasErrors = false;
for (const error of errors) {
if (error.severity === "warning") {
warnOnce(error.message);
continue;
}
emitter.fire(new ErrorEvent(new Error(error.message)));
hasErrors = true;
}
return hasErrors;
}
/**
* Runs a validator over a value and emits whatever it finds.
*
* @param emitter - the object to fire {@link ErrorEvent}s on
* @param validator - the validator to run, e.g. {@link validateFilter}
* @param params - what to validate: the `value`, plus whatever context the validator needs, such as
* the `key` locating it in the style, or the surrounding `style` that {@link validateStyle.layer} looks at
* @param options - setter options; validation is skipped entirely when `validate` is `false`
* @returns whether validation failed, i.e. whether the caller should give up on the value
*/
function validateAndEmit(emitter, validator, params, options) {
if (options?.validate === false) return false;
return emitValidationErrors(emitter, validator({
styleSpec: latest,
...params
}));
}
//#endregion
//#region src/util/transferable_grid_index.ts
const NUM_PARAMS = 3;
var TransferableGridIndex = class TransferableGridIndex {
constructor(extent, n, padding) {
const cells = this.cells = [];
if (extent instanceof ArrayBuffer) {
this.arrayBuffer = extent;
const array = new Int32Array(this.arrayBuffer);
extent = array[0];
n = array[1];
padding = array[2];
this.d = n + 2 * padding;
for (let k = 0; k < this.d * this.d; k++) {
const start = array[NUM_PARAMS + k];
const end = array[NUM_PARAMS + k + 1];
cells.push(start === end ? null : array.subarray(start, end));
}
const keysOffset = array[NUM_PARAMS + cells.length];
const bboxesOffset = array[NUM_PARAMS + cells.length + 1];
this.keys = array.subarray(keysOffset, bboxesOffset);
this.bboxes = array.subarray(bboxesOffset);
this.insert = this._insertReadonly;
} else {
this.d = n + 2 * padding;
for (let i = 0; i < this.d * this.d; i++) cells.push([]);
this.keys = [];
this.bboxes = [];
}
this.n = n;
this.extent = extent;
this.padding = padding;
this.scale = n / extent;
this.uid = 0;
const p = padding / n * extent;
this.min = -p;
this.max = extent + p;
}
insert(key, x1, y1, x2, y2) {
this._forEachCell(x1, y1, x2, y2, this._insertCell, this.uid++, void 0, void 0);
this.keys.push(key);
this.bboxes.push(x1);
this.bboxes.push(y1);
this.bboxes.push(x2);
this.bboxes.push(y2);
}
_insertReadonly() {
throw new Error("Cannot insert into a GridIndex created from an ArrayBuffer.");
}
_insertCell(x1, y1, x2, y2, cellIndex, uid) {
this.cells[cellIndex].push(uid);
}
query(x1, y1, x2, y2, intersectionTest) {
const min = this.min;
const max = this.max;
if (x1 <= min && y1 <= min && max <= x2 && max <= y2 && !intersectionTest) return [...this.keys];
else {
const result = [];
this._forEachCell(x1, y1, x2, y2, this._queryCell, result, {}, intersectionTest);
return result;
}
}
_queryCell(x1, y1, x2, y2, cellIndex, result, seenUids, intersectionTest) {
const cell = this.cells[cellIndex];
if (cell !== null) {
const keys = this.keys;
const bboxes = this.bboxes;
for (const uid of cell) if (seenUids[uid] === void 0) {
const offset = uid * 4;
if (intersectionTest ? intersectionTest(bboxes[offset + 0], bboxes[offset + 1], bboxes[offset + 2], bboxes[offset + 3]) : x1 <= bboxes[offset + 2] && y1 <= bboxes[offset + 3] && x2 >= bboxes[offset + 0] && y2 >= bboxes[offset + 1]) {
seenUids[uid] = true;
result.push(keys[uid]);
} else seenUids[uid] = false;
}
}
}
_forEachCell(x1, y1, x2, y2, fn, arg1, arg2, intersectionTest) {
const cx1 = this._convertToCellCoord(x1);
const cy1 = this._convertToCellCoord(y1);
const cx2 = this._convertToCellCoord(x2);
const cy2 = this._convertToCellCoord(y2);
for (let x = cx1; x <= cx2; x++) for (let y = cy1; y <= cy2; y++) {
const cellIndex = this.d * y + x;
if (intersectionTest && !intersectionTest(this._convertFromCellCoord(x), this._convertFromCellCoord(y), this._convertFromCellCoord(x + 1), this._convertFromCellCoord(y + 1))) continue;
if (fn.call(this, x1, y1, x2, y2, cellIndex, arg1, arg2, intersectionTest)) return;
}
}
_convertFromCellCoord(x) {
return (x - this.padding) / this.scale;
}
_convertToCellCoord(x) {
return Math.max(0, Math.min(this.d - 1, Math.floor(x * this.scale) + this.padding));
}
toArrayBuffer() {
if (this.arrayBuffer) return this.arrayBuffer;
const cells = this.cells;
const metadataLength = NUM_PARAMS + this.cells.length + 1 + 1;
let totalCellLength = 0;
for (const cell of this.cells) totalCellLength += cell.length;
const array = new Int32Array(metadataLength + totalCellLength + this.keys.length + this.bboxes.length);
array[0] = this.extent;
array[1] = this.n;
array[2] = this.padding;
let offset = metadataLength;
for (let k = 0; k < cells.length; k++) {
const cell = cells[k];
array[NUM_PARAMS + k] = offset;
array.set(cell, offset);
offset += cell.length;
}
array[NUM_PARAMS + cells.length] = offset;
array.set(this.keys, offset);
offset += this.keys.length;
array[NUM_PARAMS + cells.length + 1] = offset;
array.set(this.bboxes, offset);
offset += this.bboxes.length;
return array.buffer;
}
static serialize(grid, transferables) {
const buffer = grid.toArrayBuffer();
if (transferables) transferables.push(buffer);
return { buffer };
}
static deserialize(serialized) {
return new TransferableGridIndex(serialized.buffer);
}
};
//#endregion
//#region src/util/web_worker_transfer.ts
const registry = {};
/**
* Register the given class as serializable.
*
* @param options - the registration options
*/
function register(name, klass, options = {}) {
if (registry[name]) throw new Error(`${name} is already registered.`);
Object.defineProperty(klass, "_classRegistryKey", {
value: name,
writeable: false
});
registry[name] = {
klass,
omit: options.omit || [],
shallow: options.shallow || []
};
}
register("Object", Object);
register("Set", Set);
register("TransferableGridIndex", TransferableGridIndex);
register("Color", Color);
register("Error", Error);
register("AJAXError", AJAXError);
register("ResolvedImage", ResolvedImage);
register("StylePropertyFunction", StylePropertyFunction);
register("StyleExpression", StyleExpression, { omit: ["_evaluator"] });
register("ZoomDependentExpression", ZoomDependentExpression);
register("ZoomConstantExpression", ZoomConstantExpression);
register("CompoundExpression", CompoundExpression, { omit: ["_evaluate"] });
for (const name in expressions) {
if (expressions[name]._classRegistryKey) continue;
register(`Expression_${name}`, expressions[name]);
}
function isArrayBuffer(value) {
return value && typeof ArrayBuffer !== "undefined" && (value instanceof ArrayBuffer || value.constructor?.name === "ArrayBuffer");
}
function getClassRegistryKey(input) {
const klass = input.constructor;
return input.$name || klass._classRegistryKey;
}
function isRegistered(input) {
if (input === null || typeof input !== "object") return false;
const classRegistryKey = getClassRegistryKey(input);
return classRegistryKey && classRegistryKey !== "Object";
}
function isSerializeHandledByBuiltin(input) {
return !isRegistered(input) && (input === null || input === void 0 || typeof input === "boolean" || typeof input === "number" || typeof input === "string" || input instanceof Boolean || input instanceof Number || input instanceof String || input instanceof Date || input instanceof RegExp || input instanceof Blob || input instanceof Error || isArrayBuffer(input) || isImageBitmap(input) || ArrayBuffer.isView(input) || input instanceof ImageData);
}
/**
* Serialize the given object for transfer to or from a web worker.
*
* For non-builtin types, recursively serialize each property (possibly
* omitting certain properties - see register()), and package the result along
* with the constructor's `name` so that the appropriate constructor can be
* looked up in `deserialize()`.
*
* If a `transferables` array is provided, add any transferable objects (i.e.,
* any ArrayBuffers or ArrayBuffer views) to the list. (If a copy is needed,
* this should happen in the client code, before using serialize().)
*/
function serialize(input, transferables) {
if (isSerializeHandledByBuiltin(input)) {
if (isArrayBuffer(input) || isImageBitmap(input)) {
if (transferables) transferables.push(input);
}
if (ArrayBuffer.isView(input)) {
if (transferables) transferables.push(input.buffer);
}
if (input instanceof ImageData) {
if (transferables) transferables.push(input.data.buffer);
}
return input;
}
if (Array.isArray(input)) {
const serialized = [];
for (const item of input) serialized.push(serialize(item, transferables));
return serialized;
}
if (typeof input !== "object") throw new Error(`can't serialize object of type ${typeof input}`);
const classRegistryKey = getClassRegistryKey(input);
if (!classRegistryKey) throw new Error(`can't serialize object of unregistered class ${input.constructor.name}`);
if (!registry[classRegistryKey]) throw new Error(`${classRegistryKey} is not registered.`);
const { klass } = registry[classRegistryKey];
const properties = klass.serialize ? klass.serialize(input, transferables) : {};
if (!klass.serialize) {
for (const key in input) {
if (!input.hasOwnProperty(key)) continue;
if (registry[classRegistryKey].omit.includes(key)) continue;
const property = input[key];
if (property === void 0) continue;
properties[key] = registry[classRegistryKey].shallow.includes(key) ? property : serialize(property, transferables);
}
if (input instanceof Error) properties.message = input.message;
} else if (properties === transferables?.[transferables.length - 1]) throw new Error("statically serialized object won't survive transfer of $name property");
if (properties.$name) throw new Error("$name property is reserved for worker serialization logic.");
if (classRegistryKey !== "Object") properties.$name = classRegistryKey;
return properties;
}
function deserialize(input) {
if (isSerializeHandledByBuiltin(input)) return input;
if (Array.isArray(input)) return input.map(deserialize);
if (typeof input !== "object") throw new Error(`can't deserialize object of type ${typeof input}`);
const classRegistryKey = getClassRegistryKey(input) || "Object";
if (!registry[classRegistryKey]) throw new Error(`can't deserialize unregistered class ${classRegistryKey}`);
const { klass } = registry[classRegistryKey];
if (!klass) throw new Error(`can't deserialize unregistered class ${classRegistryKey}`);
if (klass.deserialize) return klass.deserialize(input);
const result = Object.create(klass.prototype);
for (const key of Object.keys(input)) {
if (key === "$name") continue;
const value = input[key];
result[key] = registry[classRegistryKey].shallow.includes(key) ? value : deserialize(value);
}
return result;
}
//#endregion
//#region src/style/zoom_history.ts
var ZoomHistory = class {
constructor() {
this.first = true;
}
update(z, now) {
const floorZ = Math.floor(z);
if (this.first) {
this.first = false;
this.lastIntegerZoom = floorZ;
this.lastIntegerZoomTime = 0;
this.lastZoom = z;
this.lastFloorZoom = floorZ;
return true;
}
if (this.lastFloorZoom > floorZ) {
this.lastIntegerZoom = floorZ + 1;
this.lastIntegerZoomTime = now;
} else if (this.lastFloorZoom < floorZ) {
this.lastIntegerZoom = floorZ;
this.lastIntegerZoomTime = now;
}
if (z !== this.lastZoom) {
this.lastZoom = z;
this.lastFloorZoom = floorZ;
return true;
}
return false;
}
};
//#endregion
//#region src/util/unicode_properties.g.ts
/**
* Returns whether the fallback fonts specified by the
* `localIdeographFontFamily` map option apply to the given codepoint.
*/
function codePointUsesLocalIdeographFontFamily(codePoint) {
return /[\u02EA\u02EB\u1100-\u11FF\u2E80-\u2FDF\u3000-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFE10-\uFE1F\uFE30-\uFE4F\uFF00-\uFFEF]|\uD81B[\uDFE0-\uDFFF]|[\uD81C-\uD822\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFFF]|\uD82C[\uDC00-\uDEFB]|\uD83C[\uDE00-\uDEFF]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79]/gim.test(String.fromCodePoint(codePoint));
}
/**
* Returns whether the given codepoint participates in ideographic line
* breaking.
*/
function codePointAllowsIdeographicBreaking(codePoint) {
return /[\u02EA\u02EB\u2E80-\u2FDF\u2FF0-\u303F\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FD-\u30FF\u3105-\u312F\u31A0-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uF900-\uFA6D\uFA70-\uFAD9\uFE10-\uFE1F\uFE30-\uFE4F\uFF00-\uFFEF]|\uD81B[\uDFE0-\uDFFF]|[\uD81C-\uD822\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFFF]|\uD82C[\uDC00-\uDEFB]|\uD83C[\uDE00-\uDEFF]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79]/gim.test(String.fromCodePoint(codePoint));
}
/**
* Returns true if the given Unicode codepoint identifies a character with
* upright orientation.
*
* A character has upright orientation if it is drawn upright (unrotated)
* whether the line is oriented horizontally or vertically, even if both
* adjacent characters can be rotated. For example, a Chinese character is
* always drawn upright. An uprightly oriented character causes an adjacent
* “neutral” character to be drawn upright as well.
*/
function codePointHasUprightVerticalOrientation(codePoint) {
return /[\u02EA\u02EB\u1100-\u11FF\u1400-\u167F\u18B0-\u18F5\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u3007\u3012\u3013\u3020-\u302F\u3031-\u303F\u3041-\u3096\u309D-\u30FB\u30FD-\u30FF\u3105-\u312F\u3131-\u318E\u3190-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFE10-\uFE1F\uFE30-\uFE48\uFE50-\uFE57\uFE5F-\uFE62\uFE67-\uFE6F\uFF00-\uFF07\uFF0A-\uFF0C\uFF0E-\uFF19\uFF1F-\uFF3A\uFF3C\uFF3E\uFF40-\uFF5A\uFFE0-\uFFE2\uFFE4-\uFFE7]|\uD802[\uDD80-\uDD9F]|\uD805[\uDD80-\uDDFF]|\uD806[\uDE00-\uDEBF]|\uD811[\uDC00-\uDE7F]|\uD81B[\uDFE0-\uDFE4\uDFF0-\uDFF6]|[\uD81C-\uD822\uD83D\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD30-\uDEFB]|\uD833[\uDEC0-\uDFCF]|\uD834[\uDC00-\uDDFF\uDEE0-\uDF7F]|\uD836[\uDC00-\uDEAF]|\uD83C[\uDC00-\uDE00\uDF00-\uDFFF]|\uD83E[\uDD00-\uDEFF]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79]/gim.test(String.fromCodePoint(codePoint));
}
/**
* Returns true if the given Unicode codepoint identifies a character with
* neutral orientation.
*
* A character has neutral orientation if it may be drawn rotated or unrotated
* when the line is oriented vertically, depending on the orientation of the
* adjacent characters. For example, along a vertically oriented line, the
* vulgar fraction ½ is drawn upright among Chinese characters but rotated among
* Latin letters. A neutrally oriented character does not influence whether an
* adjacent character is drawn upright or rotated.
*/
function codePointHasNeutralVerticalOrientation(codePoint) {
return /[\xA7\xA9\xAE\xB1\xBC-\xBE\xD7\xF7\u2016\u2020\u2021\u2030\u2031\u203B\u203C\u2042\u2047-\u2049\u2051\u2100-\u218F\u221E\u2234\u2235\u2300-\u2307\u230C-\u231F\u2324-\u2328\u232B\u237D-\u239A\u23BE-\u23CD\u23CF\u23D1-\u23DB\u23E2-\u2422\u2424-\u24FF\u25A0-\u2619\u2620-\u2767\u2776-\u2793\u2B12-\u2B2F\u2B50-\u2B59\u2BB8-\u2BEB\u3000-\u303F\u30A0-\u30FF\uE000-\uF8FF\uFE30-\uFE6F\uFF00-\uFFEF\uFFFC\uFFFD]|[\uDB80-\uDBFF][\uDC00-\uDFFF]/gim.test(String.fromCodePoint(codePoint));
}
/**
* Returns whether the give codepoint is likely to require complex text shaping.
*/
function codePointRequiresComplexTextShaping(codePoint) {
return /[\u0900-\u0DFF\u0F00-\u109F\u1780-\u17FF]/gim.test(String.fromCodePoint(codePoint));
}
//#endregion
//#region src/util/script_detection.ts
function charIsWhitespace(char) {
return /\s/u.test(String.fromCodePoint(char));
}
function allowsVerticalWritingMode(chars) {
for (const char of chars) if (codePointHasUprightVerticalOrientation(char.codePointAt(0))) return true;
return false;
}
function allowsLetterSpacing(chars) {
for (const char of chars) if (!charAllowsLetterSpacing(char.codePointAt(0))) return false;
return true;
}
/**
* Returns a regular expression matching the given script codes, excluding any
* code that the execution environment lacks support for in regular expressions.
*/
function sanitizedRegExpFromScriptCodes(scriptCodes) {
const supportedPropertyEscapes = scriptCodes.map((code) => {
try {
return new RegExp(`\\p{sc=${code}}`, "u").source;
} catch {
return null;
}
}).filter((pe) => pe);
return new RegExp(supportedPropertyEscapes.join("|"), "u");
}
const cursiveScriptRegExp = sanitizedRegExpFromScriptCodes([
"Arab",
"Dupl",
"Mong",
"Ougr",
"Syrc"
]);
function charAllowsLetterSpacing(char) {
return !cursiveScriptRegExp.test(String.fromCodePoint(char));
}
/**
* Returns true if the given Unicode codepoint identifies a character with
* rotated orientation.
*
* A character has rotated orientation if it is drawn rotated when the line is
* oriented vertically, even if both adjacent characters are upright. For
* example, a Latin letter is drawn rotated along a vertical line. A rotated
* character causes an adjacent “neutral” character to be drawn rotated as well.
*/
function charHasRotatedVerticalOrientation(char) {
return !(codePointHasUprightVerticalOrientation(char) || codePointHasNeutralVerticalOrientation(char));
}
function charInComplexShapingScript(char) {
return /\p{sc=Arab}/u.test(String.fromCodePoint(char));
}
const rtlScriptRegExp = sanitizedRegExpFromScriptCodes([
"Adlm",
"Arab",
"Armi",
"Avst",
"Chrs",
"Cprt",
"Egyp",
"Elym",
"Gara",
"Hatr",
"Hebr",
"Hung",
"Khar",
"Lydi",
"Mand",
"Mani",
"Mend",
"Merc",
"Mero",
"Narb",
"Nbat",
"Nkoo",
"Orkh",
"Palm",
"Phli",
"Phlp",
"Phnx",
"Prti",
"Rohg",
"Samr",
"Sarb",
"Sogo",
"Syrc",
"Thaa",
"Todr",
"Yezi"
]);
function charInRTLScript(char) {
return rtlScriptRegExp.test(String.fromCodePoint(char));
}
function charInSupportedScript(char, canRenderRTL) {
if (!canRenderRTL && charInRTLScript(char)) return false;
return !codePointRequiresComplexTextShaping(char);
}
function stringContainsRTLText(chars) {
for (const char of chars) if (charInRTLScript(char.codePointAt(0))) return true;
return false;
}
function isStringInSupportedScript(chars, canRenderRTL) {
for (const char of chars) if (!charInSupportedScript(char.codePointAt(0), canRenderRTL)) return false;
return true;
}
//#endregion
//#region src/source/rtl_text_plugin_worker.ts
var RTLWorkerPlugin = class {
constructor() {
this.TIMEOUT = 5e3;
this.applyArabicShaping = null;
this.processBidirectionalText = null;
this.processStyledBidirectionalText = null;
this.pluginStatus = "unavailable";
this.pluginURL = null;
this.loadScriptResolve = () => {};
}
setState(state) {
this.pluginStatus = state.pluginStatus;
this.pluginURL = state.pluginURL;
}
getState() {
return {
pluginStatus: this.pluginStatus,
pluginURL: this.pluginURL
};
}
setMethods(rtlTextPlugin) {
if (rtlWorkerPlugin.isParsed()) throw new Error("RTL text plugin already registered.");
this.applyArabicShaping = rtlTextPlugin.applyArabicShaping;
this.processBidirectionalText = rtlTextPlugin.processBidirectionalText;
this.processStyledBidirectionalText = rtlTextPlugin.processStyledBidirectionalText;
this.loadScriptResolve();
}
isParsed() {
return this.applyArabicShaping != null && this.processBidirectionalText != null && this.processStyledBidirectionalText != null;
}
getRTLTextPluginStatus() {
return this.pluginStatus;
}
async syncState(incomingState, loadScript) {
if (this.isParsed()) return this.getState();
if (incomingState.pluginStatus !== "loading") {
this.setState(incomingState);
return incomingState;
}
const urlToLoad = incomingState.pluginURL;
const loadScriptPromise = new Promise((resolve) => {
this.loadScriptResolve = resolve;
});
const dontWaitForeverTimeoutPromise = new Promise((resolve) => setTimeout(() => resolve(), this.TIMEOUT));
await loadScript(urlToLoad);
await Promise.race([loadScriptPromise, dontWaitForeverTimeoutPromise]);
if (this.isParsed()) {
const loadedState = {
pluginStatus: "loaded",
pluginURL: urlToLoad
};
this.setState(loadedState);
return loadedState;
}
this.setState({
pluginStatus: "error",
pluginURL: ""
});
throw new Error(`RTL Text Plugin failed to import scripts from ${urlToLoad}`);
}
};
const rtlWorkerPlugin = new RTLWorkerPlugin();
//#endregion
//#region src/style/evaluation_parameters.ts
/**
* @internal
* A parameter that can be evaluated to a value.
* It's main purpose is a parameter to expression `evaluate` methods.
*/
var EvaluationParameters = class {
constructor(zoom, options) {
this.isSupportedScript = isSupportedScript;
this.zoom = zoom;
if (options) {
this.now = options.now || 0;
this.fadeDuration = options.fadeDuration || 0;
this.zoomHistory = options.zoomHistory || new ZoomHistory();
this.transition = options.transition || {};
} else {
this.now = 0;
this.fadeDuration = 0;
this.zoomHistory = new ZoomHistory();
this.transition = {};
}
}
crossFadingFactor() {
if (this.fadeDuration === 0) return 1;
else return Math.min((this.now - this.zoomHistory.lastIntegerZoomTime) / this.fadeDuration, 1);
}
getCrossfadeParameters() {
const z = this.zoom;
const fraction = z - Math.floor(z);
const t = this.crossFadingFactor();
return z > this.zoomHistory.lastIntegerZoom ? {
fromScale: 2,
toScale: 1,
t: fraction + (1 - fraction) * t
} : {
fromScale: .5,
toScale: 1,
t: 1 - (1 - t) * fraction
};
}
};
function isSupportedScript(str) {
return isStringInSupportedScript(str, rtlWorkerPlugin.getRTLTextPluginStatus() === "loaded");
}
//#endregion
//#region src/style/properties.ts
const TRANSITION_SUFFIX = "-transition";
/**
* @internal
* `PropertyValue` represents the value part of a property key-value unit. It's used to represent both
* paint and layout property values, and regardless of whether or not their property supports data-driven
* expressions.
*
* `PropertyValue` stores the raw input value as seen in a style or a runtime styling API call, i.e. one of the
* following:
*
* * A constant value of the type appropriate for the property
* * A function which produces a value of that type (but functions are quasi-deprecated in favor of expressions)
* * An expression which produces a value of that type
* * "undefined"/"not present", in which case the property is assumed to take on its default value.
*
* In addition to storing the original input value, `PropertyValue` also stores a normalized representation,
* effectively treating functions as if they are expressions, and constant or default values as if they are
* (constant) expressions.
*/
var PropertyValue = class {
constructor(property, value, rootKey, globalState) {
this.property = property;
this.value = value;
this.expression = normalizePropertyExpression(value === void 0 ? property.specification.default : value, rootKey, property.specification, globalState);
}
isDataDriven() {
return this.expression.kind === "source" || this.expression.kind === "composite";
}
getGlobalStateRefs() {
return this.expression.globalStateRefs || /* @__PURE__ */ new Set();
}
possiblyEvaluate(parameters, canonical, availableImages) {
return this.property.possiblyEvaluate(this, parameters, canonical, availableImages);
}
};
/**
* @internal
* Paint properties are _transitionable_: they can change in a fluid manner, interpolating or cross-fading between
* old and new value. The duration of the transition, and the delay before it begins, is configurable.
*
* `TransitionablePropertyValue` is a compositional class that stores both the property value and that transition
* configuration.
*
* A `TransitionablePropertyValue` can calculate the next step in the evaluation chain for paint property values:
* `TransitioningPropertyValue`.
*/
var TransitionablePropertyValue = class {
constructor(property, rootKey, globalState) {
this.property = property;
this.value = new PropertyValue(property, void 0, rootKey, globalState);
}
transitioned(parameters, prior) {
return new TransitioningPropertyValue(this.property, this.value, prior, extend({}, parameters.transition, this.transition), parameters.now);
}
untransitioned() {
return new TransitioningPropertyValue(this.property, this.value, null, {}, 0);
}
};
/**
* @internal
* `Transitionable` stores a map of all (property name, `TransitionablePropertyValue`) pairs for paint properties of a
* given layer type. It can calculate the `TransitioningPropertyValue`s for all of them at once, producing a
* `Transitioning` instance for the same set of properties.
*/
var Transitionable = class {
constructor(properties, rootKey, globalState) {
this._properties = properties;
this._values = Object.create(properties.defaultTransitionablePropertyValues);
this._globalState = globalState;
this._rootKey = rootKey;
}
/** rootKey of a property, e.g. `layers[3].paint.line-color`. */
_propertyRootKey(name) {
return `${this._rootKey}.${String(name)}`;
}
hasProperty(name) {
return name in this._properties.defaultTransitionablePropertyValues;
}
getValue(name) {
return clone(this._values[name].value.value);
}
setValue(name, value) {
if (!Object.hasOwn(this._values, name)) this._values[name] = new TransitionablePropertyValue(this._values[name].property, this._propertyRootKey(name), this._globalState);
this._values[name].value = new PropertyValue(this._values[name].property, value === null ? void 0 : clone(value), this._propertyRootKey(name), this._globalState);
}
getTransition(name) {
return clone(this._values[name].transition);
}
setTransition(name, value) {
if (!Object.hasOwn(this._values, name)) this._values[name] = new TransitionablePropertyValue(this._values[name].property, this._propertyRootKey(name), this._globalState);
this._values[name].transition = clone(value) || void 0;
}
serialize() {
const result = {};
for (const property of Object.keys(this._values)) {
const value = this.getValue(property);
if (value !== void 0) result[property] = value;
const transition = this.getTransition(property);
if (transition !== void 0) result[`${property}${TRANSITION_SUFFIX}`] = transition;
}
return result;
}
transitioned(parameters, prior) {
const result = new Transitioning(this._properties);
for (const property of Object.keys(this._values)) result._values[property] = this._values[property].transitioned(parameters, prior._values[property]);
return result;
}
untransitioned() {
const result = new Transitioning(this._properties);
for (const property of Object.keys(this._values)) result._values[property] = this._values[property].untransitioned();
return result;
}
};
/**
* @internal
* `TransitioningPropertyValue` implements the first of two intermediate steps in the evaluation chain of a paint
* property value. In this step, transitions between old and new values are handled: as long as the transition is in
* progress, `TransitioningPropertyValue` maintains a reference to the prior value, and interpolates between it and
* the new value based on the current time and the configured transition duration and delay. The product is the next
* step in the evaluation chain: the "possibly evaluated" result type `R`. See below for more on this concept.
*/
var TransitioningPropertyValue = class {
constructor(property, value, prior, transition, now) {
this.property = property;
this.value = value;
this.begin = now + transition.delay || 0;
this.end = this.begin + transition.duration || 0;
if (property.specification.transition && (transition.delay || transition.duration)) this.prior = prior;
}
possiblyEvaluate(parameters, canonical, availableImages) {
const now = parameters.now || 0;
const finalValue = this.value.possiblyEvaluate(parameters, canonical, availableImages);
const prior = this.prior;
if (!prior) return finalValue;
else if (now > this.end) {
this.prior = null;
return finalValue;
} else if (this.value.isDataDriven()) {
this.prior = null;
return finalValue;
} else if (now < this.begin) return prior.possiblyEvaluate(parameters, canonical, availableImages);
else {
const t = (now - this.begin) / (this.end - this.begin);
return this.property.interpolate(prior.possiblyEvaluate(parameters, canonical, availableImages), finalValue, easeCubicInOut(t));
}
}
};
/**
* @internal
* `Transitioning` stores a map of all (property name, `TransitioningPropertyValue`) pairs for paint properties of a
* given layer type. It can calculate the possibly-evaluated values for all of them at once, producing a
* `PossiblyEvaluated` instance for the same set of properties.
*/
var Transitioning = class {
constructor(properties) {
this._properties = properties;
this._values = Object.create(properties.defaultTransitioningPropertyValues);
}
possiblyEvaluate(parameters, canonical, availableImages) {
const result = new PossiblyEvaluated(this._properties);
for (const property of Object.keys(this._values)) result._values[property] = this._values[property].possiblyEvaluate(parameters, canonical, availableImages);
return result;
}
hasTransition() {
for (const property of Object.keys(this._values)) if (this._values[property].prior) return true;
return false;
}
};
/**
* Because layout properties are not transitionable, they have a simpler representation and evaluation chain than
* paint properties: `PropertyValue`s are possibly evaluated, producing possibly evaluated values, which are then
* fully evaluated.
*
* `Layout` stores a map of all (property name, `PropertyValue`) pairs for layout properties of a
* given layer type. It can calculate the possibly-evaluated values for all of them at once, producing a
* `PossiblyEvaluated` instance for the same set of properties.
*/
var Layout = class {
constructor(properties, rootKey, globalState) {
this._properties = properties;
this._values = Object.create(properties.defaultPropertyValues);
this._globalState = globalState;
this._rootKey = rootKey;
}
/** rootKey of a property, e.g. `layers[3].layout.line-cap`. */
_propertyRootKey(name) {
return `${this._rootKey}.${String(name)}`;
}
hasValue(name) {
return this._values[name].value !== void 0;
}
hasProperty(name) {
return name in this._properties.defaultPropertyValues;
}
getValue(name) {
return clone(this._values[name].value);
}
setValue(name, value) {
this._values[name] = new PropertyValue(this._values[name].property, value === null ? void 0 : clone(value), this._propertyRootKey(name), this._globalState);
}
serialize() {
const result = {};
for (const property of Object.keys(this._values)) {
const value = this.getValue(property);
if (value !== void 0) result[property] = value;
}
return result;
}
possiblyEvaluate(parameters, canonical, availableImages) {
const result = new PossiblyEvaluated(this._properties);
for (const property of Object.keys(this._values)) result._values[property] = this._values[property].possiblyEvaluate(parameters, canonical, availableImages);
return result;
}
};
/**
* @internal
* `PossiblyEvaluatedPropertyValue` is used for data-driven paint and layout property values. It holds a
* `PossiblyEvaluatedValue` and the `GlobalProperties` that were used to generate it. You're not allowed to supply
* a different set of `GlobalProperties` when performing the final evaluation because they would be ignored in the
* case where the input value was a constant or camera function.
*/
var PossiblyEvaluatedPropertyValue = class {
constructor(property, value, parameters) {
this.property = property;
this.value = value;
this.parameters = parameters;
}
isConstant() {
return this.value.kind === "constant";
}
constantOr(value) {
if (this.value.kind === "constant") return this.value.value;
else return value;
}
evaluate(feature, featureState, canonical, availableImages) {
return this.property.evaluate(this.value, this.parameters, feature, featureState, canonical, availableImages);
}
};
/**
* @internal
* `PossiblyEvaluated` stores a map of all (property name, `R`) pairs for paint or layout properties of a
* given layer type.
*/
var PossiblyEvaluated = class {
constructor(properties) {
this._properties = properties;
this._values = Object.create(properties.defaultPossiblyEvaluatedValues);
}
get(name) {
return this._values[name];
}
};
/**
* Returns the length of the array value, or undefined if the value is not an array or a style spec array wrapper.
*/
function getArrayValueLength(value) {
if (Array.isArray(value)) return value.length;
const values = value?.values;
return Array.isArray(values) ? values.length : void 0;
}
/**
* Returns true if the two values are arrays of different length, either bare arrays or style spec array wrappers.
*/
function isNonInterpolableArrayChange(a, b) {
const lengthA = getArrayValueLength(a);
const lengthB = getArrayValueLength(b);
return lengthA !== void 0 && lengthB !== void 0 && lengthA !== lengthB;
}
/**
* @internal
* An implementation of `Property` for properties that do not permit data-driven (source or composite) expressions.
* This restriction allows us to declare statically that the result of possibly evaluating this kind of property
* is in fact always the scalar type `T`, and can be used without further evaluating the value on a per-feature basis.
*/
var DataConstantProperty = class {
constructor(specification, name) {
this.specification = specification;
this.name = name;
}
possiblyEvaluate(value, parameters) {
if (value.isDataDriven()) throw new Error("Value should not be data driven");
return value.expression.evaluate(parameters);
}
interpolate(a, b, t) {
if (isNonInterpolableArrayChange(a, b)) {
warnOnce(`Property "${this.name}" is trying to interpolate arrays of different lengths. Rendering may 'jump'.`);
return b;
}
const interpolationType = this.specification.type;
const interpolationFn = interpolateFactory[interpolationType];
if (interpolationFn) return interpolationFn(a, b, t);
else return a;
}
};
/**
* @internal
* An implementation of `Property` for properties that permit data-driven (source or composite) expressions.
* The result of possibly evaluating this kind of property is `PossiblyEvaluatedPropertyValue<T>`; obtaining
* a scalar value `T` requires further evaluation on a per-feature basis.
*/
var DataDrivenProperty = class {
constructor(specification, name, overrides) {
this.specification = specification;
this.name = name;
this.overrides = overrides;
}
possiblyEvaluate(value, parameters, canonical, availableImages) {
if (value.expression.kind === "constant" || value.expression.kind === "camera") return new PossiblyEvaluatedPropertyValue(this, {
kind: "constant",
value: value.expression.evaluate(parameters, null, {}, canonical, availableImages)
}, parameters);
else return new PossiblyEvaluatedPropertyValue(this, value.expression, parameters);
}
interpolate(a, b, t) {
if (a.value.kind !== "constant" || b.value.kind !== "constant") return a;
if (a.value.value === void 0 || b.value.value === void 0) return new PossiblyEvaluatedPropertyValue(this, {
kind: "constant",
value: void 0
}, a.parameters);
if (isNonInterpolableArrayChange(a.value.value, b.value.value)) {
warnOnce(`Property "${this.name}" is trying to interpolate arrays of different lengths. Rendering may 'jump'.`);
return b;
}
const interpolationType = this.specification.type;
const interpolationFn = interpolateFactory[interpolationType];
if (interpolationFn) {
const interpolatedValue = interpolationFn(a.value.value, b.value.value, t);
return new PossiblyEvaluatedPropertyValue(this, {
kind: "constant",
value: interpolatedValue
}, a.parameters);
} else return a;
}
evaluate(value, parameters, feature, featureState, canonical, availableImages) {
if (value.kind === "constant") return value.value;
else return value.evaluate(parameters, feature, featureState, canonical, availableImages);
}
};
/**
* @internal
* An implementation of `Property` for data driven `line-pattern` which are transitioned by cross-fading
* rather than interpolation.
*/
var CrossFadedDataDrivenProperty = class extends DataDrivenProperty {
possiblyEvaluate(value, parameters, canonical, availableImages) {
if (value.value === void 0) return new PossiblyEvaluatedPropertyValue(this, {
kind: "constant",
value: void 0
}, parameters);
else if (value.expression.kind === "constant") {
const evaluatedValue = value.expression.evaluate(parameters, null, {}, canonical, availableImages);
const constantValue = value.property.specification.type === "resolvedImage" && typeof evaluatedValue !== "string" ? evaluatedValue.name : evaluatedValue;
const constant = this._calculate(constantValue, constantValue, constantValue, parameters);
return new PossiblyEvaluatedPropertyValue(this, {
kind: "constant",
value: constant
}, parameters);
} else if (value.expression.kind === "camera") {
const cameraVal = this._calculate(value.expression.evaluate({ zoom: parameters.zoom - 1 }), value.expression.evaluate({ zoom: parameters.zoom }), value.expression.evaluate({ zoom: parameters.zoom + 1 }), parameters);
return new PossiblyEvaluatedPropertyValue(this, {
kind: "constant",
value: cameraVal
}, parameters);
} else return new PossiblyEvaluatedPropertyValue(this, value.expression, parameters);
}
evaluate(value, globals, feature, featureState, canonical, availableImages) {
if (value.kind === "source") {
const constant = value.evaluate(globals, feature, featureState, canonical, availableImages);
return this._calculate(constant, constant, constant, globals);
} else if (value.kind === "composite") return this._calculate(value.evaluate({ zoom: Math.floor(globals.zoom) - 1 }, feature, featureState), value.evaluate({ zoom: Math.floor(globals.zoom) }, feature, featureState), value.evaluate({ zoom: Math.floor(globals.zoom) + 1 }, feature, featureState), globals);
else return value.value;
}
_calculate(min, mid, max, parameters) {
return parameters.zoom > parameters.zoomHistory.lastIntegerZoom ? {
from: min,
to: mid
} : {
from: max,
to: mid
};
}
interpolate(a) {
return a;
}
};
/**
* @internal
* An implementation of `Property` for `*-pattern` and `line-dasharray`, which are transitioned by cross-fading
* rather than interpolation.
*/
var CrossFadedProperty = class {
constructor(specification, name) {
this.specification = specification;
this.name = name;
}
possiblyEvaluate(value, parameters, canonical, availableImages) {
if (value.value === void 0) return;
else if (value.expression.kind === "constant") {
const constant = value.expression.evaluate(parameters, null, {}, canonical, availableImages);
return this._calculate(constant, constant, constant, parameters);
} else return this._calculate(value.expression.evaluate(new EvaluationParameters(Math.floor(parameters.zoom - 1), parameters)), value.expression.evaluate(new EvaluationParameters(Math.floor(parameters.zoom), parameters)), value.expression.evaluate(new EvaluationParameters(Math.floor(parameters.zoom + 1), parameters)), parameters);
}
_calculate(min, mid, max, parameters) {
return parameters.zoom > parameters.zoomHistory.lastIntegerZoom ? {
from: min,
to: mid
} : {
from: max,
to: mid
};
}
interpolate(a) {
return a;
}
};
/**
* @internal
* An implementation of `Property` for `heatmap-color` and `line-gradient`. Interpolation is a no-op, and
* evaluation returns a boolean value in order to indicate its presence, but the real
* evaluation happens in StyleLayer classes.
*/
var ColorRampProperty = class {
constructor(specification, name) {
this.specification = specification;
this.name = name;
}
possiblyEvaluate(value, parameters, canonical, availableImages) {
return !!value.expression.evaluate(parameters, null, {}, canonical, availableImages);
}
interpolate() {
return false;
}
};
/**
* @internal
* `Properties` holds objects containing default values for the layout or paint property set of a given
* layer type. These objects are immutable, and they are used as the prototypes for the `_values` members of
* `Transitionable`, `Transitioning`, `Layout`, and `PossiblyEvaluated`. This allows these classes to avoid
* doing work in the common case where a property has no explicit value set and should be considered to take
* on the default value: using `for (const property of Object.keys(this._values))`, they can iterate over
* only the _own_ properties of `_values`, skipping repeated calculation of transitions and possible/final
* evaluations for defaults, the result of which will always be the same.
*/
var Properties = class {
constructor(properties) {
this.properties = properties;
this.defaultPropertyValues = {};
this.defaultTransitionablePropertyValues = {};
this.defaultTransitioningPropertyValues = {};
this.defaultPossiblyEvaluatedValues = {};
this.overridableProperties = [];
for (const property in properties) {
const prop = properties[property];
if (prop.specification.overridable) this.overridableProperties.push(property);
const defaultPropertyValue = this.defaultPropertyValues[property] = new PropertyValue(prop, void 0, prop.name, void 0);
const defaultTransitionablePropertyValue = this.defaultTransitionablePropertyValues[property] = new TransitionablePropertyValue(prop, prop.name, void 0);
this.defaultTransitioningPropertyValues[property] = defaultTransitionablePropertyValue.untransitioned();
this.defaultPossiblyEvaluatedValues[property] = defaultPropertyValue.possiblyEvaluate({});
}
}
};
register("DataDrivenProperty", DataDrivenProperty);
register("DataConstantProperty", DataConstantProperty);
register("CrossFadedDataDrivenProperty", CrossFadedDataDrivenProperty);
register("CrossFadedProperty", CrossFadedProperty);
register("ColorRampProperty", ColorRampProperty);
//#endregion
//#region src/style/style_layer.ts
const ERROR_PAINT_NOT_LAYOUT = " is a PAINT property not a LAYOUT property. Use get/setPaintProperty instead?";
const ERROR_LAYOUT_NOT_PAINT = " is a LAYOUT property not a PAINT property. Use get/setLayoutProperty instead?";
/**
* A base class for style layers
*/
var StyleLayer = class extends Evented {
constructor(layer, properties, globalState) {
super();
this.id = layer.id;
this.type = layer.type;
this._globalState = globalState;
this._featureFilter = {
filter: () => true,
needGeometry: false,
getGlobalStateRefs: () => /* @__PURE__ */ new Set()
};
this._visibilityExpression = createVisibility(this.visibility, `layers[${this.id}].layout.visibility`, globalState);
if (layer.type === "custom") return;
this.metadata = layer.metadata;
this.minzoom = layer.minzoom;
this.maxzoom = layer.maxzoom;
if (layer.type !== "background") {
this.source = layer.source;
this.sourceLayer = layer["source-layer"];
this.filter = layer.filter;
this._featureFilter = featureFilter(layer.filter, `layers[${this.id}].filter`, globalState);
}
if (properties.layout) this._unevaluatedLayout = new Layout(properties.layout, `layers[${this.id}].layout`, globalState);
if (properties.paint) {
this._transitionablePaint = new Transitionable(properties.paint, `layers[${this.id}].paint`, globalState);
for (const property in layer.paint) this.setPaintProperty(property, layer.paint[property], { validate: false });
for (const property in layer.layout) this.setLayoutProperty(property, layer.layout[property], { validate: false });
this._transitioningPaint = this._transitionablePaint.untransitioned();
this.paint = new PossiblyEvaluated(properties.paint);
}
}
setFilter(filter) {
this.filter = filter;
this._featureFilter = featureFilter(filter, `layers[${this.id}].filter`, this._globalState);
}
getCrossfadeParameters() {
return this._crossfadeParameters;
}
getLayoutProperty(name) {
if (name === "visibility") return this.visibility;
if (this._transitionablePaint?.hasProperty(name)) throw new Error(name + ERROR_PAINT_NOT_LAYOUT);
if (!this._unevaluatedLayout) throw new Error(`Cannot get layout property "${name}" on layer type "${this.type}" which has no layout properties.`);
return this._unevaluatedLayout.getValue(name);
}
/**
* Get list of global state references that are used within layout or filter properties.
* This is used to determine if layer source need to be reloaded when global state property changes.
*
*/
getLayoutAffectingGlobalStateRefs() {
const globalStateRefs = /* @__PURE__ */ new Set();
for (const globalStateRef of this._visibilityExpression.getGlobalStateRefs()) globalStateRefs.add(globalStateRef);
if (this._unevaluatedLayout) for (const propertyName in this._unevaluatedLayout._values) {
const value = this._unevaluatedLayout._values[propertyName];
for (const globalStateRef of value.getGlobalStateRefs()) globalStateRefs.add(globalStateRef);
}
for (const globalStateRef of this._featureFilter.getGlobalStateRefs()) globalStateRefs.add(globalStateRef);
return globalStateRefs;
}
/**
* Get list of global state references that are used within paint properties.
* This is used to determine if layer needs to be repainted when global state property changes.
*
*/
getPaintAffectingGlobalStateRefs() {
const globalStateRefs = new globalThis.Map();
if (this._transitionablePaint) for (const propertyName in this._transitionablePaint._values) {
const value = this._transitionablePaint._values[propertyName].value;
for (const globalStateRef of value.getGlobalStateRefs()) {
const properties = globalStateRefs.get(globalStateRef) ?? [];
properties.push({
name: propertyName,
value: value.value
});
globalStateRefs.set(globalStateRef, properties);
}
}
return globalStateRefs;
}
/**
* Get list of global state references that are used within visibility expression.
* This is used to determine if layer visibility needs to be updated when global state property changes.
*/
getVisibilityAffectingGlobalStateRefs() {
return this._visibilityExpression.getGlobalStateRefs();
}
setLayoutProperty(name, value, options = {}) {
if (name === "visibility") {
this.visibility = value;
this._visibilityExpression.setValue(value);
this.recalculateVisibility();
return;
}
if (this._transitionablePaint?.hasProperty(name)) {
this.fire(new ErrorEvent(/* @__PURE__ */ new Error(name + ERROR_PAINT_NOT_LAYOUT)));
return;
}
if (value !== null && value !== void 0 && this._validate(validateStyle.layoutProperty, `layers.${this.id}.layout.${name}`, name, value, options)) return;
this._unevaluatedLayout.setValue(name, value);
}
getPaintProperty(name) {
if (name.endsWith("-transition")) {
const baseName = name.slice(0, -11);
if (baseName === "visibility" || this._unevaluatedLayout?.hasProperty(baseName)) throw new Error(name + ERROR_LAYOUT_NOT_PAINT);
return this._transitionablePaint.getTransition(baseName);
} else {
if (name === "visibility" || this._unevaluatedLayout?.hasProperty(name)) throw new Error(name + ERROR_LAYOUT_NOT_PAINT);
return this._transitionablePaint.getValue(name);
}
}
setPaintProperty(name, value, options = {}) {
if (name === "visibility" || this._unevaluatedLayout?.hasProperty(name)) {
this.fire(new ErrorEvent(/* @__PURE__ */ new Error(name + ERROR_LAYOUT_NOT_PAINT)));
return false;
}
if (value !== null && value !== void 0 && this._validate(validateStyle.paintProperty, `layers.${this.id}.paint.${name}`, name, value, options)) return false;
if (name.endsWith("-transition")) {
this._transitionablePaint.setTransition(name.slice(0, -11), value || void 0);
return false;
} else {
const transitionable = this._transitionablePaint._values[name];
const isCrossFadedProperty = transitionable.property.specification["property-type"] === "cross-faded-data-driven";
const wasDataDriven = transitionable.value.isDataDriven();
const oldValue = transitionable.value;
this._transitionablePaint.setValue(name, value);
this._handleSpecialPaintPropertyUpdate(name);
const newValue = this._transitionablePaint._values[name].value;
return newValue.isDataDriven() || wasDataDriven || isCrossFadedProperty || this._handleOverridablePaintPropertyUpdate(name, oldValue, newValue);
}
}
_handleSpecialPaintPropertyUpdate(_) {}
_handleOverridablePaintPropertyUpdate(name, oldValue, newValue) {
return false;
}
isHidden(zoom = this.minzoom, roundMinZoom = false) {
if (this.minzoom && zoom < (roundMinZoom ? Math.floor(this.minzoom) : this.minzoom)) return true;
if (this.maxzoom && zoom >= this.maxzoom) return true;
return this._evaluatedVisibility === "none";
}
updateTransitions(parameters) {
this._transitioningPaint = this._transitionablePaint.transitioned(parameters, this._transitioningPaint);
}
hasTransition() {
return this._transitioningPaint.hasTransition();
}
recalculateVisibility() {
this._evaluatedVisibility = this._visibilityExpression.evaluate();
}
recalculate(parameters, availableImages) {
if (parameters.getCrossfadeParameters) this._crossfadeParameters = parameters.getCrossfadeParameters();
if (this._unevaluatedLayout) this.layout = this._unevaluatedLayout.possiblyEvaluate(parameters, void 0, availableImages);
this.paint = this._transitioningPaint.possiblyEvaluate(parameters, void 0, availableImages);
}
serialize() {
const output = {
"id": this.id,
"type": this.type,
"source": this.source,
"source-layer": this.sourceLayer,
"metadata": this.metadata,
"minzoom": this.minzoom,
"maxzoom": this.maxzoom,
"filter": this.filter,
"layout": this._unevaluatedLayout?.serialize(),
"paint": this._transitionablePaint?.serialize()
};
if (this.visibility) {
output.layout ||= {};
output.layout.visibility = this.visibility;
}
return filterObject(output, (value, key) => {
return value !== void 0 && !(key === "layout" && !Object.keys(value).length) && !(key === "paint" && !Object.keys(value).length);
});
}
_validate(validate, key, name, value, options = {}) {
return validateAndEmit(this, validate, {
key,
layerType: this.type,
objectKey: name,
value
}, options);
}
is3D() {
return false;
}
isTileClipped() {
return false;
}
hasOffscreenPass() {
return false;
}
resize() {}
isStateDependent() {
for (const property in this.paint._values) {
const value = this.paint.get(property);
if (!(value instanceof PossiblyEvaluatedPropertyValue) || !supportsPropertyExpression(value.property.specification)) continue;
if ((value.value.kind === "source" || value.value.kind === "composite") && value.value.isStateDependent) return true;
}
return false;
}
};
//#endregion
//#region src/style/style_layer/raster_style_layer_properties.g.ts
let paint$9;
const getPaint$9 = () => paint$9 = paint$9 || new Properties({
"raster-opacity": new DataConstantProperty(latest["paint_raster"]["raster-opacity"], "raster-opacity"),
"raster-hue-rotate": new DataConstantProperty(latest["paint_raster"]["raster-hue-rotate"], "raster-hue-rotate"),
"raster-brightness-min": new DataConstantProperty(latest["paint_raster"]["raster-brightness-min"], "raster-brightness-min"),
"raster-brightness-max": new DataConstantProperty(latest["paint_raster"]["raster-brightness-max"], "raster-brightness-max"),
"raster-saturation": new DataConstantProperty(latest["paint_raster"]["raster-saturation"], "raster-saturation"),
"raster-contrast": new DataConstantProperty(latest["paint_raster"]["raster-contrast"], "raster-contrast"),
"resampling": new DataConstantProperty(latest["paint_raster"]["resampling"], "resampling"),
"raster-resampling": new DataConstantProperty(latest["paint_raster"]["raster-resampling"], "raster-resampling"),
"raster-fade-duration": new DataConstantProperty(latest["paint_raster"]["raster-fade-duration"], "raster-fade-duration")
});
var raster_style_layer_properties_g_default = { get paint() {
return getPaint$9();
} };
//#endregion
//#region src/style/style_layer/raster_style_layer.ts
const isRasterStyleLayer = (layer) => layer.type === "raster";
var RasterStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, raster_style_layer_properties_g_default, globalState);
}
};
//#endregion
//#region src/util/struct_array.ts
/**
* @internal
* A view type size
*/
const viewTypes = {
"Int8": Int8Array,
"Uint8": Uint8Array,
"Int16": Int16Array,
"Uint16": Uint16Array,
"Int32": Int32Array,
"Uint32": Uint32Array,
"Float32": Float32Array
};
/** @internal */
var Struct = class {
/**
* @param structArray - The StructArray the struct is stored in
* @param index - The index of the struct in the StructArray.
*/
constructor(structArray, index) {
this._structArray = structArray;
this._pos1 = index * this.size;
this._pos2 = this._pos1 / 2;
this._pos4 = this._pos1 / 4;
this._pos8 = this._pos1 / 8;
}
};
const DEFAULT_CAPACITY = 128;
const RESIZE_MULTIPLIER = 5;
/**
* @internal
* `StructArray` provides an abstraction over `ArrayBuffer` and `TypedArray`
* making it behave like an array of typed structs.
*
* Conceptually, a StructArray is comprised of elements, i.e., instances of its
* associated struct type. Each particular struct type, together with an
* alignment size, determines the memory layout of a StructArray whose elements
* are of that type. Thus, for each such layout that we need, we have
* a corresponding StructArrayLayout class, inheriting from StructArray and
* implementing `emplaceBack()` and `_refreshViews()`.
*
* In some cases, where we need to access particular elements of a StructArray,
* we implement a more specific subclass that inherits from one of the
* StructArrayLayouts and adds a `get(i): T` accessor that returns a structured
* object whose properties are proxies into the underlying memory space for the
* i-th element. This affords the convenience of working with (seemingly) plain
* Javascript objects without the overhead of serializing/deserializing them
* into ArrayBuffers for efficient web worker transfer.
*/
var StructArray = class {
constructor() {
this.isTransferred = false;
this.capacity = -1;
this.resize(0);
}
/**
* Serialize a StructArray instance. Serializes both the raw data and the
* metadata needed to reconstruct the StructArray base class during
* deserialization.
*/
static serialize(array, transferables) {
array._trim();
if (transferables) {
array.isTransferred = true;
transferables.push(array.arrayBuffer);
}
return {
length: array.length,
arrayBuffer: array.arrayBuffer
};
}
static deserialize(input) {
const structArray = Object.create(this.prototype);
structArray.arrayBuffer = input.arrayBuffer;
structArray.length = input.length;
structArray.capacity = input.arrayBuffer.byteLength / structArray.bytesPerElement;
structArray._refreshViews();
return structArray;
}
/**
* Resize the array to discard unused capacity.
*/
_trim() {
if (this.length !== this.capacity) {
this.capacity = this.length;
this.arrayBuffer = this.arrayBuffer.slice(0, this.length * this.bytesPerElement);
this._refreshViews();
}
}
/**
* Resets the length of the array to 0 without de-allocating capacity.
*/
clear() {
this.length = 0;
}
/**
* Resize the array.
* If `n` is greater than the current length then additional elements with undefined values are added.
* If `n` is less than the current length then the array will be reduced to the first `n` elements.
* @param n - The new size of the array.
*/
resize(n) {
this.reserve(n);
this.length = n;
}
/**
* Indicate a planned increase in size, so that any necessary allocation may
* be done once, ahead of time.
* @param n - The expected size of the array.
*/
reserve(n) {
if (n > this.capacity) {
this.capacity = Math.max(n, Math.floor(this.capacity * RESIZE_MULTIPLIER), DEFAULT_CAPACITY);
this.arrayBuffer = new ArrayBuffer(this.capacity * this.bytesPerElement);
const oldUint8Array = this.uint8;
this._refreshViews();
if (oldUint8Array) this.uint8.set(oldUint8Array);
}
}
/**
* Create TypedArray views for the current ArrayBuffer.
*/
_refreshViews() {
throw new Error("_refreshViews() must be implemented by each concrete StructArray layout");
}
/**
* Replace the buffer with an empty one so typed views release the original ArrayBuffer for GC.
*/
freeBufferAfterUpload() {
this.arrayBuffer = /* @__PURE__ */ new ArrayBuffer(0);
this._refreshViews();
}
};
/**
* Given a list of member fields, create a full StructArrayLayout, in
* particular calculating the correct byte offset for each field. This data
* is used at build time to generate StructArrayLayout_*.emplaceBack() and
* other accessors, and at runtime for binding vertex buffer attributes.
*/
function createLayout(members, alignment = 1) {
let offset = 0;
let maxSize = 0;
return {
members: members.map((member) => {
const typeSize = sizeOf(member.type);
const memberOffset = offset = align$1(offset, Math.max(alignment, typeSize));
const components = member.components || 1;
maxSize = Math.max(maxSize, typeSize);
offset += typeSize * components;
return {
name: member.name,
type: member.type,
components,
offset: memberOffset
};
}),
size: align$1(offset, Math.max(maxSize, alignment)),
alignment
};
}
function sizeOf(type) {
return viewTypes[type].BYTES_PER_ELEMENT;
}
function align$1(offset, size) {
return Math.ceil(offset / size) * size;
}
//#endregion
//#region src/data/array_types.g.ts
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[2]
*
*/
var StructArrayLayout2i4 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1);
}
emplace(i, v0, v1) {
const o2 = i * 2;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
return i;
}
};
StructArrayLayout2i4.prototype.bytesPerElement = 4;
register("StructArrayLayout2i4", StructArrayLayout2i4);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[3]
*
*/
var StructArrayLayout3i6 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2);
}
emplace(i, v0, v1, v2) {
const o2 = i * 3;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
return i;
}
};
StructArrayLayout3i6.prototype.bytesPerElement = 6;
register("StructArrayLayout3i6", StructArrayLayout3i6);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[4]
*
*/
var StructArrayLayout4i8 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3);
}
emplace(i, v0, v1, v2, v3) {
const o2 = i * 4;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
this.int16[o2 + 3] = v3;
return i;
}
};
StructArrayLayout4i8.prototype.bytesPerElement = 8;
register("StructArrayLayout4i8", StructArrayLayout4i8);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[2]
* [4] - Int16[4]
*
*/
var StructArrayLayout2i4i12 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5);
}
emplace(i, v0, v1, v2, v3, v4, v5) {
const o2 = i * 6;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
this.int16[o2 + 3] = v3;
this.int16[o2 + 4] = v4;
this.int16[o2 + 5] = v5;
return i;
}
};
StructArrayLayout2i4i12.prototype.bytesPerElement = 12;
register("StructArrayLayout2i4i12", StructArrayLayout2i4i12);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[2]
* [4] - Uint8[4]
*
*/
var StructArrayLayout2i4ub8 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5);
}
emplace(i, v0, v1, v2, v3, v4, v5) {
const o2 = i * 4;
const o1 = i * 8;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.uint8[o1 + 4] = v2;
this.uint8[o1 + 5] = v3;
this.uint8[o1 + 6] = v4;
this.uint8[o1 + 7] = v5;
return i;
}
};
StructArrayLayout2i4ub8.prototype.bytesPerElement = 8;
register("StructArrayLayout2i4ub8", StructArrayLayout2i4ub8);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Float32[2]
*
*/
var StructArrayLayout2f8 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
}
emplaceBack(v0, v1) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1);
}
emplace(i, v0, v1) {
const o4 = i * 2;
this.float32[o4 + 0] = v0;
this.float32[o4 + 1] = v1;
return i;
}
};
StructArrayLayout2f8.prototype.bytesPerElement = 8;
register("StructArrayLayout2f8", StructArrayLayout2f8);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint16[10]
*
*/
var StructArrayLayout10ui20 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
}
emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) {
const o2 = i * 10;
this.uint16[o2 + 0] = v0;
this.uint16[o2 + 1] = v1;
this.uint16[o2 + 2] = v2;
this.uint16[o2 + 3] = v3;
this.uint16[o2 + 4] = v4;
this.uint16[o2 + 5] = v5;
this.uint16[o2 + 6] = v6;
this.uint16[o2 + 7] = v7;
this.uint16[o2 + 8] = v8;
this.uint16[o2 + 9] = v9;
return i;
}
};
StructArrayLayout10ui20.prototype.bytesPerElement = 20;
register("StructArrayLayout10ui20", StructArrayLayout10ui20);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint16[8]
*
*/
var StructArrayLayout8ui16 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7);
}
emplace(i, v0, v1, v2, v3, v4, v5, v6, v7) {
const o2 = i * 8;
this.uint16[o2 + 0] = v0;
this.uint16[o2 + 1] = v1;
this.uint16[o2 + 2] = v2;
this.uint16[o2 + 3] = v3;
this.uint16[o2 + 4] = v4;
this.uint16[o2 + 5] = v5;
this.uint16[o2 + 6] = v6;
this.uint16[o2 + 7] = v7;
return i;
}
};
StructArrayLayout8ui16.prototype.bytesPerElement = 16;
register("StructArrayLayout8ui16", StructArrayLayout8ui16);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[4]
* [8] - Uint16[4]
* [16] - Int16[4]
*
*/
var StructArrayLayout4i4ui4i24 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
}
emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) {
const o2 = i * 12;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
this.int16[o2 + 3] = v3;
this.uint16[o2 + 4] = v4;
this.uint16[o2 + 5] = v5;
this.uint16[o2 + 6] = v6;
this.uint16[o2 + 7] = v7;
this.int16[o2 + 8] = v8;
this.int16[o2 + 9] = v9;
this.int16[o2 + 10] = v10;
this.int16[o2 + 11] = v11;
return i;
}
};
StructArrayLayout4i4ui4i24.prototype.bytesPerElement = 24;
register("StructArrayLayout4i4ui4i24", StructArrayLayout4i4ui4i24);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Float32[3]
*
*/
var StructArrayLayout3f12 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2);
}
emplace(i, v0, v1, v2) {
const o4 = i * 3;
this.float32[o4 + 0] = v0;
this.float32[o4 + 1] = v1;
this.float32[o4 + 2] = v2;
return i;
}
};
StructArrayLayout3f12.prototype.bytesPerElement = 12;
register("StructArrayLayout3f12", StructArrayLayout3f12);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint32[1]
*
*/
var StructArrayLayout1ul4 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint32 = new Uint32Array(this.arrayBuffer);
}
emplaceBack(v0) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0);
}
emplace(i, v0) {
const o4 = i * 1;
this.uint32[o4 + 0] = v0;
return i;
}
};
StructArrayLayout1ul4.prototype.bytesPerElement = 4;
register("StructArrayLayout1ul4", StructArrayLayout1ul4);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[6]
* [12] - Uint32[1]
* [16] - Uint16[2]
*
*/
var StructArrayLayout6i1ul2ui20 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
this.uint32 = new Uint32Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7, v8) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8);
}
emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8) {
const o2 = i * 10;
const o4 = i * 5;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
this.int16[o2 + 3] = v3;
this.int16[o2 + 4] = v4;
this.int16[o2 + 5] = v5;
this.uint32[o4 + 3] = v6;
this.uint16[o2 + 8] = v7;
this.uint16[o2 + 9] = v8;
return i;
}
};
StructArrayLayout6i1ul2ui20.prototype.bytesPerElement = 20;
register("StructArrayLayout6i1ul2ui20", StructArrayLayout6i1ul2ui20);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[2]
* [4] - Int16[2]
* [8] - Int16[2]
*
*/
var StructArrayLayout2i2i2i12 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5);
}
emplace(i, v0, v1, v2, v3, v4, v5) {
const o2 = i * 6;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
this.int16[o2 + 3] = v3;
this.int16[o2 + 4] = v4;
this.int16[o2 + 5] = v5;
return i;
}
};
StructArrayLayout2i2i2i12.prototype.bytesPerElement = 12;
register("StructArrayLayout2i2i2i12", StructArrayLayout2i2i2i12);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Float32[2]
* [8] - Float32[1]
* [12] - Int16[2]
*
*/
var StructArrayLayout2f1f2i16 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4);
}
emplace(i, v0, v1, v2, v3, v4) {
const o4 = i * 4;
const o2 = i * 8;
this.float32[o4 + 0] = v0;
this.float32[o4 + 1] = v1;
this.float32[o4 + 2] = v2;
this.int16[o2 + 6] = v3;
this.int16[o2 + 7] = v4;
return i;
}
};
StructArrayLayout2f1f2i16.prototype.bytesPerElement = 16;
register("StructArrayLayout2f1f2i16", StructArrayLayout2f1f2i16);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint8[2]
* [4] - Float32[2]
* [12] - Int16[2]
*
*/
var StructArrayLayout2ub2f2i16 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5);
}
emplace(i, v0, v1, v2, v3, v4, v5) {
const o1 = i * 16;
const o4 = i * 4;
const o2 = i * 8;
this.uint8[o1 + 0] = v0;
this.uint8[o1 + 1] = v1;
this.float32[o4 + 1] = v2;
this.float32[o4 + 2] = v3;
this.int16[o2 + 6] = v4;
this.int16[o2 + 7] = v5;
return i;
}
};
StructArrayLayout2ub2f2i16.prototype.bytesPerElement = 16;
register("StructArrayLayout2ub2f2i16", StructArrayLayout2ub2f2i16);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint16[3]
*
*/
var StructArrayLayout3ui6 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2);
}
emplace(i, v0, v1, v2) {
const o2 = i * 3;
this.uint16[o2 + 0] = v0;
this.uint16[o2 + 1] = v1;
this.uint16[o2 + 2] = v2;
return i;
}
};
StructArrayLayout3ui6.prototype.bytesPerElement = 6;
register("StructArrayLayout3ui6", StructArrayLayout3ui6);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[2]
* [4] - Uint16[2]
* [8] - Uint32[3]
* [20] - Uint16[3]
* [28] - Float32[2]
* [36] - Uint8[3]
* [40] - Uint32[1]
* [44] - Int16[1]
*
*/
var StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
this.uint32 = new Uint32Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16);
}
emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) {
const o2 = i * 24;
const o4 = i * 12;
const o1 = i * 48;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.uint16[o2 + 2] = v2;
this.uint16[o2 + 3] = v3;
this.uint32[o4 + 2] = v4;
this.uint32[o4 + 3] = v5;
this.uint32[o4 + 4] = v6;
this.uint16[o2 + 10] = v7;
this.uint16[o2 + 11] = v8;
this.uint16[o2 + 12] = v9;
this.float32[o4 + 7] = v10;
this.float32[o4 + 8] = v11;
this.uint8[o1 + 36] = v12;
this.uint8[o1 + 37] = v13;
this.uint8[o1 + 38] = v14;
this.uint32[o4 + 10] = v15;
this.int16[o2 + 22] = v16;
return i;
}
};
StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48.prototype.bytesPerElement = 48;
register("StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48", StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Int16[8]
* [16] - Uint16[15]
* [48] - Uint32[1]
* [52] - Float32[2]
* [60] - Uint16[2]
*
*/
var StructArrayLayout8i15ui1ul2f2ui64 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.int16 = new Int16Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
this.uint32 = new Uint32Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27);
}
emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) {
const o2 = i * 32;
const o4 = i * 16;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
this.int16[o2 + 3] = v3;
this.int16[o2 + 4] = v4;
this.int16[o2 + 5] = v5;
this.int16[o2 + 6] = v6;
this.int16[o2 + 7] = v7;
this.uint16[o2 + 8] = v8;
this.uint16[o2 + 9] = v9;
this.uint16[o2 + 10] = v10;
this.uint16[o2 + 11] = v11;
this.uint16[o2 + 12] = v12;
this.uint16[o2 + 13] = v13;
this.uint16[o2 + 14] = v14;
this.uint16[o2 + 15] = v15;
this.uint16[o2 + 16] = v16;
this.uint16[o2 + 17] = v17;
this.uint16[o2 + 18] = v18;
this.uint16[o2 + 19] = v19;
this.uint16[o2 + 20] = v20;
this.uint16[o2 + 21] = v21;
this.uint16[o2 + 22] = v22;
this.uint32[o4 + 12] = v23;
this.float32[o4 + 13] = v24;
this.float32[o4 + 14] = v25;
this.uint16[o2 + 30] = v26;
this.uint16[o2 + 31] = v27;
return i;
}
};
StructArrayLayout8i15ui1ul2f2ui64.prototype.bytesPerElement = 64;
register("StructArrayLayout8i15ui1ul2f2ui64", StructArrayLayout8i15ui1ul2f2ui64);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Float32[1]
*
*/
var StructArrayLayout1f4 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
}
emplaceBack(v0) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0);
}
emplace(i, v0) {
const o4 = i * 1;
this.float32[o4 + 0] = v0;
return i;
}
};
StructArrayLayout1f4.prototype.bytesPerElement = 4;
register("StructArrayLayout1f4", StructArrayLayout1f4);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint16[1]
* [4] - Float32[2]
*
*/
var StructArrayLayout1ui2f12 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2);
}
emplace(i, v0, v1, v2) {
const o2 = i * 6;
const o4 = i * 3;
this.uint16[o2 + 0] = v0;
this.float32[o4 + 1] = v1;
this.float32[o4 + 2] = v2;
return i;
}
};
StructArrayLayout1ui2f12.prototype.bytesPerElement = 12;
register("StructArrayLayout1ui2f12", StructArrayLayout1ui2f12);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint32[1]
* [4] - Uint16[2]
*
*/
var StructArrayLayout1ul2ui8 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint32 = new Uint32Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2);
}
emplace(i, v0, v1, v2) {
const o4 = i * 2;
const o2 = i * 4;
this.uint32[o4 + 0] = v0;
this.uint16[o2 + 2] = v1;
this.uint16[o2 + 3] = v2;
return i;
}
};
StructArrayLayout1ul2ui8.prototype.bytesPerElement = 8;
register("StructArrayLayout1ul2ui8", StructArrayLayout1ul2ui8);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint16[2]
*
*/
var StructArrayLayout2ui4 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0, v1) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1);
}
emplace(i, v0, v1) {
const o2 = i * 2;
this.uint16[o2 + 0] = v0;
this.uint16[o2 + 1] = v1;
return i;
}
};
StructArrayLayout2ui4.prototype.bytesPerElement = 4;
register("StructArrayLayout2ui4", StructArrayLayout2ui4);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Uint16[1]
*
*/
var StructArrayLayout1ui2 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}
emplaceBack(v0) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0);
}
emplace(i, v0) {
const o2 = i * 1;
this.uint16[o2 + 0] = v0;
return i;
}
};
StructArrayLayout1ui2.prototype.bytesPerElement = 2;
register("StructArrayLayout1ui2", StructArrayLayout1ui2);
/**
* @internal
* Implementation of the StructArray layout:
* [0] - Float32[4]
*
*/
var StructArrayLayout4f16 = class extends StructArray {
_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.float32 = new Float32Array(this.arrayBuffer);
}
emplaceBack(v0, v1, v2, v3) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3);
}
emplace(i, v0, v1, v2, v3) {
const o4 = i * 4;
this.float32[o4 + 0] = v0;
this.float32[o4 + 1] = v1;
this.float32[o4 + 2] = v2;
this.float32[o4 + 3] = v3;
return i;
}
};
StructArrayLayout4f16.prototype.bytesPerElement = 16;
register("StructArrayLayout4f16", StructArrayLayout4f16);
/** @internal */
var CollisionBoxStruct = class extends Struct {
get anchorPointX() {
return this._structArray.int16[this._pos2 + 0];
}
get anchorPointY() {
return this._structArray.int16[this._pos2 + 1];
}
get x1() {
return this._structArray.int16[this._pos2 + 2];
}
get y1() {
return this._structArray.int16[this._pos2 + 3];
}
get x2() {
return this._structArray.int16[this._pos2 + 4];
}
get y2() {
return this._structArray.int16[this._pos2 + 5];
}
get featureIndex() {
return this._structArray.uint32[this._pos4 + 3];
}
get sourceLayerIndex() {
return this._structArray.uint16[this._pos2 + 8];
}
get bucketIndex() {
return this._structArray.uint16[this._pos2 + 9];
}
get anchorPoint() {
return new Point(this.anchorPointX, this.anchorPointY);
}
};
CollisionBoxStruct.prototype.size = 20;
/** @internal */
var CollisionBoxArray = class extends StructArrayLayout6i1ul2ui20 {
/**
* Return the CollisionBoxStruct at the given location in the array.
* @param index - The index of the element.
*/
get(index) {
return new CollisionBoxStruct(this, index);
}
};
register("CollisionBoxArray", CollisionBoxArray);
/** @internal */
var PlacedSymbolStruct = class extends Struct {
get anchorX() {
return this._structArray.int16[this._pos2 + 0];
}
get anchorY() {
return this._structArray.int16[this._pos2 + 1];
}
get glyphStartIndex() {
return this._structArray.uint16[this._pos2 + 2];
}
get numGlyphs() {
return this._structArray.uint16[this._pos2 + 3];
}
get vertexStartIndex() {
return this._structArray.uint32[this._pos4 + 2];
}
get lineStartIndex() {
return this._structArray.uint32[this._pos4 + 3];
}
get lineLength() {
return this._structArray.uint32[this._pos4 + 4];
}
get segment() {
return this._structArray.uint16[this._pos2 + 10];
}
get lowerSize() {
return this._structArray.uint16[this._pos2 + 11];
}
get upperSize() {
return this._structArray.uint16[this._pos2 + 12];
}
get lineOffsetX() {
return this._structArray.float32[this._pos4 + 7];
}
get lineOffsetY() {
return this._structArray.float32[this._pos4 + 8];
}
get writingMode() {
return this._structArray.uint8[this._pos1 + 36];
}
get placedOrientation() {
return this._structArray.uint8[this._pos1 + 37];
}
set placedOrientation(x) {
this._structArray.uint8[this._pos1 + 37] = x;
}
get hidden() {
return this._structArray.uint8[this._pos1 + 38];
}
set hidden(x) {
this._structArray.uint8[this._pos1 + 38] = x;
}
get crossTileID() {
return this._structArray.uint32[this._pos4 + 10];
}
set crossTileID(x) {
this._structArray.uint32[this._pos4 + 10] = x;
}
get associatedIconIndex() {
return this._structArray.int16[this._pos2 + 22];
}
};
PlacedSymbolStruct.prototype.size = 48;
/** @internal */
var PlacedSymbolArray = class extends StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48 {
/**
* Return the PlacedSymbolStruct at the given location in the array.
* @param index - The index of the element.
*/
get(index) {
return new PlacedSymbolStruct(this, index);
}
};
register("PlacedSymbolArray", PlacedSymbolArray);
/** @internal */
var SymbolInstanceStruct = class extends Struct {
get anchorX() {
return this._structArray.int16[this._pos2 + 0];
}
get anchorY() {
return this._structArray.int16[this._pos2 + 1];
}
get rightJustifiedTextSymbolIndex() {
return this._structArray.int16[this._pos2 + 2];
}
get centerJustifiedTextSymbolIndex() {
return this._structArray.int16[this._pos2 + 3];
}
get leftJustifiedTextSymbolIndex() {
return this._structArray.int16[this._pos2 + 4];
}
get verticalPlacedTextSymbolIndex() {
return this._structArray.int16[this._pos2 + 5];
}
get placedIconSymbolIndex() {
return this._structArray.int16[this._pos2 + 6];
}
get verticalPlacedIconSymbolIndex() {
return this._structArray.int16[this._pos2 + 7];
}
get key() {
return this._structArray.uint16[this._pos2 + 8];
}
get textBoxStartIndex() {
return this._structArray.uint16[this._pos2 + 9];
}
get textBoxEndIndex() {
return this._structArray.uint16[this._pos2 + 10];
}
get verticalTextBoxStartIndex() {
return this._structArray.uint16[this._pos2 + 11];
}
get verticalTextBoxEndIndex() {
return this._structArray.uint16[this._pos2 + 12];
}
get iconBoxStartIndex() {
return this._structArray.uint16[this._pos2 + 13];
}
get iconBoxEndIndex() {
return this._structArray.uint16[this._pos2 + 14];
}
get verticalIconBoxStartIndex() {
return this._structArray.uint16[this._pos2 + 15];
}
get verticalIconBoxEndIndex() {
return this._structArray.uint16[this._pos2 + 16];
}
get featureIndex() {
return this._structArray.uint16[this._pos2 + 17];
}
get numHorizontalGlyphVertices() {
return this._structArray.uint16[this._pos2 + 18];
}
get numVerticalGlyphVertices() {
return this._structArray.uint16[this._pos2 + 19];
}
get numIconVertices() {
return this._structArray.uint16[this._pos2 + 20];
}
get numVerticalIconVertices() {
return this._structArray.uint16[this._pos2 + 21];
}
get useRuntimeCollisionCircles() {
return this._structArray.uint16[this._pos2 + 22];
}
get crossTileID() {
return this._structArray.uint32[this._pos4 + 12];
}
set crossTileID(x) {
this._structArray.uint32[this._pos4 + 12] = x;
}
get textBoxScale() {
return this._structArray.float32[this._pos4 + 13];
}
get collisionCircleDiameter() {
return this._structArray.float32[this._pos4 + 14];
}
get textAnchorOffsetStartIndex() {
return this._structArray.uint16[this._pos2 + 30];
}
get textAnchorOffsetEndIndex() {
return this._structArray.uint16[this._pos2 + 31];
}
};
SymbolInstanceStruct.prototype.size = 64;
/** @internal */
var SymbolInstanceArray = class extends StructArrayLayout8i15ui1ul2f2ui64 {
/**
* Return the SymbolInstanceStruct at the given location in the array.
* @param index - The index of the element.
*/
get(index) {
return new SymbolInstanceStruct(this, index);
}
};
register("SymbolInstanceArray", SymbolInstanceArray);
/** @internal */
var GlyphOffsetArray = class extends StructArrayLayout1f4 {
getoffsetX(index) {
return this.float32[index * 1 + 0];
}
};
register("GlyphOffsetArray", GlyphOffsetArray);
/** @internal */
var SymbolLineVertexArray = class extends StructArrayLayout3i6 {
getx(index) {
return this.int16[index * 3 + 0];
}
gety(index) {
return this.int16[index * 3 + 1];
}
gettileUnitDistanceFromAnchor(index) {
return this.int16[index * 3 + 2];
}
};
register("SymbolLineVertexArray", SymbolLineVertexArray);
/** @internal */
var TextAnchorOffsetStruct = class extends Struct {
get textAnchor() {
return this._structArray.uint16[this._pos2 + 0];
}
get textOffset0() {
return this._structArray.float32[this._pos4 + 1];
}
get textOffset1() {
return this._structArray.float32[this._pos4 + 2];
}
};
TextAnchorOffsetStruct.prototype.size = 12;
/** @internal */
var TextAnchorOffsetArray = class extends StructArrayLayout1ui2f12 {
/**
* Return the TextAnchorOffsetStruct at the given location in the array.
* @param index - The index of the element.
*/
get(index) {
return new TextAnchorOffsetStruct(this, index);
}
};
register("TextAnchorOffsetArray", TextAnchorOffsetArray);
/** @internal */
var FeatureIndexStruct = class extends Struct {
get featureIndex() {
return this._structArray.uint32[this._pos4 + 0];
}
get sourceLayerIndex() {
return this._structArray.uint16[this._pos2 + 2];
}
get bucketIndex() {
return this._structArray.uint16[this._pos2 + 3];
}
};
FeatureIndexStruct.prototype.size = 8;
/** @internal */
var FeatureIndexArray = class extends StructArrayLayout1ul2ui8 {
/**
* Return the FeatureIndexStruct at the given location in the array.
* @param index - The index of the element.
*/
get(index) {
return new FeatureIndexStruct(this, index);
}
};
register("FeatureIndexArray", FeatureIndexArray);
var PosArray = class extends StructArrayLayout2i4 {};
var Pos3dArray = class extends StructArrayLayout3i6 {};
var RasterBoundsArray = class extends StructArrayLayout4i8 {};
var CircleLayoutArray = class extends StructArrayLayout2i4 {};
var FillLayoutArray = class extends StructArrayLayout2i4 {};
var FillExtrusionLayoutArray = class extends StructArrayLayout2i4i12 {};
var LineLayoutArray = class extends StructArrayLayout2i4ub8 {};
var LineExtLayoutArray = class extends StructArrayLayout2f8 {};
var PatternLayoutArray = class extends StructArrayLayout10ui20 {};
var DashLayoutArray = class extends StructArrayLayout8ui16 {};
var SymbolLayoutArray = class extends StructArrayLayout4i4ui4i24 {};
var SymbolDynamicLayoutArray = class extends StructArrayLayout3f12 {};
var SymbolOpacityArray = class extends StructArrayLayout1ul4 {};
var CollisionBoxLayoutArray = class extends StructArrayLayout2i2i2i12 {};
var CollisionCircleLayoutArray = class extends StructArrayLayout2f1f2i16 {};
var CollisionVertexArray = class extends StructArrayLayout2ub2f2i16 {};
var QuadTriangleArray = class extends StructArrayLayout3ui6 {};
var TriangleIndexArray = class extends StructArrayLayout3ui6 {};
var LineIndexArray = class extends StructArrayLayout2ui4 {};
var LineStripIndexArray = class extends StructArrayLayout1ui2 {};
//#endregion
//#region src/data/bucket/circle_attributes.ts
const layout$7 = createLayout([{
name: "a_pos",
components: 2,
type: "Int16"
}], 4);
const members$4 = layout$7.members;
layout$7.size;
layout$7.alignment;
//#endregion
//#region src/data/segment.ts
/**
* @internal
* Used for calculations on vector segments
*/
var SegmentVector = class SegmentVector {
constructor(segments = []) {
this._forceNewSegmentOnNextPrepare = false;
this.segments = segments;
}
/**
* Returns the last segment if `numVertices` fits into it.
* If there are no segments yet or `numVertices` doesn't fit into the last one, creates a new empty segment and returns it.
*/
prepareSegment(numVertices, layoutVertexArray, indexArray, sortKey) {
const lastSegment = this.segments[this.segments.length - 1];
if (numVertices > SegmentVector.MAX_VERTEX_ARRAY_LENGTH) warnOnce(`Max vertices per segment is ${SegmentVector.MAX_VERTEX_ARRAY_LENGTH}: bucket requested ${numVertices}. Consider using the \`fillLargeMeshArrays\` function if you require meshes with more than ${SegmentVector.MAX_VERTEX_ARRAY_LENGTH} vertices.`);
if (this._forceNewSegmentOnNextPrepare || !lastSegment || lastSegment.vertexLength + numVertices > SegmentVector.MAX_VERTEX_ARRAY_LENGTH || lastSegment.sortKey !== sortKey) return this.createNewSegment(layoutVertexArray, indexArray, sortKey);
else return lastSegment;
}
/**
* Creates a new empty segment and returns it.
*/
createNewSegment(layoutVertexArray, indexArray, sortKey) {
const segment = {
vertexOffset: layoutVertexArray.length,
primitiveOffset: indexArray.length,
vertexLength: 0,
primitiveLength: 0,
vaos: {}
};
if (sortKey !== void 0) segment.sortKey = sortKey;
this._forceNewSegmentOnNextPrepare = false;
this.segments.push(segment);
return segment;
}
/**
* Returns the last segment, or creates a new segments if there are no segments yet.
*/
getOrCreateLatestSegment(layoutVertexArray, indexArray, sortKey) {
return this.prepareSegment(0, layoutVertexArray, indexArray, sortKey);
}
/**
* Causes the next call to {@link prepareSegment} to always return a new segment,
* not reusing the current segment even if the new geometry would fit it.
*/
forceNewSegmentOnNextPrepare() {
this._forceNewSegmentOnNextPrepare = true;
}
get() {
return this.segments;
}
destroy() {
for (const segment of this.segments) for (const k in segment.vaos) segment.vaos[k].destroy();
}
static simpleSegment(vertexOffset, primitiveOffset, vertexLength, primitiveLength) {
return new SegmentVector([{
vertexOffset,
primitiveOffset,
vertexLength,
primitiveLength,
vaos: {},
sortKey: 0
}]);
}
};
/**
* The maximum size of a vertex array. This limit is imposed by WebGL's 16 bit
* addressing of vertex buffers.
*/
SegmentVector.MAX_VERTEX_ARRAY_LENGTH = Math.pow(2, 16) - 1;
register("SegmentVector", SegmentVector);
//#endregion
//#region src/shaders/encode_attribute.ts
/**
* Packs two numbers, interpreted as 8-bit unsigned integers, into a single
* float. Unpack them in the shader using the `unpack_float()` function,
* defined in _prelude.vertex.glsl
*/
function packUint8ToFloat(a, b) {
a = clamp$2(Math.floor(a), 0, 255);
b = clamp$2(Math.floor(b), 0, 255);
return 256 * a + b;
}
//#endregion
//#region src/data/bucket/pattern_attributes.ts
const patternAttributes = createLayout([
{
name: "a_pattern_from",
components: 4,
type: "Uint16"
},
{
name: "a_pattern_to",
components: 4,
type: "Uint16"
},
{
name: "a_pixel_ratio_from",
components: 1,
type: "Uint16"
},
{
name: "a_pixel_ratio_to",
components: 1,
type: "Uint16"
}
]);
//#endregion
//#region src/data/bucket/dash_attributes.ts
const dashAttributes = createLayout([{
name: "a_dasharray_from",
components: 4,
type: "Uint16"
}, {
name: "a_dasharray_to",
components: 4,
type: "Uint16"
}]);
//#endregion
//#region node_modules/murmurhash-js/murmurhash3_gc.js
var require_murmurhash3_gc = /* @__PURE__ */ __commonJSMin(((exports, module) => {
/**
* JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
*
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
* @see http://github.com/garycourt/murmurhash-js
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
* @see http://sites.google.com/site/murmurhash/
*
* @param {string} key ASCII only
* @param {number} seed Positive integer only
* @return {number} 32-bit positive integer hash
*/
function murmurhash3_32_gc(key, seed) {
var remainder = key.length & 3, bytes = key.length - remainder, h1 = seed, h1b, c1 = 3432918353, c2 = 461845907, k1, i = 0;
while (i < bytes) {
k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
++i;
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
k1 = k1 << 15 | k1 >>> 17;
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
h1 ^= k1;
h1 = h1 << 13 | h1 >>> 19;
h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
}
k1 = 0;
switch (remainder) {
case 3: k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
case 2: k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
case 1:
k1 ^= key.charCodeAt(i) & 255;
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
k1 = k1 << 15 | k1 >>> 17;
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
h1 ^= k1;
}
h1 ^= key.length;
h1 ^= h1 >>> 16;
h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
h1 ^= h1 >>> 13;
h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
h1 ^= h1 >>> 16;
return h1 >>> 0;
}
if (typeof module !== "undefined") module.exports = murmurhash3_32_gc;
}));
//#endregion
//#region node_modules/murmurhash-js/murmurhash2_gc.js
var require_murmurhash2_gc = /* @__PURE__ */ __commonJSMin(((exports, module) => {
/**
* JS Implementation of MurmurHash2
*
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
* @see http://github.com/garycourt/murmurhash-js
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
* @see http://sites.google.com/site/murmurhash/
*
* @param {string} str ASCII only
* @param {number} seed Positive integer only
* @return {number} 32-bit positive integer hash
*/
function murmurhash2_32_gc(str, seed) {
var l = str.length, h = seed ^ l, i = 0, k;
while (l >= 4) {
k = str.charCodeAt(i) & 255 | (str.charCodeAt(++i) & 255) << 8 | (str.charCodeAt(++i) & 255) << 16 | (str.charCodeAt(++i) & 255) << 24;
k = (k & 65535) * 1540483477 + (((k >>> 16) * 1540483477 & 65535) << 16);
k ^= k >>> 24;
k = (k & 65535) * 1540483477 + (((k >>> 16) * 1540483477 & 65535) << 16);
h = (h & 65535) * 1540483477 + (((h >>> 16) * 1540483477 & 65535) << 16) ^ k;
l -= 4;
++i;
}
switch (l) {
case 3: h ^= (str.charCodeAt(i + 2) & 255) << 16;
case 2: h ^= (str.charCodeAt(i + 1) & 255) << 8;
case 1:
h ^= str.charCodeAt(i) & 255;
h = (h & 65535) * 1540483477 + (((h >>> 16) * 1540483477 & 65535) << 16);
}
h ^= h >>> 13;
h = (h & 65535) * 1540483477 + (((h >>> 16) * 1540483477 & 65535) << 16);
h ^= h >>> 15;
return h >>> 0;
}
module.exports = murmurhash2_32_gc;
}));
//#endregion
//#region src/data/feature_position_map.ts
var import_murmurhash_js = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
var murmur3 = require_murmurhash3_gc();
var murmur2 = require_murmurhash2_gc();
module.exports = murmur3;
module.exports.murmur3 = murmur3;
module.exports.murmur2 = murmur2;
})))(), 1);
var FeaturePositionMap = class FeaturePositionMap {
constructor() {
this.ids = [];
this.positions = [];
this.indexed = false;
}
add(id, index, start, end) {
this.ids.push(getNumericId(id));
this.positions.push(index, start, end);
}
getPositions(id) {
if (!this.indexed) throw new Error("Trying to get index, but feature positions are not indexed");
const intId = getNumericId(id);
let i = 0;
let j = this.ids.length - 1;
while (i < j) {
const m = i + j >> 1;
if (this.ids[m] >= intId) j = m;
else i = m + 1;
}
const positions = [];
while (this.ids[i] === intId) {
const index = this.positions[3 * i];
const start = this.positions[3 * i + 1];
const end = this.positions[3 * i + 2];
positions.push({
index,
start,
end
});
i++;
}
return positions;
}
static serialize(map, transferables) {
const ids = new Float64Array(map.ids);
const positions = new Uint32Array(map.positions);
sort$1(ids, positions, 0, ids.length - 1);
if (transferables) transferables.push(ids.buffer, positions.buffer);
return {
ids,
positions
};
}
static deserialize(obj) {
const map = new FeaturePositionMap();
map.ids = obj.ids;
map.positions = obj.positions;
map.indexed = true;
return map;
}
};
function getNumericId(value) {
const numValue = +value;
if (!isNaN(numValue) && numValue <= Number.MAX_SAFE_INTEGER) return numValue;
return (0, import_murmurhash_js.default)(String(value));
}
function sort$1(ids, positions, left, right) {
while (left < right) {
const pivot = ids[left + right >> 1];
let i = left - 1;
let j = right + 1;
while (true) {
do
i++;
while (ids[i] < pivot);
do
j--;
while (ids[j] > pivot);
if (i >= j) break;
swap$1(ids, i, j);
swap$1(positions, 3 * i, 3 * j);
swap$1(positions, 3 * i + 1, 3 * j + 1);
swap$1(positions, 3 * i + 2, 3 * j + 2);
}
if (j - left < right - j) {
sort$1(ids, positions, left, j);
left = j + 1;
} else {
sort$1(ids, positions, j + 1, right);
right = j;
}
}
}
function swap$1(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
register("FeaturePositionMap", FeaturePositionMap);
//#endregion
//#region src/webgl/uniform_binding.ts
/**
* @internal
* A base uniform abstract class
*/
var Uniform = class {
constructor(context, location) {
this.gl = context.gl;
this.location = location;
}
};
var Uniform1i = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = 0;
}
set(v) {
if (this.current !== v) {
this.current = v;
this.gl.uniform1i(this.location, v);
}
}
};
var Uniform1f = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = 0;
}
set(v) {
if (this.current !== v) {
this.current = v;
this.gl.uniform1f(this.location, v);
}
}
};
var Uniform2f = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = [0, 0];
}
set(v) {
if (v[0] !== this.current[0] || v[1] !== this.current[1]) {
this.current = v;
this.gl.uniform2f(this.location, v[0], v[1]);
}
}
};
var Uniform3f = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = [
0,
0,
0
];
}
set(v) {
if (v[0] !== this.current[0] || v[1] !== this.current[1] || v[2] !== this.current[2]) {
this.current = v;
this.gl.uniform3f(this.location, v[0], v[1], v[2]);
}
}
};
var Uniform4f = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = [
0,
0,
0,
0
];
}
set(v) {
if (v[0] !== this.current[0] || v[1] !== this.current[1] || v[2] !== this.current[2] || v[3] !== this.current[3]) {
this.current = v;
this.gl.uniform4f(this.location, v[0], v[1], v[2], v[3]);
}
}
};
var UniformColor = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = Color.transparent;
}
set(v) {
if (v.r !== this.current.r || v.g !== this.current.g || v.b !== this.current.b || v.a !== this.current.a) {
this.current = v;
this.gl.uniform4f(this.location, v.r, v.g, v.b, v.a);
}
}
};
var UniformColorArray = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = new Array();
}
set(v) {
if (v != this.current) {
this.current = v;
const values = new Float32Array(v.length * 4);
for (let i = 0; i < v.length; i++) {
values[4 * i] = v[i].r;
values[4 * i + 1] = v[i].g;
values[4 * i + 2] = v[i].b;
values[4 * i + 3] = v[i].a;
}
this.gl.uniform4fv(this.location, values);
}
}
};
var UniformFloatArray = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = new Array();
}
set(v) {
if (v != this.current) {
this.current = v;
const values = new Float32Array(v);
this.gl.uniform1fv(this.location, values);
}
}
};
const emptyMat4 = /* @__PURE__ */ new Float32Array(16);
var UniformMatrix4f = class extends Uniform {
constructor(context, location) {
super(context, location);
this.current = emptyMat4;
}
set(v) {
if (v[12] !== this.current[12] || v[0] !== this.current[0]) {
this.current = v;
this.gl.uniformMatrix4fv(this.location, false, v);
return;
}
for (let i = 1; i < 16; i++) if (v[i] !== this.current[i]) {
this.current = v;
this.gl.uniformMatrix4fv(this.location, false, v);
break;
}
}
};
//#endregion
//#region src/data/program_configuration.ts
function packColor(color) {
return [packUint8ToFloat(255 * color.r, 255 * color.g), packUint8ToFloat(255 * color.b, 255 * color.a)];
}
var ConstantBinder = class {
constructor(value, names, type) {
this.value = value;
this.uniformNames = names.map((name) => `u_${name}`);
this.type = type;
}
setUniform(uniform, globals, currentValue) {
uniform.set(currentValue.constantOr(this.value));
}
getBinding(context, location, _) {
return this.type === "color" ? new UniformColor(context, location) : new Uniform1f(context, location);
}
};
var CrossFadedConstantBinder = class {
constructor(value, names) {
this.uniformNames = names.map((name) => `u_${name}`);
this.patternFrom = null;
this.patternTo = null;
this.pixelRatioFrom = 1;
this.pixelRatioTo = 1;
}
setConstantPatternPositions(posTo, posFrom) {
this.pixelRatioFrom = posFrom.pixelRatio;
this.pixelRatioTo = posTo.pixelRatio;
this.patternFrom = posFrom.tlbr;
this.patternTo = posTo.tlbr;
}
setConstantDashPositions(dashTo, dashFrom) {
this.dashTo = [
0,
dashTo.y,
dashTo.height,
dashTo.width
];
this.dashFrom = [
0,
dashFrom.y,
dashFrom.height,
dashFrom.width
];
}
setUniform(uniform, globals, currentValue, uniformName) {
let value = null;
if (uniformName === "u_pattern_to") value = this.patternTo;
else if (uniformName === "u_pattern_from") value = this.patternFrom;
else if (uniformName === "u_dasharray_to") value = this.dashTo;
else if (uniformName === "u_dasharray_from") value = this.dashFrom;
else if (uniformName === "u_pixel_ratio_to") value = this.pixelRatioTo;
else if (uniformName === "u_pixel_ratio_from") value = this.pixelRatioFrom;
if (value !== null) uniform.set(value);
}
getBinding(context, location, name) {
return name.startsWith("u_pattern") || name.startsWith("u_dasharray_") ? new Uniform4f(context, location) : new Uniform1f(context, location);
}
};
var SourceExpressionBinder = class {
constructor(expression, names, type, PaintVertexArray) {
this.expression = expression;
this.type = type;
this.maxValue = 0;
this.paintVertexAttributes = names.map((name) => ({
name: `a_${name}`,
type: "Float32",
components: type === "color" ? 2 : 1,
offset: 0
}));
this.paintVertexArray = new PaintVertexArray();
}
populatePaintArray(newLength, feature, options) {
const start = this.paintVertexArray.length;
const value = this.expression.evaluate(new EvaluationParameters(0, options), feature, {}, options.canonical, [], options.formattedSection);
this.paintVertexArray.resize(newLength);
this._setPaintValue(start, newLength, value);
}
updatePaintArray(start, end, feature, featureState, options) {
const value = this.expression.evaluate(new EvaluationParameters(0, options), feature, featureState);
this._setPaintValue(start, end, value);
}
_setPaintValue(start, end, value) {
if (this.type === "color") {
const color = packColor(value);
for (let i = start; i < end; i++) this.paintVertexArray.emplace(i, color[0], color[1]);
} else {
for (let i = start; i < end; i++) this.paintVertexArray.emplace(i, value);
this.maxValue = Math.max(this.maxValue, Math.abs(value));
}
}
upload(context) {
if (this.paintVertexArray?.arrayBuffer.byteLength) {
if (this.paintVertexBuffer?.buffer) this.paintVertexBuffer.updateData(this.paintVertexArray);
else this.paintVertexBuffer = context.createVertexBuffer(this.paintVertexArray, this.paintVertexAttributes, this.expression.isStateDependent);
}
}
destroy() {
if (this.paintVertexBuffer) this.paintVertexBuffer.destroy();
}
};
var CompositeExpressionBinder = class {
constructor(expression, names, type, useIntegerZoom, zoom, PaintVertexArray) {
this.expression = expression;
this.uniformNames = names.map((name) => `u_${name}_t`);
this.type = type;
this.useIntegerZoom = useIntegerZoom;
this.zoom = zoom;
this.maxValue = 0;
this.paintVertexAttributes = names.map((name) => ({
name: `a_${name}`,
type: "Float32",
components: type === "color" ? 4 : 2,
offset: 0
}));
this.paintVertexArray = new PaintVertexArray();
}
populatePaintArray(newLength, feature, options) {
const min = this.expression.evaluate(new EvaluationParameters(this.zoom, options), feature, {}, options.canonical, [], options.formattedSection);
const max = this.expression.evaluate(new EvaluationParameters(this.zoom + 1, options), feature, {}, options.canonical, [], options.formattedSection);
const start = this.paintVertexArray.length;
this.paintVertexArray.resize(newLength);
this._setPaintValue(start, newLength, min, max);
}
updatePaintArray(start, end, feature, featureState, options) {
const min = this.expression.evaluate(new EvaluationParameters(this.zoom, options), feature, featureState);
const max = this.expression.evaluate(new EvaluationParameters(this.zoom + 1, options), feature, featureState);
this._setPaintValue(start, end, min, max);
}
_setPaintValue(start, end, min, max) {
if (this.type === "color") {
const minColor = packColor(min);
const maxColor = packColor(max);
for (let i = start; i < end; i++) this.paintVertexArray.emplace(i, minColor[0], minColor[1], maxColor[0], maxColor[1]);
} else {
for (let i = start; i < end; i++) this.paintVertexArray.emplace(i, min, max);
this.maxValue = Math.max(this.maxValue, Math.abs(min), Math.abs(max));
}
}
upload(context) {
if (this.paintVertexArray?.arrayBuffer.byteLength) {
if (this.paintVertexBuffer?.buffer) this.paintVertexBuffer.updateData(this.paintVertexArray);
else this.paintVertexBuffer = context.createVertexBuffer(this.paintVertexArray, this.paintVertexAttributes, this.expression.isStateDependent);
}
}
destroy() {
if (this.paintVertexBuffer) this.paintVertexBuffer.destroy();
}
setUniform(uniform, globals) {
const currentZoom = this.useIntegerZoom ? Math.floor(globals.zoom) : globals.zoom;
const factor = clamp$2(this.expression.interpolationFactor(currentZoom, this.zoom, this.zoom + 1), 0, 1);
uniform.set(factor);
}
getBinding(context, location, _) {
return new Uniform1f(context, location);
}
};
var CrossFadedBinder = class {
constructor(expression, type, useIntegerZoom, zoom, PaintVertexArray, layerId) {
this.expression = expression;
this.type = type;
this.useIntegerZoom = useIntegerZoom;
this.zoom = zoom;
this.layerId = layerId;
this.zoomInPaintVertexArray = new PaintVertexArray();
this.zoomOutPaintVertexArray = new PaintVertexArray();
}
populatePaintArray(length, feature, options) {
const start = this.zoomInPaintVertexArray.length;
this.zoomInPaintVertexArray.resize(length);
this.zoomOutPaintVertexArray.resize(length);
this._setPaintValues(start, length, this.getPositionIds(feature), options);
}
updatePaintArray(start, end, feature, featureState, options) {
this._setPaintValues(start, end, this.getPositionIds(feature), options);
}
_setPaintValues(start, end, positionIds, options) {
const positions = this.getPositions(options);
if (!positions || !positionIds) return;
const min = positions[positionIds.min];
const mid = positions[positionIds.mid];
const max = positions[positionIds.max];
if (!min || !mid || !max) return;
for (let i = start; i < end; i++) {
this.emplace(this.zoomInPaintVertexArray, i, min, mid);
this.emplace(this.zoomOutPaintVertexArray, i, max, mid);
}
}
upload(context) {
if (this.zoomInPaintVertexArray?.arrayBuffer.byteLength && this.zoomOutPaintVertexArray?.arrayBuffer.byteLength) {
const attributes = this.getVertexAttributes();
this.zoomInPaintVertexBuffer = context.createVertexBuffer(this.zoomInPaintVertexArray, attributes, this.expression.isStateDependent);
this.zoomOutPaintVertexBuffer = context.createVertexBuffer(this.zoomOutPaintVertexArray, attributes, this.expression.isStateDependent);
}
}
destroy() {
if (this.zoomOutPaintVertexBuffer) this.zoomOutPaintVertexBuffer.destroy();
if (this.zoomInPaintVertexBuffer) this.zoomInPaintVertexBuffer.destroy();
}
};
var CrossFadedPatternBinder = class extends CrossFadedBinder {
getPositions(options) {
return options.imagePositions;
}
getPositionIds(feature) {
return feature.patterns?.[this.layerId];
}
getVertexAttributes() {
return patternAttributes.members;
}
emplace(array, index, fromPos, toPos) {
array.emplace(index, fromPos.tlbr[0], fromPos.tlbr[1], fromPos.tlbr[2], fromPos.tlbr[3], toPos.tlbr[0], toPos.tlbr[1], toPos.tlbr[2], toPos.tlbr[3], fromPos.pixelRatio, toPos.pixelRatio);
}
};
var CrossFadedDasharrayBinder = class extends CrossFadedBinder {
getPositions(options) {
return options.dashPositions;
}
getPositionIds(feature) {
return feature.dashes?.[this.layerId];
}
getVertexAttributes() {
return dashAttributes.members;
}
emplace(array, index, fromPos, toPos) {
array.emplace(index, 0, fromPos.y, fromPos.height, fromPos.width, 0, toPos.y, toPos.height, toPos.width);
}
};
/**
* @internal
* ProgramConfiguration contains the logic for binding style layer properties and tile
* layer feature data into GL program uniforms and vertex attributes.
*
* Non-data-driven property values are bound to shader uniforms. Data-driven property
* values are bound to vertex attributes. In order to support a uniform GLSL syntax over
* both, the [shaders](../shaders/README.md) define a `#pragma` abstraction, which
* ProgramConfiguration is responsible for implementing. At runtime,
* it examines the attributes of a particular layer, combines this with fixed knowledge
* about how layers of the particular type are implemented, and determines which uniforms
* and vertex attributes will be required. It can then substitute the appropriate text
* into the shader source code, create and link a program, and bind the uniforms and
* vertex attributes in preparation for drawing.
*
* When a vector tile is parsed, this same configuration information is used to
* populate the attribute buffers needed for data-driven styling using the zoom
* level and feature property data.
*/
var ProgramConfiguration = class {
constructor(layer, zoom, filterProperties) {
this.binders = {};
this._buffers = [];
const keys = [];
for (const property in layer.paint._values) {
if (!filterProperties(property)) continue;
const value = layer.paint.get(property);
if (!(value instanceof PossiblyEvaluatedPropertyValue) || !supportsPropertyExpression(value.property.specification)) continue;
const names = paintAttributeNames(property, layer.type);
const expression = value.value;
const type = value.property.specification.type;
const useIntegerZoom = value.property.useIntegerZoom;
const propType = value.property.specification["property-type"];
const isCrossFaded = propType === "cross-faded" || propType === "cross-faded-data-driven";
if (expression.kind === "constant") {
this.binders[property] = isCrossFaded ? new CrossFadedConstantBinder(expression.value, names) : new ConstantBinder(expression.value, names, type);
keys.push(`/u_${property}`);
} else if (expression.kind === "source" || isCrossFaded) {
const StructArrayLayout = layoutType(property, type, "source");
this.binders[property] = isCrossFaded ? property === "line-dasharray" ? new CrossFadedDasharrayBinder(expression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) : new CrossFadedPatternBinder(expression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) : new SourceExpressionBinder(expression, names, type, StructArrayLayout);
keys.push(`/a_${property}`);
} else {
const StructArrayLayout = layoutType(property, type, "composite");
this.binders[property] = new CompositeExpressionBinder(expression, names, type, useIntegerZoom, zoom, StructArrayLayout);
keys.push(`/z_${property}`);
}
}
this.cacheKey = keys.sort().join("");
}
getMaxValue(property) {
const binder = this.binders[property];
return binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder ? binder.maxValue : 0;
}
populatePaintArrays(newLength, feature, options) {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder) binder.populatePaintArray(newLength, feature, options);
}
}
setConstantPatternPositions(posTo, posFrom) {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof CrossFadedConstantBinder) binder.setConstantPatternPositions(posTo, posFrom);
}
}
setConstantDashPositions(dashTo, dashFrom) {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof CrossFadedConstantBinder) binder.setConstantDashPositions(dashTo, dashFrom);
}
}
updatePaintArrays(featureStates, featureMap, vtLayer, layer, options) {
let dirty = false;
for (const fs of featureStates) {
const positions = featureMap.getPositions(fs.id);
for (const pos of positions) {
const feature = vtLayer.feature(pos.index);
for (const property in this.binders) {
const binder = this.binders[property];
if ((binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder) && binder.expression.isStateDependent === true) {
binder.expression = layer.paint.get(property).value;
binder.updatePaintArray(pos.start, pos.end, feature, fs.state, options);
dirty = true;
}
}
}
}
return dirty;
}
defines() {
const result = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder) result.push(...binder.uniformNames.map((name) => `#define HAS_UNIFORM_${name}`));
}
return result;
}
getBinderAttributes() {
const result = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder) for (const attribute of binder.paintVertexAttributes) result.push(attribute.name);
else if (binder instanceof CrossFadedBinder) {
const attributes = binder.getVertexAttributes();
for (const attribute of attributes) result.push(attribute.name);
}
}
return result;
}
getBinderUniforms() {
const uniforms = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder || binder instanceof CompositeExpressionBinder) for (const uniformName of binder.uniformNames) uniforms.push(uniformName);
}
return uniforms;
}
getPaintVertexBuffers() {
return this._buffers;
}
getUniforms(context, locations) {
const uniforms = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder || binder instanceof CompositeExpressionBinder) {
for (const name of binder.uniformNames) if (locations[name]) {
const binding = binder.getBinding(context, locations[name], name);
uniforms.push({
name,
property,
binding
});
}
}
}
return uniforms;
}
setUniforms(context, binderUniforms, properties, globals) {
for (const { name, property, binding } of binderUniforms) this.binders[property].setUniform(binding, globals, properties.get(property), name);
}
updatePaintBuffers(crossfade) {
this._buffers = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (crossfade && binder instanceof CrossFadedBinder) {
const patternVertexBuffer = crossfade.fromScale === 2 ? binder.zoomInPaintVertexBuffer : binder.zoomOutPaintVertexBuffer;
if (patternVertexBuffer) this._buffers.push(patternVertexBuffer);
} else if ((binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder) && binder.paintVertexBuffer) this._buffers.push(binder.paintVertexBuffer);
}
}
upload(context) {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder) binder.upload(context);
}
this.updatePaintBuffers();
}
destroy() {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder) binder.destroy();
}
}
};
var ProgramConfigurationSet = class {
constructor(layers, zoom, filterProperties = () => true) {
this.programConfigurations = {};
for (const layer of layers) this.programConfigurations[layer.id] = new ProgramConfiguration(layer, zoom, filterProperties);
this.needsUpload = false;
this._featureMap = new FeaturePositionMap();
this._bufferOffset = 0;
}
populatePaintArrays(length, feature, index, options) {
for (const key in this.programConfigurations) this.programConfigurations[key].populatePaintArrays(length, feature, options);
if (feature.id !== void 0) this._featureMap.add(feature.id, index, this._bufferOffset, length);
this._bufferOffset = length;
this.needsUpload = true;
}
updatePaintArrays(featureStates, vtLayer, layers, options) {
for (const layer of layers) this.needsUpload = this.programConfigurations[layer.id].updatePaintArrays(featureStates, this._featureMap, vtLayer, layer, options) || this.needsUpload;
}
get(layerId) {
return this.programConfigurations[layerId];
}
upload(context) {
if (!this.needsUpload) return;
for (const layerId in this.programConfigurations) this.programConfigurations[layerId].upload(context);
this.needsUpload = false;
}
destroy() {
for (const layerId in this.programConfigurations) this.programConfigurations[layerId].destroy();
}
};
function paintAttributeNames(property, type) {
return {
"text-opacity": ["opacity"],
"icon-opacity": ["opacity"],
"text-color": ["fill_color"],
"icon-color": ["fill_color"],
"text-halo-color": ["halo_color"],
"icon-halo-color": ["halo_color"],
"text-halo-blur": ["halo_blur"],
"icon-halo-blur": ["halo_blur"],
"text-halo-width": ["halo_width"],
"icon-halo-width": ["halo_width"],
"line-gap-width": ["gapwidth"],
"line-dasharray": ["dasharray_to", "dasharray_from"],
"line-pattern": [
"pattern_to",
"pattern_from",
"pixel_ratio_to",
"pixel_ratio_from"
],
"fill-pattern": [
"pattern_to",
"pattern_from",
"pixel_ratio_to",
"pixel_ratio_from"
],
"fill-extrusion-pattern": [
"pattern_to",
"pattern_from",
"pixel_ratio_to",
"pixel_ratio_from"
]
}[property] || [property.replace(`${type}-`, "").replace(/-/g, "_")];
}
function getLayoutException(property) {
return {
"line-pattern": {
"source": PatternLayoutArray,
"composite": PatternLayoutArray
},
"fill-pattern": {
"source": PatternLayoutArray,
"composite": PatternLayoutArray
},
"fill-extrusion-pattern": {
"source": PatternLayoutArray,
"composite": PatternLayoutArray
},
"line-dasharray": {
"source": DashLayoutArray,
"composite": DashLayoutArray
}
}[property];
}
function layoutType(property, type, binderType) {
const defaultLayouts = {
"color": {
"source": StructArrayLayout2f8,
"composite": StructArrayLayout4f16
},
"number": {
"source": StructArrayLayout1f4,
"composite": StructArrayLayout2f8
}
};
return getLayoutException(property)?.[binderType] || defaultLayouts[type][binderType];
}
register("ConstantBinder", ConstantBinder);
register("CrossFadedConstantBinder", CrossFadedConstantBinder);
register("SourceExpressionBinder", SourceExpressionBinder);
register("CrossFadedPatternBinder", CrossFadedPatternBinder);
register("CrossFadedDasharrayBinder", CrossFadedDasharrayBinder);
register("CompositeExpressionBinder", CompositeExpressionBinder);
register("ProgramConfiguration", ProgramConfiguration, { omit: ["_buffers"] });
register("ProgramConfigurationSet", ProgramConfigurationSet);
const MAX = Math.pow(2, 14) - 1;
const MIN = -MAX - 1;
/**
* Loads a geometry from a VectorTileFeatureLike and scales it to the common extent
* used internally.
* @param feature - the vector tile feature to load
*/
function loadGeometry(feature) {
const scale = EXTENT$1 / feature.extent;
const geometry = feature.loadGeometry();
for (const ring of geometry) for (const point of ring) {
const x = Math.round(point.x * scale);
const y = Math.round(point.y * scale);
point.x = clamp$2(x, MIN, MAX);
point.y = clamp$2(y, MIN, MAX);
if (x < point.x || x > point.x + 1 || y < point.y || y > point.y + 1) warnOnce("Geometry exceeds allowed extent, reduce your vector tile buffer size");
}
return geometry;
}
//#endregion
//#region src/data/evaluation_feature.ts
/**
* Construct a new feature based on a VectorTileFeatureLike for expression evaluation, the geometry of which
* will be loaded based on necessity.
* @param feature - the feature to evaluate
* @param needGeometry - if set to true this will load the geometry
*/
function toEvaluationFeature(feature, needGeometry) {
return {
type: feature.type,
id: feature.id,
properties: feature.properties,
geometry: needGeometry ? loadGeometry(feature) : []
};
}
//#endregion
//#region src/data/bucket/circle_bucket.ts
const VERTEX_MIN_VALUE = -32768;
function addCircleVertex(layoutVertexArray, x, y, extrudeX, extrudeY) {
layoutVertexArray.emplaceBack(VERTEX_MIN_VALUE + x * 8 + extrudeX, VERTEX_MIN_VALUE + y * 8 + extrudeY);
}
/**
* @internal
* Circles are represented by two triangles.
*
* Each corner has a pos that is the center of the circle and an extrusion
* vector that is where it points.
*/
var CircleBucket = class {
constructor(options) {
this.zoom = options.zoom;
this.overscaling = options.overscaling;
this.layers = options.layers;
this.layerIds = this.layers.map((layer) => layer.id);
this.index = options.index;
this.hasDependencies = false;
this.layoutVertexArray = new CircleLayoutArray();
this.indexArray = new TriangleIndexArray();
this.segments = new SegmentVector();
this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom);
this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id);
}
populate(features, options, canonical) {
const styleLayer = this.layers[0];
const bucketFeatures = [];
let circleSortKey = null;
let sortFeaturesByKey = false;
let subdivide = styleLayer.type === "heatmap";
if (styleLayer.type === "circle") {
const circleStyle = styleLayer;
circleSortKey = circleStyle.layout.get("circle-sort-key");
sortFeaturesByKey = !circleSortKey.isConstant();
subdivide ||= circleStyle.paint.get("circle-pitch-alignment") === "map";
}
const granularity = subdivide ? options.subdivisionGranularity.circle : 1;
const globalProperties = new EvaluationParameters(this.zoom);
const needGeometry = this.layers[0]._featureFilter.needGeometry;
for (const { feature, id, index, sourceLayerIndex } of features) {
const evaluationFeature = toEvaluationFeature(feature, needGeometry);
if (!this.layers[0]._featureFilter.filter(globalProperties, evaluationFeature, canonical)) continue;
const sortKey = sortFeaturesByKey ? circleSortKey.evaluate(evaluationFeature, {}, canonical) : void 0;
const bucketFeature = {
id,
properties: feature.properties,
type: feature.type,
sourceLayerIndex,
index,
geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
patterns: {},
sortKey
};
bucketFeatures.push(bucketFeature);
}
if (sortFeaturesByKey) bucketFeatures.sort((a, b) => a.sortKey - b.sortKey);
for (const bucketFeature of bucketFeatures) {
const { geometry, index, sourceLayerIndex } = bucketFeature;
const feature = features[index].feature;
this.addFeature(bucketFeature, geometry, index, canonical, granularity);
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
}
}
update(states, vtLayer, imagePositions) {
if (!this.stateDependentLayers.length) return;
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, { imagePositions });
}
isEmpty() {
return this.layoutVertexArray.length === 0;
}
uploadPending() {
return !this.uploaded || this.programConfigurations.needsUpload;
}
upload(context) {
if (!this.uploaded) {
this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, members$4);
this.indexBuffer = context.createIndexBuffer(this.indexArray);
}
this.programConfigurations.upload(context);
this.uploaded = true;
}
destroy() {
if (!this.layoutVertexBuffer) return;
this.layoutVertexBuffer.destroy();
this.indexBuffer.destroy();
this.programConfigurations.destroy();
this.segments.destroy();
}
addFeature(feature, geometry, index, canonical, granularity = 1) {
let extrudes;
switch (granularity) {
case 1:
extrudes = [0, 7];
break;
case 3:
extrudes = [
0,
2,
5,
7
];
break;
case 5:
extrudes = [
0,
1,
3,
4,
6,
7
];
break;
case 7:
extrudes = [
0,
1,
2,
3,
4,
5,
6,
7
];
break;
default: throw new Error(`Invalid circle bucket granularity: ${granularity}; valid values are 1, 3, 5, 7.`);
}
const verticesPerAxis = extrudes.length;
for (const ring of geometry) for (const point of ring) {
const vx = point.x;
const vy = point.y;
if (vx < 0 || vx >= 8192 || vy < 0 || vy >= 8192) continue;
const segment = this.segments.prepareSegment(verticesPerAxis * verticesPerAxis, this.layoutVertexArray, this.indexArray, feature.sortKey);
const index = segment.vertexLength;
for (let y = 0; y < verticesPerAxis; y++) for (let x = 0; x < verticesPerAxis; x++) addCircleVertex(this.layoutVertexArray, vx, vy, extrudes[x], extrudes[y]);
for (let y = 0; y < verticesPerAxis - 1; y++) for (let x = 0; x < verticesPerAxis - 1; x++) {
const lowerIndex = index + y * verticesPerAxis + x;
const upperIndex = index + (y + 1) * verticesPerAxis + x;
this.indexArray.emplaceBack(lowerIndex, upperIndex + 1, lowerIndex + 1);
this.indexArray.emplaceBack(lowerIndex, upperIndex, upperIndex + 1);
}
segment.vertexLength += verticesPerAxis * verticesPerAxis;
segment.primitiveLength += (verticesPerAxis - 1) * (verticesPerAxis - 1) * 2;
}
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {
imagePositions: {},
canonical
});
}
};
register("CircleBucket", CircleBucket, { omit: ["layers"] });
//#endregion
//#region src/util/intersection_tests.ts
function polygonIntersectsPolygon(polygonA, polygonB) {
for (const point of polygonA) if (polygonContainsPoint(polygonB, point)) return true;
for (const point of polygonB) if (polygonContainsPoint(polygonA, point)) return true;
return lineIntersectsLine(polygonA, polygonB);
}
function polygonIntersectsBufferedPoint(polygon, point, radius) {
if (polygonContainsPoint(polygon, point)) return true;
return pointIntersectsBufferedLine(point, polygon, radius);
}
function polygonIntersectsMultiPolygon(polygon, multiPolygon) {
if (polygon.length === 1) return multiPolygonContainsPoint(multiPolygon, polygon[0]);
for (const ring of multiPolygon) for (const point of ring) if (polygonContainsPoint(polygon, point)) return true;
for (const point of polygon) if (multiPolygonContainsPoint(multiPolygon, point)) return true;
for (const ring of multiPolygon) if (lineIntersectsLine(polygon, ring)) return true;
return false;
}
function polygonIntersectsBufferedMultiLine(polygon, multiLine, radius) {
for (const line of multiLine) {
if (polygon.length >= 3) {
for (const point of line) if (polygonContainsPoint(polygon, point)) return true;
}
if (lineIntersectsBufferedLine(polygon, line, radius)) return true;
}
return false;
}
function lineIntersectsBufferedLine(lineA, lineB, radius) {
if (lineA.length > 1) {
if (lineIntersectsLine(lineA, lineB)) return true;
for (const point of lineB) if (pointIntersectsBufferedLine(point, lineA, radius)) return true;
}
for (const point of lineA) if (pointIntersectsBufferedLine(point, lineB, radius)) return true;
return false;
}
function lineIntersectsLine(lineA, lineB) {
if (lineA.length === 0 || lineB.length === 0) return false;
for (let i = 0; i < lineA.length - 1; i++) {
const a0 = lineA[i];
const a1 = lineA[i + 1];
for (let j = 0; j < lineB.length - 1; j++) {
const b0 = lineB[j];
const b1 = lineB[j + 1];
if (lineSegmentIntersectsLineSegment(a0, a1, b0, b1)) return true;
}
}
return false;
}
function lineSegmentIntersectsLineSegment(a0, a1, b0, b1) {
return isCounterClockwise(a0, b0, b1) !== isCounterClockwise(a1, b0, b1) && isCounterClockwise(a0, a1, b0) !== isCounterClockwise(a0, a1, b1);
}
function pointIntersectsBufferedLine(p, line, radius) {
const radiusSquared = radius * radius;
if (line.length === 1) return p.distSqr(line[0]) < radiusSquared;
for (let i = 1; i < line.length; i++) {
const v = line[i - 1], w = line[i];
if (distToSegmentSquared(p, v, w) < radiusSquared) return true;
}
return false;
}
function distToSegmentSquared(p, v, w) {
const l2 = v.distSqr(w);
if (l2 === 0) return p.distSqr(v);
const t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) return p.distSqr(v);
if (t > 1) return p.distSqr(w);
return p.distSqr(w.sub(v)._mult(t)._add(v));
}
function multiPolygonContainsPoint(rings, p) {
let c = false, ring, p1, p2;
for (const currentRing of rings) {
ring = currentRing;
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
p1 = ring[i];
p2 = ring[j];
if (p1.y > p.y !== p2.y > p.y && p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x) c = !c;
}
}
return c;
}
function polygonContainsPoint(ring, p) {
let c = false;
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
const p1 = ring[i];
const p2 = ring[j];
if (p1.y > p.y !== p2.y > p.y && p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x) c = !c;
}
return c;
}
function polygonIntersectsBox(ring, boxX1, boxY1, boxX2, boxY2) {
for (const p of ring) if (boxX1 <= p.x && boxY1 <= p.y && boxX2 >= p.x && boxY2 >= p.y) return true;
const corners = [
new Point(boxX1, boxY1),
new Point(boxX1, boxY2),
new Point(boxX2, boxY2),
new Point(boxX2, boxY1)
];
if (ring.length > 2) {
for (const corner of corners) if (polygonContainsPoint(ring, corner)) return true;
}
for (let i = 0; i < ring.length - 1; i++) {
const p1 = ring[i];
const p2 = ring[i + 1];
if (edgeIntersectsBox(p1, p2, corners)) return true;
}
return false;
}
function edgeIntersectsBox(e1, e2, corners) {
const tl = corners[0];
const br = corners[2];
if (e1.x < tl.x && e2.x < tl.x || e1.x > br.x && e2.x > br.x || e1.y < tl.y && e2.y < tl.y || e1.y > br.y && e2.y > br.y) return false;
const dir = isCounterClockwise(e1, e2, corners[0]);
return dir !== isCounterClockwise(e1, e2, corners[1]) || dir !== isCounterClockwise(e1, e2, corners[2]) || dir !== isCounterClockwise(e1, e2, corners[3]);
}
//#endregion
//#region src/style/query_utils.ts
function getMaximumPaintValue(property, layer, bucket) {
const value = layer.paint.get(property).value;
if (value.kind === "constant") return value.value;
else return bucket.programConfigurations.get(layer.id).getMaxValue(property);
}
function translateDistance(translate) {
return Math.sqrt(translate[0] * translate[0] + translate[1] * translate[1]);
}
/**
* @internal
* Translates a geometry by a certain pixels in tile coordinates
* @param queryGeometry - The geometry to translate in tile coordinates
* @param translate - The translation in pixels
* @param translateAnchor - The anchor of the translation
* @param bearing - The bearing of the map
* @param pixelsToTileUnits - The scale factor from pixels to tile units
* @returns the translated geometry in tile coordinates
*/
function translate(queryGeometry, translate, translateAnchor, bearing, pixelsToTileUnits) {
if (!translate[0] && !translate[1]) return queryGeometry;
const pt = Point.convert(translate)._mult(pixelsToTileUnits);
if (translateAnchor === "viewport") pt._rotate(-bearing);
const translated = [];
for (const point of queryGeometry) translated.push(point.sub(pt));
return translated;
}
/**
* Filter out consecutive duplicate points from a line
*/
function _stripDuplicates(ring) {
const filteredRing = [];
for (let index = 0; index < ring.length; index++) {
const point = ring[index];
const prevPoint = filteredRing.at(-1);
if (index === 0 || prevPoint && !point.equals(prevPoint)) filteredRing.push(point);
}
return filteredRing;
}
function offsetLine(rings, offset) {
const newRings = [];
for (const rawRing of rings) {
const ring = _stripDuplicates(rawRing);
const newRing = [];
for (let index = 0; index < ring.length; index++) {
const point = ring[index];
const prevPoint = ring[index - 1];
const nextPoint = ring[index + 1];
const unitNormalAB = index === 0 ? new Point(0, 0) : point.sub(prevPoint)._unit()._perp();
const unitNormalBC = index === ring.length - 1 ? new Point(0, 0) : nextPoint.sub(point)._unit()._perp();
const bisectorDir = unitNormalAB._add(unitNormalBC)._unit();
const cosHalfAngle = bisectorDir.x * unitNormalBC.x + bisectorDir.y * unitNormalBC.y;
if (cosHalfAngle !== 0) bisectorDir._mult(1 / cosHalfAngle);
newRing.push(bisectorDir._mult(offset)._add(point));
}
newRings.push(newRing);
}
return newRings;
}
function intersectionTestMapMap({ queryGeometry, size }, point) {
return polygonIntersectsBufferedPoint(queryGeometry, point, size);
}
function intersectionTestMapViewport({ queryGeometry, size, transform, unwrappedTileID, getElevation }, point) {
return polygonIntersectsBufferedPoint(queryGeometry, point, size * (transform.projectTileCoordinates(point.x, point.y, unwrappedTileID, getElevation).signedDistanceFromCamera / transform.cameraToCenterDistance));
}
function intersectionTestViewportMap({ queryGeometry, size, transform, unwrappedTileID, getElevation }, point) {
const w = transform.projectTileCoordinates(point.x, point.y, unwrappedTileID, getElevation).signedDistanceFromCamera;
const adjustedSize = size * (transform.cameraToCenterDistance / w);
return polygonIntersectsBufferedPoint(queryGeometry, projectPoint(point, transform, unwrappedTileID, getElevation), adjustedSize);
}
function intersectionTestViewportViewport({ queryGeometry, size, transform, unwrappedTileID, getElevation }, point) {
return polygonIntersectsBufferedPoint(queryGeometry, projectPoint(point, transform, unwrappedTileID, getElevation), size);
}
function circleIntersection({ queryGeometry, size, transform, unwrappedTileID, getElevation, pitchAlignment = "map", pitchScale = "map" }, geometry) {
const intersectionTest = pitchAlignment === "map" ? pitchScale === "map" ? intersectionTestMapMap : intersectionTestMapViewport : pitchScale === "map" ? intersectionTestViewportMap : intersectionTestViewportViewport;
const param = {
queryGeometry,
size,
transform,
unwrappedTileID,
getElevation
};
for (const ring of geometry) for (const point of ring) if (intersectionTest(param, point)) return true;
return false;
}
function projectPoint(tilePoint, transform, unwrappedTileID, getElevation) {
const clipPoint = transform.projectTileCoordinates(tilePoint.x, tilePoint.y, unwrappedTileID, getElevation).point;
return new Point((clipPoint.x * .5 + .5) * transform.width, (-clipPoint.y * .5 + .5) * transform.height);
}
function projectQueryGeometry$1(queryGeometry, transform, unwrappedTileID, getElevation) {
return queryGeometry.map((p) => {
return projectPoint(p, transform, unwrappedTileID, getElevation);
});
}
//#endregion
//#region src/style/style_layer/circle_style_layer_properties.g.ts
let layout$6;
const getLayout$4 = () => layout$6 = layout$6 || new Properties({ "circle-sort-key": new DataDrivenProperty(latest["layout_circle"]["circle-sort-key"], "circle-sort-key") });
let paint$8;
const getPaint$8 = () => paint$8 = paint$8 || new Properties({
"circle-radius": new DataDrivenProperty(latest["paint_circle"]["circle-radius"], "circle-radius"),
"circle-color": new DataDrivenProperty(latest["paint_circle"]["circle-color"], "circle-color"),
"circle-blur": new DataDrivenProperty(latest["paint_circle"]["circle-blur"], "circle-blur"),
"circle-opacity": new DataDrivenProperty(latest["paint_circle"]["circle-opacity"], "circle-opacity"),
"circle-translate": new DataConstantProperty(latest["paint_circle"]["circle-translate"], "circle-translate"),
"circle-translate-anchor": new DataConstantProperty(latest["paint_circle"]["circle-translate-anchor"], "circle-translate-anchor"),
"circle-pitch-scale": new DataConstantProperty(latest["paint_circle"]["circle-pitch-scale"], "circle-pitch-scale"),
"circle-pitch-alignment": new DataConstantProperty(latest["paint_circle"]["circle-pitch-alignment"], "circle-pitch-alignment"),
"circle-stroke-width": new DataDrivenProperty(latest["paint_circle"]["circle-stroke-width"], "circle-stroke-width"),
"circle-stroke-color": new DataDrivenProperty(latest["paint_circle"]["circle-stroke-color"], "circle-stroke-color"),
"circle-stroke-opacity": new DataDrivenProperty(latest["paint_circle"]["circle-stroke-opacity"], "circle-stroke-opacity")
});
var circle_style_layer_properties_g_default = {
get paint() {
return getPaint$8();
},
get layout() {
return getLayout$4();
}
};
//#endregion
//#region src/style/style_layer/circle_style_layer.ts
const isCircleStyleLayer = (layer) => layer.type === "circle";
/**
* A style layer that defines a circle
*/
var CircleStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, circle_style_layer_properties_g_default, globalState);
}
createBucket(parameters) {
return new CircleBucket(parameters);
}
queryRadius(bucket) {
const circleBucket = bucket;
return getMaximumPaintValue("circle-radius", this, circleBucket) + getMaximumPaintValue("circle-stroke-width", this, circleBucket) + translateDistance(this.paint.get("circle-translate"));
}
queryIntersectsFeature({ queryGeometry, feature, featureState, geometry, transform, pixelsToTileUnits, unwrappedTileID, getElevation }) {
const translatedPolygon = translate(queryGeometry, this.paint.get("circle-translate"), this.paint.get("circle-translate-anchor"), -transform.bearingInRadians, pixelsToTileUnits);
const size = this.paint.get("circle-radius").evaluate(feature, featureState) + this.paint.get("circle-stroke-width").evaluate(feature, featureState);
const pitchScale = this.paint.get("circle-pitch-scale");
const pitchAlignment = this.paint.get("circle-pitch-alignment");
let transformedPolygon;
let transformedSize;
if (pitchAlignment === "map") {
transformedPolygon = translatedPolygon;
transformedSize = size * pixelsToTileUnits;
} else {
transformedPolygon = projectQueryGeometry$1(translatedPolygon, transform, unwrappedTileID, getElevation);
transformedSize = size;
}
return circleIntersection({
queryGeometry: transformedPolygon,
size: transformedSize,
transform,
unwrappedTileID,
getElevation,
pitchAlignment,
pitchScale
}, geometry);
}
};
//#endregion
//#region src/data/bucket/heatmap_bucket.ts
var HeatmapBucket = class extends CircleBucket {};
register("HeatmapBucket", HeatmapBucket, { omit: ["layers"] });
//#endregion
//#region src/style/style_layer/heatmap_style_layer_properties.g.ts
let paint$7;
const getPaint$7 = () => paint$7 = paint$7 || new Properties({
"heatmap-radius": new DataDrivenProperty(latest["paint_heatmap"]["heatmap-radius"], "heatmap-radius"),
"heatmap-weight": new DataDrivenProperty(latest["paint_heatmap"]["heatmap-weight"], "heatmap-weight"),
"heatmap-intensity": new DataConstantProperty(latest["paint_heatmap"]["heatmap-intensity"], "heatmap-intensity"),
"heatmap-color": new ColorRampProperty(latest["paint_heatmap"]["heatmap-color"], "heatmap-color"),
"heatmap-opacity": new DataConstantProperty(latest["paint_heatmap"]["heatmap-opacity"], "heatmap-opacity")
});
var heatmap_style_layer_properties_g_default = { get paint() {
return getPaint$7();
} };
//#endregion
//#region src/util/image.ts
function createImage(image, { width, height }, channels, data) {
if (!data) data = new Uint8Array(width * height * channels);
else if (data instanceof Uint8ClampedArray) data = new Uint8Array(data.buffer);
else if (data.length !== width * height * channels) throw new RangeError(`mismatched image size. expected: ${data.length} but got: ${width * height * channels}`);
image.width = width;
image.height = height;
image.data = data;
return image;
}
function resizeImage(image, { width, height }, channels) {
if (width === image.width && height === image.height) return;
const newImage = createImage({}, {
width,
height
}, channels);
copyImage(image, newImage, {
x: 0,
y: 0
}, {
x: 0,
y: 0
}, {
width: Math.min(image.width, width),
height: Math.min(image.height, height)
}, channels);
image.width = width;
image.height = height;
image.data = newImage.data;
}
function copyImage(srcImg, dstImg, srcPt, dstPt, size, channels) {
if (size.width === 0 || size.height === 0) return dstImg;
if (size.width > srcImg.width || size.height > srcImg.height || srcPt.x > srcImg.width - size.width || srcPt.y > srcImg.height - size.height) throw new RangeError("out of range source coordinates for image copy");
if (size.width > dstImg.width || size.height > dstImg.height || dstPt.x > dstImg.width - size.width || dstPt.y > dstImg.height - size.height) throw new RangeError("out of range destination coordinates for image copy");
const srcData = srcImg.data;
const dstData = dstImg.data;
if (srcData === dstData) throw new Error("srcData equals dstData, so image is already copied");
for (let y = 0; y < size.height; y++) {
const srcOffset = ((srcPt.y + y) * srcImg.width + srcPt.x) * channels;
const dstOffset = ((dstPt.y + y) * dstImg.width + dstPt.x) * channels;
for (let i = 0; i < size.width * channels; i++) dstData[dstOffset + i] = srcData[srcOffset + i];
}
return dstImg;
}
/**
* An image with alpha color value
*/
var AlphaImage = class AlphaImage {
constructor(size, data) {
createImage(this, size, 1, data);
}
resize(size) {
resizeImage(this, size, 1);
}
clone() {
return new AlphaImage({
width: this.width,
height: this.height
}, new Uint8Array(this.data));
}
static copy(srcImg, dstImg, srcPt, dstPt, size) {
copyImage(srcImg, dstImg, srcPt, dstPt, size, 1);
}
};
/**
* An object to store image data not premultiplied, because ImageData is not premultiplied.
* Premultiplication is applied in JS before uploading to a texture.
*/
var RGBAImage = class RGBAImage {
constructor(size, data) {
createImage(this, size, 4, data);
}
resize(size) {
resizeImage(this, size, 4);
}
replace(data, copy) {
if (copy) this.data.set(data);
else if (data instanceof Uint8ClampedArray) this.data = new Uint8Array(data.buffer);
else this.data = data;
}
clone() {
return new RGBAImage({
width: this.width,
height: this.height
}, new Uint8Array(this.data));
}
static copy(srcImg, dstImg, srcPt, dstPt, size) {
copyImage(srcImg, dstImg, srcPt, dstPt, size, 4);
}
setPixel(row, col, value) {
const rLocation = (row * this.width + col) * 4;
this.data[rLocation + 0] = Math.round(value.r * 255 / value.a);
this.data[rLocation + 1] = Math.round(value.g * 255 / value.a);
this.data[rLocation + 2] = Math.round(value.b * 255 / value.a);
this.data[rLocation + 3] = Math.round(value.a * 255);
}
};
/** Returns a copy of RGBA data with premultiplied alpha. */
function premultiplyAlpha(data) {
const out = new Uint8Array(data.length);
for (let i = 0; i < data.length; i += 4) {
const a = data[i + 3];
out[i + 0] = Math.round(data[i + 0] * a / 255);
out[i + 1] = Math.round(data[i + 1] * a / 255);
out[i + 2] = Math.round(data[i + 2] * a / 255);
out[i + 3] = a;
}
return out;
}
register("AlphaImage", AlphaImage);
register("RGBAImage", RGBAImage);
//#endregion
//#region src/util/color_ramp.ts
/**
* Given an expression that should evaluate to a color ramp,
* return a RGBA image representing that ramp expression.
*/
function renderColorRamp(params) {
const evaluationGlobals = {};
const width = params.resolution || 256;
const height = params.clips ? params.clips.length : 1;
const image = params.image || new RGBAImage({
width,
height
});
if (!isPowerOfTwo(width)) throw new Error(`width is not a power of 2 - ${width}`);
const renderPixel = (stride, index, progress) => {
evaluationGlobals[params.evaluationKey] = progress;
const pxColor = params.expression.evaluate(evaluationGlobals);
image.setPixel(stride / 4 / width, index / 4, pxColor);
};
if (!params.clips) for (let i = 0, j = 0; i < width; i++, j += 4) {
const progress = i / (width - 1);
renderPixel(0, j, progress);
}
else for (let clip = 0, stride = 0; clip < height; ++clip, stride += width * 4) for (let i = 0, j = 0; i < width; i++, j += 4) {
const progress = i / (width - 1);
const { start, end } = params.clips[clip];
const evaluationProgress = start * (1 - progress) + end * progress;
renderPixel(stride, j, evaluationProgress);
}
return image;
}
//#endregion
//#region src/style/style_layer/heatmap_style_layer.ts
const HEATMAP_FULL_RENDER_FBO_KEY = "big-fb";
const isHeatmapStyleLayer = (layer) => layer.type === "heatmap";
/**
* A style layer that defines a heatmap
*/
var HeatmapStyleLayer = class extends StyleLayer {
createBucket(options) {
return new HeatmapBucket(options);
}
constructor(layer, globalState) {
super(layer, heatmap_style_layer_properties_g_default, globalState);
this.heatmapFbos = /* @__PURE__ */ new Map();
this._updateColorRamp();
}
_handleSpecialPaintPropertyUpdate(name) {
if (name === "heatmap-color") this._updateColorRamp();
}
_updateColorRamp() {
const expression = this._transitionablePaint._values["heatmap-color"].value.expression;
this.colorRamp = renderColorRamp({
expression,
evaluationKey: "heatmapDensity",
image: this.colorRamp
});
this.colorRampTexture = null;
}
resize() {
if (this.heatmapFbos.has("big-fb")) this.heatmapFbos.delete(HEATMAP_FULL_RENDER_FBO_KEY);
}
queryRadius(bucket) {
return getMaximumPaintValue("heatmap-radius", this, bucket);
}
queryIntersectsFeature({ queryGeometry, feature, featureState, geometry, transform, pixelsToTileUnits, unwrappedTileID, getElevation }) {
return circleIntersection({
queryGeometry,
size: this.paint.get("heatmap-radius").evaluate(feature, featureState) * pixelsToTileUnits,
transform,
unwrappedTileID,
getElevation
}, geometry);
}
hasOffscreenPass() {
return this.paint.get("heatmap-opacity") !== 0 && !this.isHidden();
}
};
//#endregion
//#region src/style/style_layer/hillshade_style_layer_properties.g.ts
let paint$6;
const getPaint$6 = () => paint$6 = paint$6 || new Properties({
"hillshade-illumination-direction": new DataConstantProperty(latest["paint_hillshade"]["hillshade-illumination-direction"], "hillshade-illumination-direction"),
"hillshade-illumination-altitude": new DataConstantProperty(latest["paint_hillshade"]["hillshade-illumination-altitude"], "hillshade-illumination-altitude"),
"hillshade-illumination-anchor": new DataConstantProperty(latest["paint_hillshade"]["hillshade-illumination-anchor"], "hillshade-illumination-anchor"),
"hillshade-exaggeration": new DataConstantProperty(latest["paint_hillshade"]["hillshade-exaggeration"], "hillshade-exaggeration"),
"hillshade-shadow-color": new DataConstantProperty(latest["paint_hillshade"]["hillshade-shadow-color"], "hillshade-shadow-color"),
"hillshade-highlight-color": new DataConstantProperty(latest["paint_hillshade"]["hillshade-highlight-color"], "hillshade-highlight-color"),
"hillshade-accent-color": new DataConstantProperty(latest["paint_hillshade"]["hillshade-accent-color"], "hillshade-accent-color"),
"hillshade-method": new DataConstantProperty(latest["paint_hillshade"]["hillshade-method"], "hillshade-method"),
"resampling": new DataConstantProperty(latest["paint_hillshade"]["resampling"], "resampling")
});
var hillshade_style_layer_properties_g_default = { get paint() {
return getPaint$6();
} };
//#endregion
//#region src/style/style_layer/hillshade_style_layer.ts
const isHillshadeStyleLayer = (layer) => layer.type === "hillshade";
var HillshadeStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, hillshade_style_layer_properties_g_default, globalState);
this.recalculate({
zoom: 0,
zoomHistory: {}
}, void 0);
}
getIlluminationProperties() {
let direction = this.paint.get("hillshade-illumination-direction").values;
let altitude = this.paint.get("hillshade-illumination-altitude").values;
let highlightColor = this.paint.get("hillshade-highlight-color").values;
let shadowColor = this.paint.get("hillshade-shadow-color").values;
const numIlluminationSources = Math.max(direction.length, altitude.length, highlightColor.length, shadowColor.length);
direction = direction.concat(Array(numIlluminationSources - direction.length).fill(direction.at(-1)));
altitude = altitude.concat(Array(numIlluminationSources - altitude.length).fill(altitude.at(-1)));
highlightColor = highlightColor.concat(Array(numIlluminationSources - highlightColor.length).fill(highlightColor.at(-1)));
shadowColor = shadowColor.concat(Array(numIlluminationSources - shadowColor.length).fill(shadowColor.at(-1)));
const altitudeRadians = altitude.map(degreesToRadians);
return {
directionRadians: direction.map(degreesToRadians),
altitudeRadians,
shadowColor,
highlightColor
};
}
hasOffscreenPass() {
return this.paint.get("hillshade-exaggeration") !== 0 && !this.isHidden();
}
};
//#endregion
//#region src/style/style_layer/color_relief_style_layer_properties.g.ts
let paint$5;
const getPaint$5 = () => paint$5 = paint$5 || new Properties({
"color-relief-opacity": new DataConstantProperty(latest["paint_color-relief"]["color-relief-opacity"], "color-relief-opacity"),
"color-relief-color": new ColorRampProperty(latest["paint_color-relief"]["color-relief-color"], "color-relief-color"),
"resampling": new DataConstantProperty(latest["paint_color-relief"]["resampling"], "resampling")
});
var color_relief_style_layer_properties_g_default = { get paint() {
return getPaint$5();
} };
//#endregion
//#region src/webgl/texture.ts
function hasDataProperty(image) {
return "data" in image;
}
/**
* @internal
* A `Texture` GL related object
*/
var Texture = class {
constructor(context, image, format, options) {
this.context = context;
this.format = format;
this.texture = context.gl.createTexture();
this._ownedHandle = this.texture;
this.update(image, options);
}
update(image, options, position) {
const { width, height } = image;
const resize = (this.size?.[0] !== width || this.size[1] !== height) && !position;
const { context } = this;
const { gl } = context;
this.useMipmap = Boolean(options?.useMipmap);
if (resize && this.size && this.format === gl.RGBA) {
gl.deleteTexture(this.texture);
this.texture = gl.createTexture();
this._ownedHandle = this.texture;
this.filter = void 0;
this.wrap = void 0;
}
gl.bindTexture(gl.TEXTURE_2D, this.texture);
context.pixelStoreUnpackFlipY.set(false);
context.pixelStoreUnpack.set(1);
const wantPremultiply = this.format === gl.RGBA && options?.premultiply !== false;
if (resize) {
this.size = [width, height];
if (this.format === gl.RGBA && width > 0 && height > 0) {
const mipLevels = this.useMipmap ? Math.floor(Math.log2(Math.max(width, height))) + 1 : 1;
gl.texStorage2D(gl.TEXTURE_2D, mipLevels, gl.RGBA8, width, height);
if (hasDataProperty(image)) {
context.pixelStoreUnpackPremultiplyAlpha.set(false);
let { data } = image;
if (wantPremultiply && data) data = premultiplyAlpha(data);
if (data) gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
} else {
context.pixelStoreUnpackPremultiplyAlpha.set(wantPremultiply);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
}
} else if (hasDataProperty(image)) {
context.pixelStoreUnpackPremultiplyAlpha.set(false);
this._uploadRawData(image, wantPremultiply, width, height, gl);
} else {
context.pixelStoreUnpackPremultiplyAlpha.set(wantPremultiply);
this._uploadDomImage(image, gl);
}
} else {
const { x, y } = position || {
x: 0,
y: 0
};
if (hasDataProperty(image)) {
context.pixelStoreUnpackPremultiplyAlpha.set(false);
this._updateRawData(image, wantPremultiply, x, y, width, height, gl);
} else {
context.pixelStoreUnpackPremultiplyAlpha.set(wantPremultiply);
this._updateDomImage(image, x, y, gl);
}
}
if (this.useMipmap) gl.generateMipmap(gl.TEXTURE_2D);
context.pixelStoreUnpackFlipY.setDefault();
context.pixelStoreUnpack.setDefault();
context.pixelStoreUnpackPremultiplyAlpha.setDefault();
}
_uploadDomImage(image, gl) {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, gl.UNSIGNED_BYTE, image);
}
_uploadRawData(image, wantPremultiply, width, height, gl) {
let { data } = image;
if (wantPremultiply && data) data = premultiplyAlpha(data);
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, gl.UNSIGNED_BYTE, data);
}
_updateDomImage(image, x, y, gl) {
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, image);
}
_updateRawData(image, wantPremultiply, x, y, width, height, gl) {
let { data } = image;
if (wantPremultiply && data) data = premultiplyAlpha(data);
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
}
bind(filter, wrap, minFilter) {
const { context } = this;
const { gl } = context;
if (this.texture !== this._ownedHandle) this.texture = this._ownedHandle;
gl.bindTexture(gl.TEXTURE_2D, this.texture);
if (minFilter === gl.LINEAR_MIPMAP_NEAREST && !this.useMipmap) minFilter = gl.LINEAR;
if (filter !== this.filter) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter || filter);
this.filter = filter;
}
if (wrap !== this.wrap) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
this.wrap = wrap;
}
}
destroy() {
const { gl } = this.context;
gl.deleteTexture(this.texture);
this.texture = null;
this._ownedHandle = null;
}
};
//#endregion
//#region src/data/dem_data.ts
/**
* DEMData is a data structure for decoding, backfilling, and storing elevation data for processing in the hillshade shaders
* data can be populated either from a png raw image tile or from serialized data sent back from a worker. When data is initially
* loaded from a image tile, we decode the pixel values using the appropriate decoding formula, but we store the
* elevation data as an Int32 value. we add 65536 (2^16) to eliminate negative values and enable the use of
* integer overflow when creating the texture used in the hillshadePrepare step.
*
* DEMData also handles the backfilling of data from a tile's neighboring tiles. This is necessary because we use a pixel's 8
* surrounding pixel values to compute the slope at that pixel, and we cannot accurately calculate the slope at pixels on a
* tile's edge without backfilling from neighboring tiles.
*/
var DEMData = class DEMData {
static {
this.byteViewCache = /* @__PURE__ */ new WeakMap();
}
/**
* Constructs a `DEMData` object
* @param uid - the tile's unique id
* @param data - RGBAImage data has uniform 1px padding on all sides: square tile edge size defines stride
// and dim is calculated as stride - 2.
* @param encoding - the encoding type of the data
* @param redFactor - the red channel factor used to unpack the data, used for `custom` encoding only
* @param greenFactor - the green channel factor used to unpack the data, used for `custom` encoding only
* @param blueFactor - the blue channel factor used to unpack the data, used for `custom` encoding only
* @param baseShift - the base shift used to unpack the data, used for `custom` encoding only
*/
constructor(uid, data, encoding, redFactor = 1, greenFactor = 1, blueFactor = 1, baseShift = 0) {
this.uid = uid;
if (data.height !== data.width) throw new RangeError("DEM tiles must be square");
if (encoding && ![
"mapbox",
"terrarium",
"custom"
].includes(encoding)) {
warnOnce(`"${encoding}" is not a valid encoding type. Valid types include "mapbox", "terrarium" and "custom".`);
return;
}
this.stride = data.height;
const dim = this.dim = data.height - 2;
this.data = new Uint32Array(data.data.buffer);
DEMData.byteViewCache.set(this, new Uint8Array(this.data.buffer));
switch (encoding) {
case "terrarium":
this.redFactor = 256;
this.greenFactor = 1;
this.blueFactor = 1 / 256;
this.baseShift = 32768;
break;
case "custom":
this.redFactor = redFactor;
this.greenFactor = greenFactor;
this.blueFactor = blueFactor;
this.baseShift = baseShift;
break;
default:
this.redFactor = 6553.6;
this.greenFactor = 25.6;
this.blueFactor = .1;
this.baseShift = 1e4;
}
for (let x = 0; x < dim; x++) {
this.data[this._idx(-1, x)] = this.data[this._idx(0, x)];
this.data[this._idx(dim, x)] = this.data[this._idx(dim - 1, x)];
this.data[this._idx(x, -1)] = this.data[this._idx(x, 0)];
this.data[this._idx(x, dim)] = this.data[this._idx(x, dim - 1)];
}
this.data[this._idx(-1, -1)] = this.data[this._idx(0, 0)];
this.data[this._idx(dim, -1)] = this.data[this._idx(dim - 1, 0)];
this.data[this._idx(-1, dim)] = this.data[this._idx(0, dim - 1)];
this.data[this._idx(dim, dim)] = this.data[this._idx(dim - 1, dim - 1)];
const pixels = this._getByteView();
this.min = Number.MAX_SAFE_INTEGER;
this.max = Number.MIN_SAFE_INTEGER;
for (let x = 0; x < dim; x++) for (let y = 0; y < dim; y++) {
const index = this._idx(x, y) * 4;
const ele = this._unpackAtIndex(pixels, index);
if (ele > this.max) this.max = ele;
if (ele < this.min) this.min = ele;
}
}
get(x, y) {
const pixels = this._getByteView();
const index = this._idx(x, y) * 4;
return this._unpackAtIndex(pixels, index);
}
sampleBilinear(x, y) {
const cx = Math.floor(x);
const cy = Math.floor(y);
if (cx < -1 || cx >= this.dim || cy < -1 || cy >= this.dim) throw new RangeError(`Out of range source coordinates for DEM data. x: ${x}, y: ${y}, dim: ${this.dim}`);
const pixels = this._getByteView();
const index = ((cy + 1) * this.stride + cx + 1) * 4;
const strideByteWidth = this.stride * 4;
const tx = x - cx;
const ty = y - cy;
const z00 = this._unpackAtIndex(pixels, index);
const z10 = this._unpackAtIndex(pixels, index + 4);
const z01 = this._unpackAtIndex(pixels, index + strideByteWidth);
const z11 = this._unpackAtIndex(pixels, index + strideByteWidth + 4);
return z00 * (1 - tx) * (1 - ty) + z10 * tx * (1 - ty) + z01 * (1 - tx) * ty + z11 * tx * ty;
}
getUnpackVector() {
return [
this.redFactor,
this.greenFactor,
this.blueFactor,
this.baseShift
];
}
_idx(x, y) {
if (x < -1 || x >= this.dim + 1 || y < -1 || y >= this.dim + 1) throw new RangeError(`Out of range source coordinates for DEM data. x: ${x}, y: ${y}, dim: ${this.dim}`);
return (y + 1) * this.stride + (x + 1);
}
unpack(r, g, b) {
return r * this.redFactor + g * this.greenFactor + b * this.blueFactor - this.baseShift;
}
pack(v) {
return packDEMData(v, this.getUnpackVector());
}
getPixels() {
return new RGBAImage({
width: this.stride,
height: this.stride
}, this._getByteView());
}
backfillBorder(borderTile, dx, dy) {
if (this.dim !== borderTile.dim) throw new Error("dem dimension mismatch");
let xMin = dx * this.dim, xMax = dx * this.dim + this.dim, yMin = dy * this.dim, yMax = dy * this.dim + this.dim;
switch (dx) {
case -1:
xMin = xMax - 1;
break;
case 1: xMax = xMin + 1;
}
switch (dy) {
case -1:
yMin = yMax - 1;
break;
case 1: yMax = yMin + 1;
}
const ox = -dx * this.dim;
const oy = -dy * this.dim;
for (let y = yMin; y < yMax; y++) for (let x = xMin; x < xMax; x++) this.data[this._idx(x, y)] = borderTile.data[this._idx(x + ox, y + oy)];
}
_getByteView() {
let byteView = DEMData.byteViewCache.get(this);
if (byteView?.buffer !== this.data.buffer) {
byteView = new Uint8Array(this.data.buffer);
DEMData.byteViewCache.set(this, byteView);
}
return byteView;
}
_unpackAtIndex(pixels, index) {
return this.unpack(pixels[index], pixels[index + 1], pixels[index + 2]);
}
};
function packDEMData(v, unpackVector) {
const redFactor = unpackVector[0];
const greenFactor = unpackVector[1];
const blueFactor = unpackVector[2];
const baseShift = unpackVector[3];
const minScale = Math.min(redFactor, greenFactor, blueFactor);
const vScaled = Math.round((v + baseShift) / minScale);
return {
r: Math.floor(vScaled * minScale / redFactor) % 256,
g: Math.floor(vScaled * minScale / greenFactor) % 256,
b: Math.floor(vScaled * minScale / blueFactor) % 256
};
}
register("DEMData", DEMData);
//#endregion
//#region src/style/style_layer/color_relief_style_layer.ts
const isColorReliefStyleLayer = (layer) => layer.type === "color-relief";
var ColorReliefStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, color_relief_style_layer_properties_g_default, globalState);
}
/**
* Create the color ramp, enforcing a maximum length for the vectors. This modifies the internal color ramp,
* so that the remapping is only performed once.
*
* @param maxLength - the maximum number of stops in the color ramp
*
* @return a `ColorRamp` object with no more than `maxLength` stops.
*
*/
_createColorRamp(maxLength) {
const colorRamp = {
elevationStops: [],
colorStops: []
};
const expression = this._transitionablePaint._values["color-relief-color"].value.expression;
if (expression instanceof ZoomConstantExpression && expression._styleExpression.expression instanceof Interpolate) {
this.colorRampExpression = expression;
const interpolater = expression._styleExpression.expression;
colorRamp.elevationStops = interpolater.labels;
colorRamp.colorStops = [];
for (const label of colorRamp.elevationStops) colorRamp.colorStops.push(interpolater.evaluate({ globals: { elevation: label } }));
}
if (colorRamp.elevationStops.length < 1) {
colorRamp.elevationStops = [0];
colorRamp.colorStops = [Color.transparent];
}
if (colorRamp.elevationStops.length < 2) {
colorRamp.elevationStops.push(colorRamp.elevationStops[0] + 1);
colorRamp.colorStops.push(colorRamp.colorStops[0]);
}
if (colorRamp.elevationStops.length <= maxLength) return colorRamp;
const remappedColorRamp = {
elevationStops: [],
colorStops: []
};
const remapStepSize = (colorRamp.elevationStops.length - 1) / (maxLength - 1);
for (let i = 0; i < colorRamp.elevationStops.length - .5; i += remapStepSize) {
remappedColorRamp.elevationStops.push(colorRamp.elevationStops[Math.round(i)]);
remappedColorRamp.colorStops.push(colorRamp.colorStops[Math.round(i)]);
}
warnOnce(`Too many colors in specification of ${this.id} color-relief layer, may not render properly. Max possible colors: ${maxLength}, provided: ${colorRamp.elevationStops.length}`);
return remappedColorRamp;
}
_colorRampChanged() {
return this.colorRampExpression != this._transitionablePaint._values["color-relief-color"].value.expression;
}
getColorRampTextures(context, maxLength, unpackVector) {
if (this.colorRampTextures && !this._colorRampChanged()) return this.colorRampTextures;
const colorRamp = this._createColorRamp(maxLength);
const colorImage = new RGBAImage({
width: colorRamp.colorStops.length,
height: 1
});
const elevationImage = new RGBAImage({
width: colorRamp.colorStops.length,
height: 1
});
for (let i = 0; i < colorRamp.elevationStops.length; i++) {
const elevationPacked = packDEMData(colorRamp.elevationStops[i], unpackVector);
elevationImage.setPixel(0, i, new Color(elevationPacked.r / 255, elevationPacked.g / 255, elevationPacked.b / 255, 1));
colorImage.setPixel(0, i, colorRamp.colorStops[i]);
}
this.colorRampTextures = {
elevationTexture: new Texture(context, elevationImage, context.gl.RGBA),
colorTexture: new Texture(context, colorImage, context.gl.RGBA)
};
return this.colorRampTextures;
}
hasOffscreenPass() {
return !this.isHidden() && !!this.colorRampTextures;
}
};
//#endregion
//#region src/data/bucket/fill_attributes.ts
const layout$5 = createLayout([{
name: "a_pos",
components: 2,
type: "Int16"
}], 4);
const members$3 = layout$5.members;
layout$5.size;
layout$5.alignment;
//#endregion
//#region src/data/bucket/pattern_bucket_features.ts
function hasPattern(type, layers, options) {
const patterns = options.patternDependencies;
let hasPattern = false;
for (const layer of layers) {
const patternProperty = layer.paint.get(`${type}-pattern`);
if (!patternProperty.isConstant()) hasPattern = true;
const constantPattern = patternProperty.constantOr(null);
if (constantPattern) {
hasPattern = true;
patterns[constantPattern.to] = true;
patterns[constantPattern.from] = true;
}
}
return hasPattern;
}
function addPatternDependencies(type, layers, patternFeature, parameters, options) {
const { zoom } = parameters;
const patterns = options.patternDependencies;
for (const layer of layers) {
const patternPropertyValue = layer.paint.get(`${type}-pattern`).value;
if (patternPropertyValue.kind !== "constant") {
let min = patternPropertyValue.evaluate({ zoom: zoom - 1 }, patternFeature, {}, options.availableImages);
let mid = patternPropertyValue.evaluate({ zoom }, patternFeature, {}, options.availableImages);
let max = patternPropertyValue.evaluate({ zoom: zoom + 1 }, patternFeature, {}, options.availableImages);
min = min?.name ? min.name : min;
mid = mid?.name ? mid.name : mid;
max = max?.name ? max.name : max;
patterns[min] = true;
patterns[mid] = true;
patterns[max] = true;
patternFeature.patterns[layer.id] = {
min,
mid,
max
};
}
}
return patternFeature;
}
//#endregion
//#region node_modules/earcut/src/earcut.js
/**
* A vertex in a circular doubly linked list representing a polygon ring.
* `prev`/`next` are always linked (set immediately after {@link createNode}), so they're typed
* non-null; `prevZ`/`nextZ` are the z-order list links and are null at the ends.
*
* @typedef {object} Node
* @property {number} i vertex index in the coordinates array
* @property {number} x vertex x coordinate
* @property {number} y vertex y coordinate
* @property {Node} prev previous vertex node in the polygon ring
* @property {Node} next next vertex node in the polygon ring
* @property {number} z z-order curve value; doubles as the owning block index during eliminateHoles
* @property {Node | null} prevZ previous node in z-order
* @property {Node | null} nextZ next node in z-order
*/
/** @type {Set<Node>} */
const steiners = /* @__PURE__ */ new Set();
let filteredOut = false;
/**
* Triangulate a polygon given as a flat array of vertex coordinates.
*
* @param {ArrayLike<number>} data flat array of vertex coordinates
* @param {ArrayLike<number> | null} [holeIndices] indices (in vertices, not coordinates) where each hole ring starts
* @param {number} [dim=2] number of coordinates per vertex in `data`
* @returns {number[]} triangles as triplets of vertex indices into `data`
* @example earcut([10,0, 0,50, 60,60, 70,10]); // [1,0,3, 3,2,1]
*/
function earcut(data, holeIndices, dim = 2) {
const hasHoles = holeIndices && holeIndices.length;
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
if (steiners.size) steiners.clear();
let outerNode = linkedList(data, 0, outerLen, dim, true);
/** @type {number[]} */
const triangles = [];
if (!outerNode || outerNode.next === outerNode.prev) return triangles;
let minX = 0, minY = 0, invSize = 0;
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
if (data.length > 80 * dim) {
minX = data[0];
minY = data[1];
let maxX = minX;
let maxY = minY;
for (let i = dim; i < outerLen; i += dim) {
const x = data[i];
const y = data[i + 1];
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
}
invSize = Math.max(maxX - minX, maxY - minY);
invSize = invSize !== 0 ? 32767 / invSize : 0;
}
earcutLinked(outerNode, triangles, minX, minY, invSize);
return triangles;
}
/** @param {ArrayLike<number>} data @param {number} start @param {number} end @param {number} dim @param {boolean} clockwise @returns {Node | null} */
function linkedList(data, start, end, dim, clockwise) {
/** @type {Node | null} */
let last = null;
if (clockwise === signedArea$1(data, start, end, dim) > 0) for (let i = start; i < end; i += dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
else for (let i = end - dim; i >= start; i -= dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
if (last && equals(last, last.next)) {
removeNode(last);
last = last.next;
}
return last;
}
/** @param {Node} start @param {Node} [end] @returns {Node} */
function filterPoints(start, end = start) {
const full = end === start;
let p = start, again;
do {
again = false;
if (p !== p.next && (steiners.size === 0 || !steiners.has(p)) && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
if (full || p === end) end = p.prev;
filteredOut = true;
removeNode(p);
p = p.prev;
again = true;
} else if (full || p !== end) {
p = p.next;
again = !full;
}
} while (again || p !== end);
return end;
}
/** @param {Node} ear @param {number[]} triangles @param {number} minX @param {number} minY @param {number} invSize */
function earcutLinked(ear, triangles, minX, minY, invSize) {
if (invSize) indexCurve(ear, minX, minY, invSize);
let stop = ear, cured = false;
while (ear.prev !== ear.next) {
const prev = ear.prev;
/** @type {Node} */
const next = ear.next;
if (area(prev, ear, next) < 0 && (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear))) {
triangles.push(prev.i, ear.i, next.i);
removeNode(ear);
ear = next;
stop = next;
continue;
}
ear = next;
if (ear === stop) {
filteredOut = false;
ear = filterPoints(ear);
if (filteredOut) {
stop = ear;
continue;
}
if (!cured) {
ear = cureLocalIntersections(ear, triangles);
stop = ear;
cured = true;
continue;
}
splitEarcut(ear, triangles, minX, minY, invSize);
break;
}
}
}
/** @param {Node} ear @returns {boolean} */
function isEar(ear) {
const a = ear.prev, b = ear, c = ear.next, ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y, x0 = Math.min(ax, bx, cx), y0 = Math.min(ay, by, cy), x1 = Math.max(ax, bx, cx), y1 = Math.max(ay, by, cy);
let p = c.next;
while (p !== a) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && !(ax === p.x && ay === p.y) && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
p = p.next;
}
return true;
}
/** @param {Node} ear @param {number} minX @param {number} minY @param {number} invSize @returns {boolean} */
function isEarHashed(ear, minX, minY, invSize) {
const a = ear.prev, b = ear, c = ear.next, ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y, x0 = Math.min(ax, bx, cx), y0 = Math.min(ay, by, cy), x1 = Math.max(ax, bx, cx), y1 = Math.max(ay, by, cy), minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
let p = ear.prevZ;
while (p && p.z >= minZ) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== c && !(ax === p.x && ay === p.y) && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
p = p.prevZ;
}
let n = ear.nextZ;
while (n && n.z <= maxZ) {
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== c && !(ax === n.x && ay === n.y) && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
n = n.nextZ;
}
return true;
}
/** @param {Node} start @param {number[]} triangles @returns {Node} */
function cureLocalIntersections(start, triangles) {
let p = start;
let cured = false;
do {
const a = p.prev, b = p.next.next;
if (intersects(a, p, p.next, b, false) && locallyInside(a, b) && locallyInside(b, a)) {
triangles.push(a.i, p.i, b.i);
removeNode(p);
removeNode(p.next);
p = start = b;
cured = true;
}
p = p.next;
} while (p !== start);
return cured ? filterPoints(p) : p;
}
/** @param {Node} start @param {number[]} triangles @param {number} minX @param {number} minY @param {number} invSize */
function splitEarcut(start, triangles, minX, minY, invSize) {
let a = start;
do {
let b = a.next.next;
while (b !== a.prev) {
if (a.i !== b.i && isValidDiagonal(a, b)) {
let c = splitPolygon(a, b);
a = filterPoints(a, a.next);
c = filterPoints(c, c.next);
earcutLinked(a, triangles, minX, minY, invSize);
earcutLinked(c, triangles, minX, minY, invSize);
return;
}
b = b.next;
}
a = a.next;
} while (a !== start);
}
let indexActive = false;
/** @param {ArrayLike<number>} data @param {ArrayLike<number>} holeIndices @param {Node} outerNode @param {number} dim @returns {Node} */
function eliminateHoles(data, holeIndices, outerNode, dim) {
const queue = [];
for (let i = 0, len = holeIndices.length; i < len; i++) {
const list = linkedList(data, holeIndices[i] * dim, i < len - 1 ? holeIndices[i + 1] * dim : data.length, dim, false);
if (list === list.next) steiners.add(list);
queue.push(getLeftmost(list));
}
queue.sort(compareXYSlope);
buildBlockIndex(data.length / dim, holeIndices.length);
indexSegment(outerNode, outerNode);
indexActive = true;
for (let i = 0; i < queue.length; i++) outerNode = eliminateHole(queue[i], outerNode);
indexActive = false;
return filterPoints(outerNode);
}
/** @param {Node} a @param {Node} b @returns {number} */
function compareXYSlope(a, b) {
return a.x - b.x || a.y - b.y || (a.next.y - a.y) / (a.next.x - a.x) - (b.next.y - b.y) / (b.next.x - b.x);
}
/** @param {Node} hole @param {Node} outerNode @returns {Node} */
function eliminateHole(hole, outerNode) {
const bridge = findHoleBridge(hole, outerNode);
if (!bridge) return outerNode;
const bridgeReverse = splitPolygon(bridge, hole);
const bridge2 = bridgeReverse.next;
indexSegment(bridge, bridge2.next);
filterPoints(bridgeReverse, bridgeReverse.next);
return filterPoints(bridge, bridge.next);
}
const K = 16;
let blockBBox = /* @__PURE__ */ new Float64Array(0);
let numBlocks = 0;
/** @type {Node[]} */
const blockHead = [];
/** @type {Node[]} */
const blockStop = [];
/** @param {number} maxNodes @param {number} numHoles */
function buildBlockIndex(maxNodes, numHoles) {
const maxBlocks = Math.ceil((maxNodes + 2 * numHoles) / K) + numHoles + 2;
if (blockBBox.length < maxBlocks * 4) blockBBox = new Float64Array(maxBlocks * 4);
numBlocks = 0;
}
/** @param {Node} head @param {Node} stop */
function indexSegment(head, stop) {
let p = head;
do {
const b = numBlocks++;
blockHead[b] = p;
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
let k = 0;
do {
const c = p.next;
p.z = b;
if (p.x < minX) minX = p.x;
if (p.x > maxX) maxX = p.x;
if (p.y < minY) minY = p.y;
if (p.y > maxY) maxY = p.y;
if (c.x < minX) minX = c.x;
if (c.x > maxX) maxX = c.x;
if (c.y < minY) minY = c.y;
if (c.y > maxY) maxY = c.y;
p = c;
} while (++k < K && p !== stop);
blockStop[b] = p;
const g = b * 4;
blockBBox[g] = minX;
blockBBox[g + 1] = minY;
blockBBox[g + 2] = maxX;
blockBBox[g + 3] = maxY;
} while (p !== stop);
}
/** @param {Node} head @param {Node} tail */
function growBlock(head, tail) {
const g = head.z * 4;
if (tail.x < blockBBox[g]) blockBBox[g] = tail.x;
if (tail.y < blockBBox[g + 1]) blockBBox[g + 1] = tail.y;
if (tail.x > blockBBox[g + 2]) blockBBox[g + 2] = tail.x;
if (tail.y > blockBBox[g + 3]) blockBBox[g + 3] = tail.y;
}
/** @param {number} b @returns {Node} */
function liveBlockStop(b) {
let stop = blockStop[b];
while (stop.prev.next !== stop) stop = stop.next;
blockStop[b] = stop;
return stop;
}
/** @param {number} b @returns {Node} */
function liveBlockHead(b) {
let head = blockHead[b];
while (head.prev.next !== head) head = head.next;
blockHead[b] = head;
return head;
}
/** @param {Node} hole @param {Node} outerNode @returns {Node | null} */
function findHoleBridge(hole, outerNode) {
let p = outerNode;
const hx = hole.x;
const hy = hole.y;
let qx = -Infinity;
/** @type {Node | undefined} */
let m;
if (equals(hole, p)) return p;
for (let b = 0, g = 0; b < numBlocks; b++, g += 4) {
if (hy < blockBBox[g + 1] || hy > blockBBox[g + 3] || blockBBox[g] > hx || blockBBox[g + 2] <= qx) continue;
const stop = liveBlockStop(b);
p = liveBlockHead(b);
do {
if (p.prev.next === p) {
if (equals(hole, p.next)) return p.next;
else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
if (x <= hx && x > qx) {
qx = x;
m = p.x < p.next.x ? p : p.next;
if (x === hx) return m;
}
}
}
p = p.next;
} while (p !== stop);
}
if (!m) return null;
const mx = m.x;
const my = m.y;
const tminY = Math.min(hy, my);
const tmaxY = Math.max(hy, my);
let tanMin = Infinity;
for (let b = 0, g = 0; b < numBlocks; b++, g += 4) {
if (blockBBox[g + 2] < mx || blockBBox[g] > hx || blockBBox[g + 3] < tminY || blockBBox[g + 1] > tmaxY) continue;
const stop = liveBlockStop(b);
p = liveBlockHead(b);
do {
if (p.prev.next === p && hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
const tan = Math.abs(hy - p.y) / (hx - p.x);
if ((locallyInside(p, hole) || p.y === hy && p.next.y === hy && p.next.x > hx) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
m = p;
tanMin = tan;
}
}
p = p.next;
} while (p !== stop);
}
return m;
}
/** @param {Node} m @param {Node} p @returns {boolean} */
function sectorContainsSector(m, p) {
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
}
/** @type {Node[]} */
const sortArr = [];
/** @type {Node[]} */
let sortBuf = [];
let zArr = /* @__PURE__ */ new Uint32Array(0);
let zBuf = /* @__PURE__ */ new Uint32Array(0);
const counts = /* @__PURE__ */ new Uint32Array(256);
/** @param {Node} start @param {number} minX @param {number} minY @param {number} invSize */
function indexCurve(start, minX, minY, invSize) {
let p = start;
let n = 0;
do {
p.z = zOrder(p.x, p.y, minX, minY, invSize);
sortArr[n++] = p;
p = p.next;
} while (p !== start);
sortNodes(n);
/** @type {Node | null} */
let prev = null;
for (let i = 0; i < n; i++) {
const node = sortArr[i];
node.prevZ = prev;
if (prev) prev.nextZ = node;
prev = node;
}
/** @type {Node} */ prev.nextZ = null;
}
/** @param {number} n */
function sortNodes(n) {
if (n <= 32) {
for (let i = 1; i < n; i++) {
const node = sortArr[i], z = node.z;
let j = i - 1;
while (j >= 0 && sortArr[j].z > z) {
sortArr[j + 1] = sortArr[j];
j--;
}
sortArr[j + 1] = node;
}
return;
}
if (zArr.length < n) {
zArr = new Uint32Array(n);
zBuf = new Uint32Array(n);
sortBuf = new Array(n);
}
for (let i = 0; i < n; i++) zArr[i] = sortArr[i].z;
radixPass(n, sortArr, zArr, sortBuf, zBuf, 0);
radixPass(n, sortBuf, zBuf, sortArr, zArr, 8);
radixPass(n, sortArr, zArr, sortBuf, zBuf, 16);
radixPass(n, sortBuf, zBuf, sortArr, zArr, 24);
}
/** @param {number} n @param {Node[]} src @param {Uint32Array} srcZ @param {Node[]} dst @param {Uint32Array} dstZ @param {number} shift */
function radixPass(n, src, srcZ, dst, dstZ, shift) {
counts.fill(0);
for (let i = 0; i < n; i++) counts[srcZ[i] >>> shift & 255]++;
let sum = 0;
for (let b = 0; b < 256; b++) {
const c = counts[b];
counts[b] = sum;
sum += c;
}
for (let i = 0; i < n; i++) {
const z = srcZ[i];
const pos = counts[z >>> shift & 255]++;
dst[pos] = src[i];
dstZ[pos] = z;
}
}
/** @param {number} x @param {number} y @param {number} minX @param {number} minY @param {number} invSize @returns {number} */
function zOrder(x, y, minX, minY, invSize) {
x = (x - minX) * invSize | 0;
y = (y - minY) * invSize | 0;
x = (x | x << 8) & 16711935;
x = (x | x << 4) & 252645135;
x = (x | x << 2) & 858993459;
x = (x | x << 1) & 1431655765;
y = (y | y << 8) & 16711935;
y = (y | y << 4) & 252645135;
y = (y | y << 2) & 858993459;
y = (y | y << 1) & 1431655765;
return x | y << 1;
}
/** @param {Node} start @returns {Node} */
function getLeftmost(start) {
let p = start, leftmost = start;
do {
if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
p = p.next;
} while (p !== start);
return leftmost;
}
/** @param {number} ax @param {number} ay @param {number} bx @param {number} by @param {number} cx @param {number} cy @param {number} px @param {number} py @returns {boolean} */
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
}
/** @param {Node} a @param {Node} b @returns {boolean} true when the diagonal is valid */
function isValidDiagonal(a, b) {
const zeroLength = equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0;
return a.next.i !== b.i && (zeroLength || locallyInside(a, b) && locallyInside(b, a) && (area(a.prev, a, b.prev) !== 0 || area(a, b.prev, b) !== 0)) && !intersectsPolygon(a, b) && (zeroLength || middleInside(a, b));
}
/** @param {Node} p @param {Node} q @param {Node} r @returns {number} */
function area(p, q, r) {
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
}
/** @param {Node} p1 @param {Node} p2 @returns {boolean} */
function equals(p1, p2) {
return p1.x === p2.x && p1.y === p2.y;
}
/** @param {Node} p1 @param {Node} q1 @param {Node} p2 @param {Node} q2 @param {boolean} [includeBoundary] @returns {boolean} */
function intersects(p1, q1, p2, q2, includeBoundary = true) {
const o1 = area(p1, q1, p2);
const o2 = area(p1, q1, q2);
const o3 = area(p2, q2, p1);
const o4 = area(p2, q2, q1);
if ((o1 > 0 && o2 < 0 || o1 < 0 && o2 > 0) && (o3 > 0 && o4 < 0 || o3 < 0 && o4 > 0)) return true;
if (!includeBoundary) return false;
if (o1 === 0 && onSegment(p1, p2, q1)) return true;
if (o2 === 0 && onSegment(p1, q2, q1)) return true;
if (o3 === 0 && onSegment(p2, p1, q2)) return true;
if (o4 === 0 && onSegment(p2, q1, q2)) return true;
return false;
}
/** @param {Node} p @param {Node} q @param {Node} r @returns {boolean} */
function onSegment(p, q, r) {
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
}
/** @param {Node} a @param {Node} b @returns {boolean} */
function intersectsPolygon(a, b) {
const minX = Math.min(a.x, b.x);
const maxX = Math.max(a.x, b.x);
const minY = Math.min(a.y, b.y);
const maxY = Math.max(a.y, b.y);
let p = a;
do {
const n = p.next;
if (p.x > maxX && n.x > maxX || p.x < minX && n.x < minX || p.y > maxY && n.y > maxY || p.y < minY && n.y < minY) {
p = n;
continue;
}
if (p.i !== a.i && n.i !== a.i && p.i !== b.i && n.i !== b.i && intersects(p, n, a, b)) return true;
p = n;
} while (p !== a);
return false;
}
/** @param {Node} a @param {Node} b @returns {boolean} */
function locallyInside(a, b) {
return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
}
/** @param {Node} a @param {Node} b @returns {boolean} */
function middleInside(a, b) {
let p = a;
let inside = false;
const px = (a.x + b.x) / 2;
const py = (a.y + b.y) / 2;
do {
const n = p.next;
if (p.y > py !== n.y > py && px < (n.x - p.x) * (py - p.y) / (n.y - p.y) + p.x) inside = !inside;
p = n;
} while (p !== a);
return inside;
}
/** @param {Node} a @param {Node} b @returns {Node} */
function splitPolygon(a, b) {
const a2 = createNode(a.i, a.x, a.y), b2 = createNode(b.i, b.x, b.y), an = a.next, bp = b.prev;
a.next = b;
b.prev = a;
a2.next = an;
an.prev = a2;
b2.next = a2;
a2.prev = b2;
bp.next = b2;
b2.prev = bp;
return b2;
}
/** @param {number} i @param {number} x @param {number} y @param {Node | null} last @returns {Node} */
function insertNode(i, x, y, last) {
const p = createNode(i, x, y);
if (!last) {
p.prev = p;
p.next = p;
} else {
p.next = last.next;
p.prev = last;
last.next.prev = p;
last.next = p;
}
return p;
}
/** @param {Node} p */
function removeNode(p) {
p.next.prev = p.prev;
p.prev.next = p.next;
if (p.prevZ) p.prevZ.nextZ = p.nextZ;
if (p.nextZ) p.nextZ.prevZ = p.prevZ;
if (indexActive) growBlock(p.prev, p.next);
}
/** @param {number} i @param {number} x @param {number} y @returns {Node} */
function createNode(i, x, y) {
return {
i,
x,
y,
prev: null,
next: null,
z: 0,
prevZ: null,
nextZ: null
};
}
/** @param {ArrayLike<number>} data @param {number} start @param {number} end @param {number} dim @returns {number} */
function signedArea$1(data, start, end, dim) {
let sum = 0;
for (let i = start, j = end - dim; i < end; i += dim) {
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
j = i;
}
return sum;
}
//#endregion
//#region src/render/subdivision_granularity_settings.ts
/**
* Controls how much subdivision happens for a given type of geometry at different zoom levels.
*/
var SubdivisionGranularityExpression = class {
constructor(baseZoomGranularity, minGranularity) {
if (minGranularity > baseZoomGranularity) throw new Error("Min granularity must not be greater than base granularity.");
this._baseZoomGranularity = baseZoomGranularity;
this._minGranularity = minGranularity;
}
getGranularityForZoomLevel(zoomLevel) {
const divisor = 1 << zoomLevel;
return Math.max(Math.floor(this._baseZoomGranularity / divisor), this._minGranularity, 1);
}
};
/**
* An object describing how much subdivision should be applied to different types of geometry at different zoom levels.
*/
var SubdivisionGranularitySetting = class SubdivisionGranularitySetting {
constructor(options) {
this.fill = options.fill;
this.line = options.line;
this.tile = options.tile;
this.stencil = options.stencil;
this.circle = options.circle;
}
static {
this.noSubdivision = new SubdivisionGranularitySetting({
fill: new SubdivisionGranularityExpression(0, 0),
line: new SubdivisionGranularityExpression(0, 0),
tile: new SubdivisionGranularityExpression(0, 0),
stencil: new SubdivisionGranularityExpression(0, 0),
circle: 1
});
}
};
//#endregion
//#region src/render/subdivision.ts
register("SubdivisionGranularityExpression", SubdivisionGranularityExpression);
register("SubdivisionGranularitySetting", SubdivisionGranularitySetting);
const NORTH_POLE_Y = -32768;
const SOUTH_POLE_Y = 32767;
var Subdivider = class {
constructor(granularity, canonical) {
this._vertexBuffer = [];
this._vertexDictionary = /* @__PURE__ */ new Map();
this._used = false;
this._granularity = granularity;
this._granularityCellSize = EXTENT$1 / granularity;
this._canonical = canonical;
}
_getKey(x, y) {
x = x + 32768;
y = y + 32768;
return x << 16 | y << 0;
}
/**
* Returns an index into the internal vertex buffer for a vertex at the given coordinates.
* If the internal vertex buffer contains no such vertex, then it is added.
*/
_vertexToIndex(x, y) {
if (x < -32768 || y < -32768 || x > 32767 || y > 32767) throw new Error("Vertex coordinates are out of signed 16 bit integer range.");
const xInt = Math.round(x) | 0;
const yInt = Math.round(y) | 0;
const key = this._getKey(xInt, yInt);
if (this._vertexDictionary.has(key)) return this._vertexDictionary.get(key);
const index = this._vertexBuffer.length / 2;
this._vertexDictionary.set(key, index);
this._vertexBuffer.push(xInt, yInt);
return index;
}
/**
* Subdivides a polygon by iterating over rows of granularity subdivision cells and splitting each row along vertical subdivision axes.
* @param inputIndices - Indices into the internal vertex buffer of the triangulated polygon (after running `earcut`).
* @returns Indices into the internal vertex buffer for triangles that are a subdivision of the input geometry.
*/
_subdivideTrianglesScanline(inputIndices) {
if (this._granularity < 2) return fixWindingOrder(this._vertexBuffer, inputIndices);
const finalIndices = [];
const numIndices = inputIndices.length;
for (let primitiveIndex = 0; primitiveIndex < numIndices; primitiveIndex += 3) {
const triangleIndices = [
inputIndices[primitiveIndex + 0],
inputIndices[primitiveIndex + 1],
inputIndices[primitiveIndex + 2]
];
const triangleVertices = [
this._vertexBuffer[inputIndices[primitiveIndex + 0] * 2 + 0],
this._vertexBuffer[inputIndices[primitiveIndex + 0] * 2 + 1],
this._vertexBuffer[inputIndices[primitiveIndex + 1] * 2 + 0],
this._vertexBuffer[inputIndices[primitiveIndex + 1] * 2 + 1],
this._vertexBuffer[inputIndices[primitiveIndex + 2] * 2 + 0],
this._vertexBuffer[inputIndices[primitiveIndex + 2] * 2 + 1]
];
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for (let i = 0; i < 3; i++) {
const vx = triangleVertices[i * 2];
const vy = triangleVertices[i * 2 + 1];
minX = Math.min(minX, vx);
maxX = Math.max(maxX, vx);
minY = Math.min(minY, vy);
maxY = Math.max(maxY, vy);
}
if (minX === maxX || minY === maxY) continue;
const cellXmin = Math.floor(minX / this._granularityCellSize);
const cellXmax = Math.ceil(maxX / this._granularityCellSize);
const cellYmin = Math.floor(minY / this._granularityCellSize);
const cellYmax = Math.ceil(maxY / this._granularityCellSize);
if (cellXmin === cellXmax && cellYmin === cellYmax) {
finalIndices.push(...triangleIndices);
continue;
}
for (let cellRow = cellYmin; cellRow < cellYmax; cellRow++) {
const ring = this._scanlineGenerateVertexRingForCellRow(cellRow, triangleVertices, triangleIndices);
scanlineTriangulateVertexRing(this._vertexBuffer, ring, finalIndices);
}
}
return finalIndices;
}
/**
* Takes a triangle and a cell row index, returns a subdivided vertex ring of the intersection of the triangle and the cell row.
* @param cellRow - Index of the cell row. A cell row of index `i` convert range from `i * granularityCellSize` to `(i + 1) * granularityCellSize`.
* @param triangleVertices - An array of 6 elements, contains flattened positions of the triangle's vertices: `[v0x, v0y, v1x, v1y, v2x, v2y]`.
* @param triangleIndices - An array of 3 elements, contains the original indices of the triangle's vertices: `[index0, index1, index2]`.
* @returns The resulting ring of vertex indices and the index (to the returned ring array) of the leftmost vertex in the ring.
*/
_scanlineGenerateVertexRingForCellRow(cellRow, triangleVertices, triangleIndices) {
const cellRowYTop = cellRow * this._granularityCellSize;
const cellRowYBottom = cellRowYTop + this._granularityCellSize;
const ring = [];
for (let edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
const aX = triangleVertices[edgeIndex * 2];
const aY = triangleVertices[edgeIndex * 2 + 1];
const bX = triangleVertices[(edgeIndex + 1) * 2 % 6];
const bY = triangleVertices[((edgeIndex + 1) * 2 + 1) % 6];
const cX = triangleVertices[(edgeIndex + 2) * 2 % 6];
const cY = triangleVertices[((edgeIndex + 2) * 2 + 1) % 6];
const dirX = bX - aX;
const dirY = bY - aY;
const isParallelY = dirX === 0;
const isParallelX = dirY === 0;
const tTop = (cellRowYTop - aY) / dirY;
const tBottom = (cellRowYBottom - aY) / dirY;
const tEnter = Math.min(tTop, tBottom);
const tExit = Math.max(tTop, tBottom);
if (!isParallelX && (tEnter >= 1 || tExit <= 0) || isParallelX && (aY < cellRowYTop || aY > cellRowYBottom)) {
if (bY >= cellRowYTop && bY <= cellRowYBottom) ring.push(triangleIndices[(edgeIndex + 1) % 3]);
continue;
}
if (!isParallelX && tEnter > 0) {
const x = aX + dirX * tEnter;
const y = aY + dirY * tEnter;
ring.push(this._vertexToIndex(x, y));
}
const enterX = aX + dirX * Math.max(tEnter, 0);
const exitX = aX + dirX * Math.min(tExit, 1);
if (!isParallelY) this._generateIntraEdgeVertices(ring, aX, aY, bX, bY, enterX, exitX);
if (!isParallelX && tExit < 1) {
const x = aX + dirX * tExit;
const y = aY + dirY * tExit;
ring.push(this._vertexToIndex(x, y));
}
if (isParallelX || bY >= cellRowYTop && bY <= cellRowYBottom) ring.push(triangleIndices[(edgeIndex + 1) % 3]);
if (!isParallelX && (bY <= cellRowYTop || bY >= cellRowYBottom)) this._generateInterEdgeVertices(ring, aX, aY, bX, bY, cX, cY, exitX, cellRowYTop, cellRowYBottom);
}
return ring;
}
/**
* Generates ring vertices along an edge A-\>B, but only in the part that intersects a given cell row.
* Does not handle adding edge endpoint vertices or edge cell row enter/exit vertices.
* @param ring - Ordered array of vertex indices for the constructed ring. New indices are placed here.
* @param enterX - The X coordinate of the point where edge A-\>B enters the current cell row.
* @param exitX - The X coordinate of the point where edge A-\>B exits the current cell row.
*/
_generateIntraEdgeVertices(ring, aX, aY, bX, bY, enterX, exitX) {
const dirX = bX - aX;
const dirY = bY - aY;
const isParallelX = dirY === 0;
const leftX = isParallelX ? Math.min(aX, bX) : Math.min(enterX, exitX);
const rightX = isParallelX ? Math.max(aX, bX) : Math.max(enterX, exitX);
const edgeSubdivisionLeftCellX = Math.floor(leftX / this._granularityCellSize) + 1;
const edgeSubdivisionRightCellX = Math.ceil(rightX / this._granularityCellSize) - 1;
if (isParallelX ? aX < bX : enterX < exitX) for (let cellX = edgeSubdivisionLeftCellX; cellX <= edgeSubdivisionRightCellX; cellX++) {
const x = cellX * this._granularityCellSize;
const y = aY + dirY * (x - aX) / dirX;
ring.push(this._vertexToIndex(x, y));
}
else for (let cellX = edgeSubdivisionRightCellX; cellX >= edgeSubdivisionLeftCellX; cellX--) {
const x = cellX * this._granularityCellSize;
const y = aY + dirY * (x - aX) / dirX;
ring.push(this._vertexToIndex(x, y));
}
}
/**
* Generates ring vertices along cell border.
* Call when processing an edge A-\>B that exits the current row (B lies outside the current row).
* Generates vertices along the cell edge between the exit point from cell row
* of edge A-\>B and entry of edge B-\>C, or entry of C-\>A if both A and C lie outside the cell row.
* Does not handle adding edge endpoint vertices or edge cell row enter/exit vertices.
* @param ring - Ordered array of vertex indices for the constructed ring. New indices are placed here.
* @param exitX - The X coordinate of the point where edge A-\>B exits the current cell row.
* @param cellRowYTop - The current cell row top Y coordinate.
* @param cellRowYBottom - The current cell row bottom Y coordinate.
*/
_generateInterEdgeVertices(ring, aX, aY, bX, bY, cX, cY, exitX, cellRowYTop, cellRowYBottom) {
const dirY = bY - aY;
const dir2X = cX - bX;
const dir2Y = cY - bY;
const t2Top = (cellRowYTop - bY) / dir2Y;
const t2Bottom = (cellRowYBottom - bY) / dir2Y;
const t2Enter = Math.min(t2Top, t2Bottom);
const t2Exit = Math.max(t2Top, t2Bottom);
const enter2X = bX + dir2X * t2Enter;
let boundarySubdivisionLeftCellX = Math.floor(Math.min(enter2X, exitX) / this._granularityCellSize) + 1;
let boundarySubdivisionRightCellX = Math.ceil(Math.max(enter2X, exitX) / this._granularityCellSize) - 1;
let isBoundaryLeftToRight = exitX < enter2X;
const isParallelX2 = dir2Y === 0;
if (isParallelX2 && (cY === cellRowYTop || cY === cellRowYBottom)) return;
if (isParallelX2 || t2Enter >= 1 || t2Exit <= 0) {
const dir3X = aX - cX;
const dir3Y = aY - cY;
const t3Top = (cellRowYTop - cY) / dir3Y;
const t3Bottom = (cellRowYBottom - cY) / dir3Y;
const enter3X = cX + dir3X * Math.min(t3Top, t3Bottom);
boundarySubdivisionLeftCellX = Math.floor(Math.min(enter3X, exitX) / this._granularityCellSize) + 1;
boundarySubdivisionRightCellX = Math.ceil(Math.max(enter3X, exitX) / this._granularityCellSize) - 1;
isBoundaryLeftToRight = exitX < enter3X;
}
const boundaryY = dirY > 0 ? cellRowYBottom : cellRowYTop;
if (isBoundaryLeftToRight) for (let cellX = boundarySubdivisionLeftCellX; cellX <= boundarySubdivisionRightCellX; cellX++) {
const x = cellX * this._granularityCellSize;
ring.push(this._vertexToIndex(x, boundaryY));
}
else for (let cellX = boundarySubdivisionRightCellX; cellX >= boundarySubdivisionLeftCellX; cellX--) {
const x = cellX * this._granularityCellSize;
ring.push(this._vertexToIndex(x, boundaryY));
}
}
/**
* Generates an outline for a given polygon, returns a list of arrays of line indices.
*/
_generateOutline(polygon) {
const subdividedLines = [];
for (const ring of polygon) {
const line = subdivideVertexLine(ring, this._granularity, true);
const pathIndices = this._pointArrayToIndices(line);
const lineIndices = [];
for (let i = 1; i < pathIndices.length; i++) {
lineIndices.push(pathIndices[i - 1]);
lineIndices.push(pathIndices[i]);
}
subdividedLines.push(lineIndices);
}
return subdividedLines;
}
/**
* Adds pole geometry if needed.
* @param subdividedTriangles - Array of generated triangle indices, new pole geometry is appended here.
*/
_handlePoles(subdividedTriangles) {
let north = false;
let south = false;
if (this._canonical) {
if (this._canonical.y === 0) north = true;
if (this._canonical.y === (1 << this._canonical.z) - 1) south = true;
}
if (north || south) this._fillPoles(subdividedTriangles, north, south);
}
/**
* Checks the internal vertex buffer for all vertices that might lie on the special pole coordinates and shifts them by one unit.
* Use for removing unintended pole vertices that might have been created during subdivision. After calling this function, actual pole vertices can be safely generated.
*/
_ensureNoPoleVertices() {
const flattened = this._vertexBuffer;
for (let i = 0; i < flattened.length; i += 2) {
const vy = flattened[i + 1];
if (vy === -32768) flattened[i + 1] = -32767;
if (vy === 32767) flattened[i + 1] = 32766;
}
}
/**
* Generates a quad from an edge to a pole with the correct winding order.
* Helper function used inside {@link _fillPoles}.
* @param indices - Index array into which the geometry is generated.
* @param i0 - Index of the first edge vertex.
* @param i1 - Index of the second edge vertex.
* @param v0x - X coordinate of the first edge vertex.
* @param v1x - X coordinate of the second edge vertex.
* @param poleY - The Y coordinate of the desired pole (NORTH_POLE_Y or SOUTH_POLE_Y).
*/
_generatePoleQuad(indices, i0, i1, v0x, v1x, poleY) {
if (v0x > v1x !== (poleY === NORTH_POLE_Y)) {
indices.push(i0);
indices.push(i1);
indices.push(this._vertexToIndex(v0x, poleY));
indices.push(i1);
indices.push(this._vertexToIndex(v1x, poleY));
indices.push(this._vertexToIndex(v0x, poleY));
} else {
indices.push(i1);
indices.push(i0);
indices.push(this._vertexToIndex(v0x, poleY));
indices.push(this._vertexToIndex(v1x, poleY));
indices.push(i1);
indices.push(this._vertexToIndex(v0x, poleY));
}
}
/**
* Detects edges that border the north or south tile edge
* and adds triangles that extend those edges to the poles.
* Only run this function on tiles that border the poles.
* Assumes that supplied geometry is clipped to the inclusive range of 0..EXTENT.
* Mutates the supplies vertex and index arrays.
* @param indices - Triangle indices. This array is appended with new primitives.
* @param north - Whether to generate geometry for the north pole.
* @param south - Whether to generate geometry for the south pole.
*/
_fillPoles(indices, north, south) {
const flattened = this._vertexBuffer;
const northEdge = 0;
const southEdge = EXTENT$1;
const numIndices = indices.length;
for (let primitiveIndex = 2; primitiveIndex < numIndices; primitiveIndex += 3) {
const i0 = indices[primitiveIndex - 2];
const i1 = indices[primitiveIndex - 1];
const i2 = indices[primitiveIndex];
const v0x = flattened[i0 * 2];
const v0y = flattened[i0 * 2 + 1];
const v1x = flattened[i1 * 2];
const v1y = flattened[i1 * 2 + 1];
const v2x = flattened[i2 * 2];
const v2y = flattened[i2 * 2 + 1];
if (north) {
if (v0y === northEdge && v1y === northEdge) this._generatePoleQuad(indices, i0, i1, v0x, v1x, NORTH_POLE_Y);
if (v1y === northEdge && v2y === northEdge) this._generatePoleQuad(indices, i1, i2, v1x, v2x, NORTH_POLE_Y);
if (v2y === northEdge && v0y === northEdge) this._generatePoleQuad(indices, i2, i0, v2x, v0x, NORTH_POLE_Y);
}
if (south) {
if (v0y === southEdge && v1y === southEdge) this._generatePoleQuad(indices, i0, i1, v0x, v1x, SOUTH_POLE_Y);
if (v1y === southEdge && v2y === southEdge) this._generatePoleQuad(indices, i1, i2, v1x, v2x, SOUTH_POLE_Y);
if (v2y === southEdge && v0y === southEdge) this._generatePoleQuad(indices, i2, i0, v2x, v0x, SOUTH_POLE_Y);
}
}
}
/**
* Adds all vertices in the supplied flattened vertex buffer into the internal vertex buffer.
*/
_initializeVertices(flattened) {
for (let i = 0; i < flattened.length; i += 2) this._vertexToIndex(flattened[i], flattened[i + 1]);
}
/**
* Subdivides an input mesh. Imagine a regular square grid with the target granularity overlaid over the mesh - this is the subdivision's result.
* Assumes a mesh of tile features - vertex coordinates are integers, visible range where subdivision happens is 0..8192.
* @param polygon - The input polygon, specified as a list of vertex rings.
* @param generateOutlineLines - When true, also generates line indices for outline of the supplied polygon.
* @returns Vertex and index buffers with subdivision applied.
*/
subdividePolygonInternal(polygon, generateOutlineLines) {
if (this._used) throw new Error("Subdivision: multiple use not allowed.");
this._used = true;
const { flattened, holeIndices } = flatten(polygon);
this._initializeVertices(flattened);
let subdividedTriangles;
try {
const earcutResult = earcut(flattened, holeIndices);
const cut = this._convertIndices(flattened, earcutResult);
subdividedTriangles = this._subdivideTrianglesScanline(cut);
} catch (e) {
console.error(e);
}
let subdividedLines = [];
if (generateOutlineLines) subdividedLines = this._generateOutline(polygon);
this._ensureNoPoleVertices();
this._handlePoles(subdividedTriangles);
if (this._granularity >= 2 && this._canonical?.z === 0) {
subdividedTriangles = this._removeTrianglesOutsideTileX(subdividedTriangles);
subdividedLines = subdividedLines.map((lines) => this._removeLinesOutsideTileX(lines));
}
return {
verticesFlattened: this._vertexBuffer,
indicesTriangles: subdividedTriangles,
indicesLineList: subdividedLines
};
}
_vertexOutsideTileX(index) {
const x = this._vertexBuffer[index * 2];
return x < 0 || x > 8192;
}
/**
* Drops all triangles that reach beyond the tile's X extent.
*
* On globe the z0 tile's buffer wraps around the planet onto the tile itself, drawing buffered geometry twice.
* Only globe uses subdivision (`granularity >= 2`), so mercator is never affected.
* @param indices - Triangle indices into `this._vertexBuffer`.
* @returns The indices with every triangle that has a vertex outside the tile's X extent removed.
*/
_removeTrianglesOutsideTileX(indices) {
const filtered = [];
for (let i = 0; i < indices.length; i += 3) {
if (this._vertexOutsideTileX(indices[i]) || this._vertexOutsideTileX(indices[i + 1]) || this._vertexOutsideTileX(indices[i + 2])) continue;
filtered.push(indices[i], indices[i + 1], indices[i + 2]);
}
return filtered;
}
/**
* Drops all outline line segments that reach beyond the tile's X extent,
* for the same reason as {@link Subdivider._removeTrianglesOutsideTileX}.
* @param indices - Line segment indices into `this._vertexBuffer`.
* @returns The indices with every segment that has a vertex outside the tile's X extent removed.
*/
_removeLinesOutsideTileX(indices) {
const filtered = [];
for (let i = 0; i < indices.length; i += 2) {
if (this._vertexOutsideTileX(indices[i]) || this._vertexOutsideTileX(indices[i + 1])) continue;
filtered.push(indices[i], indices[i + 1]);
}
return filtered;
}
/**
* Sometimes the supplies vertex and index array has duplicate vertices - same coordinates that are referenced by multiple different indices.
* That is not allowed for purposes of subdivision, duplicates are removed in `this.initializeVertices`.
* This function converts the original index array that indexes into the original vertex array with duplicates
* into an index array that indexes into `this._finalVertices`.
* @param vertices - Flattened vertex array used by the old indices. This may contain duplicate vertices.
* @param oldIndices - Indices into the old vertex array.
* @returns Indices transformed so that they are valid indices into `this._finalVertices` (with duplicates removed).
*/
_convertIndices(vertices, oldIndices) {
const newIndices = [];
for (const oldIndex of oldIndices) {
const x = vertices[oldIndex * 2];
const y = vertices[oldIndex * 2 + 1];
newIndices.push(this._vertexToIndex(x, y));
}
return newIndices;
}
/**
* Converts an array of points into an array of indices into the internal vertex buffer (`_finalVertices`).
*/
_pointArrayToIndices(array) {
const indices = [];
for (const p of array) indices.push(this._vertexToIndex(p.x, p.y));
return indices;
}
};
/**
* Subdivides a polygon to a given granularity. Intended for preprocessing geometry for the 'fill' and 'fill-extrusion' layer types.
* All returned triangles have the counter-clockwise winding order.
* @param polygon - An array of point rings that specify the polygon. The first ring is the polygon exterior, all subsequent rings form holes inside the first ring.
* @param canonical - The canonical tile ID of the tile this polygon belongs to. Needed for generating special geometry for tiles that border the poles.
* @param granularity - The subdivision granularity. If we assume tile EXTENT=8192, then a granularity of 2 will result in geometry being "cut" on each axis
* divisible by 4096 (including outside the tile range, so -8192, -4096, or 12288...), granularity of 8 on axes divisible by 1024 and so on.
* Granularity of 1 or lower results in *no* subdivision.
* @param generateOutlineLines - When true, also generates index arrays for subdivided lines that form the outline of the supplied polygon. True by default.
* @returns An object that contains the generated vertex array, triangle index array and, if specified, line index arrays.
*/
function subdividePolygon(polygon, canonical, granularity, generateOutlineLines = true) {
return new Subdivider(granularity, canonical).subdividePolygonInternal(polygon, generateOutlineLines);
}
/**
* Subdivides a line represented by an array of points. Mainly intended for preprocessing geometry for the 'line' layer type.
* Assumes a line segment between each two consecutive points in the array.
* Does not assume a line segment from last point to first point, unless `isRing` is set to `true`.
* For example, an array of 4 points describes exactly 3 line segments.
* @param linePoints - An array of points describing the line segments.
* @param granularity - Subdivision granularity.
* @param isRing - When true, an additional line segment is assumed to exist between the input array's last and first point.
* @returns A new array of points of the subdivided line segments. The array may contain some of the original Point objects. If `isRing` is set to `true`, then this also includes the (subdivided) segment from the last point of the input array to the first point.
*
* @example
* ```ts
* const result = subdivideVertexLine([
* new Point(0, 0),
* new Point(8, 0),
* new Point(0, 8),
* ], EXTENT / 4, false);
* // Results in an array of points with these (x, y) coordinates:
* // 0, 0
* // 4, 0
* // 8, 0
* // 4, 4
* // 0, 8
* ```
*
* @example
* ```ts
* const result = subdivideVertexLine([
* new Point(0, 0),
* new Point(8, 0),
* new Point(0, 8),
* ], EXTENT / 4, true);
* // Results in an array of points with these (x, y) coordinates:
* // 0, 0
* // 4, 0
* // 8, 0
* // 4, 4
* // 0, 8
* // 0, 4
* // 0, 0
* ```
*/
function subdivideVertexLine(linePoints, granularity, isRing = false) {
if (!linePoints || linePoints.length < 1) return [];
if (linePoints.length < 2) return [];
const first = linePoints[0];
const last = linePoints[linePoints.length - 1];
const addLastToFirstSegment = isRing && (first.x !== last.x || first.y !== last.y);
if (granularity < 2) {
if (addLastToFirstSegment) return [...linePoints, linePoints[0]];
else return [...linePoints];
}
const cellSize = Math.floor(EXTENT$1 / granularity);
const finalLineVertices = [];
finalLineVertices.push(new Point(linePoints[0].x, linePoints[0].y));
const totalPoints = linePoints.length;
const lastIndex = addLastToFirstSegment ? totalPoints : totalPoints - 1;
for (let pointIndex = 0; pointIndex < lastIndex; pointIndex++) {
const linePoint0 = linePoints[pointIndex];
const linePoint1 = pointIndex < totalPoints - 1 ? linePoints[pointIndex + 1] : linePoints[0];
const lineVertex0x = linePoint0.x;
const lineVertex0y = linePoint0.y;
const lineVertex1x = linePoint1.x;
const lineVertex1y = linePoint1.y;
const dirXnonZero = lineVertex0x !== lineVertex1x;
const dirYnonZero = lineVertex0y !== lineVertex1y;
if (!dirXnonZero && !dirYnonZero) continue;
const dirX = lineVertex1x - lineVertex0x;
const dirY = lineVertex1y - lineVertex0y;
const absDirX = Math.abs(dirX);
const absDirY = Math.abs(dirY);
let lastPointX = lineVertex0x;
let lastPointY = lineVertex0y;
while (true) {
const nextBoundaryX = dirX > 0 ? (Math.floor(lastPointX / cellSize) + 1) * cellSize : (Math.ceil(lastPointX / cellSize) - 1) * cellSize;
const nextBoundaryY = dirY > 0 ? (Math.floor(lastPointY / cellSize) + 1) * cellSize : (Math.ceil(lastPointY / cellSize) - 1) * cellSize;
const axisDistanceToBoundaryX = Math.abs(lastPointX - nextBoundaryX);
const axisDistanceToBoundaryY = Math.abs(lastPointY - nextBoundaryY);
const axisDistanceToEndX = Math.abs(lastPointX - lineVertex1x);
const axisDistanceToEndY = Math.abs(lastPointY - lineVertex1y);
const realDistanceToBoundaryX = dirXnonZero ? axisDistanceToBoundaryX / absDirX : Number.POSITIVE_INFINITY;
const realDistanceToBoundaryY = dirYnonZero ? axisDistanceToBoundaryY / absDirY : Number.POSITIVE_INFINITY;
if ((axisDistanceToEndX <= axisDistanceToBoundaryX || !dirXnonZero) && (axisDistanceToEndY <= axisDistanceToBoundaryY || !dirYnonZero)) break;
if (realDistanceToBoundaryX < realDistanceToBoundaryY && dirXnonZero || !dirYnonZero) {
lastPointX = nextBoundaryX;
lastPointY = lastPointY + dirY * realDistanceToBoundaryX;
const next = new Point(lastPointX, Math.round(lastPointY));
if (finalLineVertices[finalLineVertices.length - 1].x !== next.x || finalLineVertices[finalLineVertices.length - 1].y !== next.y) finalLineVertices.push(next);
} else {
lastPointX = lastPointX + dirX * realDistanceToBoundaryY;
lastPointY = nextBoundaryY;
const next = new Point(Math.round(lastPointX), lastPointY);
if (finalLineVertices[finalLineVertices.length - 1].x !== next.x || finalLineVertices[finalLineVertices.length - 1].y !== next.y) finalLineVertices.push(next);
}
}
const last = new Point(lineVertex1x, lineVertex1y);
if (finalLineVertices[finalLineVertices.length - 1].x !== last.x || finalLineVertices[finalLineVertices.length - 1].y !== last.y) finalLineVertices.push(last);
}
return finalLineVertices;
}
/**
* Takes a polygon as an array of point rings, returns a flattened array of the X,Y coordinates of these points.
* Also creates an array of hole indices. Both returned arrays are required for `earcut`.
*/
function flatten(polygon) {
const holeIndices = [];
const flattened = [];
for (const ring of polygon) {
if (ring.length === 0) continue;
if (ring !== polygon[0]) holeIndices.push(flattened.length / 2);
for (const vertex of ring) {
flattened.push(vertex.x);
flattened.push(vertex.y);
}
}
return {
flattened,
holeIndices
};
}
/**
* Returns a new array of indices where all triangles have the counter-clockwise winding order.
* @param flattened - Flattened vertex buffer.
* @param indices - Triangle indices.
*/
function fixWindingOrder(flattened, indices) {
const corrected = [];
for (let i = 0; i < indices.length; i += 3) {
const i0 = indices[i];
const i1 = indices[i + 1];
const i2 = indices[i + 2];
const v0x = flattened[i0 * 2];
const v0y = flattened[i0 * 2 + 1];
const v1x = flattened[i1 * 2];
const v1y = flattened[i1 * 2 + 1];
const v2x = flattened[i2 * 2];
const v2y = flattened[i2 * 2 + 1];
const e0x = v1x - v0x;
const e0y = v1y - v0y;
const e1x = v2x - v0x;
if (e0x * (v2y - v0y) - e0y * e1x > 0) {
corrected.push(i0);
corrected.push(i2);
corrected.push(i1);
} else {
corrected.push(i0);
corrected.push(i1);
corrected.push(i2);
}
}
return corrected;
}
/**
* Triangulates a ring of vertex indices. Appends to the supplied array of final triangle indices.
* @param vertexBuffer - Flattened vertex coordinate array.
* @param ring - Ordered ring of vertex indices to triangulate.
* @param leftmostIndex - The index of the leftmost vertex in the supplied ring.
* @param finalIndices - Array of final triangle indices, into where the resulting triangles are appended.
*/
function scanlineTriangulateVertexRing(vertexBuffer, ring, finalIndices) {
if (ring.length === 0) throw new Error("Subdivision vertex ring is empty.");
let leftmostIndex = 0;
let leftmostX = vertexBuffer[ring[0] * 2];
for (let i = 1; i < ring.length; i++) {
const x = vertexBuffer[ring[i] * 2];
if (x < leftmostX) {
leftmostX = x;
leftmostIndex = i;
}
}
const ringVertexLength = ring.length;
let lastEdgeA = leftmostIndex;
let lastEdgeB = (lastEdgeA + 1) % ringVertexLength;
while (true) {
const candidateIndexA = lastEdgeA - 1 >= 0 ? lastEdgeA - 1 : ringVertexLength - 1;
const candidateIndexB = (lastEdgeB + 1) % ringVertexLength;
const candidateAx = vertexBuffer[ring[candidateIndexA] * 2];
const candidateAy = vertexBuffer[ring[candidateIndexA] * 2 + 1];
const candidateBx = vertexBuffer[ring[candidateIndexB] * 2];
const candidateBy = vertexBuffer[ring[candidateIndexB] * 2 + 1];
const lastEdgeAx = vertexBuffer[ring[lastEdgeA] * 2];
const lastEdgeAy = vertexBuffer[ring[lastEdgeA] * 2 + 1];
const lastEdgeBx = vertexBuffer[ring[lastEdgeB] * 2];
const lastEdgeBy = vertexBuffer[ring[lastEdgeB] * 2 + 1];
let pickA = false;
if (candidateAx < candidateBx) pickA = true;
else if (candidateAx > candidateBx) pickA = false;
else {
const nx = lastEdgeBy - lastEdgeAy;
const ny = -(lastEdgeBx - lastEdgeAx);
const sign = lastEdgeAy < lastEdgeBy ? 1 : -1;
if (((candidateAx - lastEdgeAx) * nx + (candidateAy - lastEdgeAy) * ny) * sign > ((candidateBx - lastEdgeAx) * nx + (candidateBy - lastEdgeAy) * ny) * sign) pickA = true;
}
if (pickA) {
const c = ring[candidateIndexA];
const a = ring[lastEdgeA];
const b = ring[lastEdgeB];
if (c !== a && c !== b && a !== b) finalIndices.push(b, a, c);
lastEdgeA--;
if (lastEdgeA < 0) lastEdgeA = ringVertexLength - 1;
} else {
const c = ring[candidateIndexB];
const a = ring[lastEdgeA];
const b = ring[lastEdgeB];
if (c !== a && c !== b && a !== b) finalIndices.push(b, a, c);
lastEdgeB++;
if (lastEdgeB >= ringVertexLength) lastEdgeB = 0;
}
if (candidateIndexA === candidateIndexB) break;
}
}
//#endregion
//#region src/render/fill_large_mesh_arrays.ts
/**
* This function will take any "mesh" and fill in into vertex buffers, breaking it up into multiple drawcalls as needed
* if too many (\>65535) vertices are used.
* This function is mainly intended for use with subdivided geometry, since sometimes subdivision might generate
* more vertices than what fits into 16 bit indices.
*
* Accepts a triangle mesh, optionally with a line list (for fill outlines) as well. The triangle and line segments are expected to share a single vertex buffer.
*
* Mutates the provided `segmentsTriangles` and `segmentsLines` SegmentVectors,
* `vertexArray`, `triangleIndexArray` and optionally `lineIndexArray`.
* Does not mutate the input `flattened` vertices, `triangleIndices` and `lineList`.
* @param addVertex - A function for adding a new vertex into `vertexArray`. We might sometimes want to add more values per vertex than just X and Y coordinates, which can be handled in this function.
* @param segmentsTriangles - The segment array for triangle draw calls. New segments will be placed here.
* @param vertexArray - The vertex array into which new vertices are placed by the provided `addVertex` function.
* @param triangleIndexArray - Index array for drawing triangles. New triangle indices are placed here.
* @param flattened - The input flattened array or vertex coordinates.
* @param triangleIndices - Triangle indices into `flattened`.
* @param segmentsLines - Segment array for line draw calls. New segments will be placed here. Only needed if the mesh also contains lines.
* @param lineIndexArray - Index array for drawing lines. New triangle indices are placed here. Only needed if the mesh also contains lines.
* @param lineList - Line indices into `flattened`. Only needed if the mesh also contains lines.
*/
function fillLargeMeshArrays(addVertex, segmentsTriangles, vertexArray, triangleIndexArray, flattened, triangleIndices, segmentsLines, lineIndexArray, lineList) {
const numVertices = flattened.length / 2;
const hasLines = segmentsLines && lineIndexArray && lineList;
if (numVertices < SegmentVector.MAX_VERTEX_ARRAY_LENGTH) {
const triangleSegment = segmentsTriangles.prepareSegment(numVertices, vertexArray, triangleIndexArray);
const triangleIndex = triangleSegment.vertexLength;
for (let i = 0; i < triangleIndices.length; i += 3) triangleIndexArray.emplaceBack(triangleIndex + triangleIndices[i], triangleIndex + triangleIndices[i + 1], triangleIndex + triangleIndices[i + 2]);
triangleSegment.vertexLength += numVertices;
triangleSegment.primitiveLength += triangleIndices.length / 3;
let lineIndicesStart;
let lineSegment;
if (hasLines) {
lineSegment = segmentsLines.prepareSegment(numVertices, vertexArray, lineIndexArray);
lineIndicesStart = lineSegment.vertexLength;
lineSegment.vertexLength += numVertices;
}
for (let i = 0; i < flattened.length; i += 2) addVertex(flattened[i], flattened[i + 1]);
if (hasLines) for (const lineIndices of lineList) {
for (let i = 1; i < lineIndices.length; i += 2) lineIndexArray.emplaceBack(lineIndicesStart + lineIndices[i - 1], lineIndicesStart + lineIndices[i]);
lineSegment.primitiveLength += lineIndices.length / 2;
}
} else {
fillSegmentsTriangles(segmentsTriangles, vertexArray, triangleIndexArray, flattened, triangleIndices, addVertex);
if (hasLines) fillSegmentsLines(segmentsLines, vertexArray, lineIndexArray, flattened, lineList, addVertex);
segmentsTriangles.forceNewSegmentOnNextPrepare();
segmentsLines?.forceNewSegmentOnNextPrepare();
}
}
/**
* Determines the new index of a vertex given by its old index.
* @param actualVertexIndices - Array that maps the old index of a given vertex to a new index in the final vertex buffer.
* @param flattened - Old vertex buffer.
* @param addVertex - Function for creating a new vertex in the final vertex buffer.
* @param totalVerticesCreated - Reference to an int holding how many vertices were added to the final vertex buffer.
* @param oldIndex - The old index of the desired vertex.
* @param needsCopy - Whether to duplicate the desired vertex in the final vertex buffer.
* @param segment - The current segment.
* @returns Index of the vertex in the final vertex array.
*/
function copyOrReuseVertex(actualVertexIndices, flattened, addVertex, totalVerticesCreated, oldIndex, needsCopy, segment) {
if (needsCopy) {
const newIndex = totalVerticesCreated.count;
addVertex(flattened[oldIndex * 2], flattened[oldIndex * 2 + 1]);
actualVertexIndices[oldIndex] = totalVerticesCreated.count;
totalVerticesCreated.count++;
segment.vertexLength++;
return newIndex;
} else return actualVertexIndices[oldIndex];
}
function fillSegmentsTriangles(segmentsTriangles, vertexArray, triangleIndexArray, flattened, triangleIndices, addVertex) {
const actualVertexIndices = [];
for (let i = 0; i < flattened.length / 2; i++) actualVertexIndices.push(-1);
const totalVerticesCreated = { count: 0 };
let currentSegmentCutoff = 0;
let segment = segmentsTriangles.getOrCreateLatestSegment(vertexArray, triangleIndexArray);
let baseVertex = segment.vertexLength;
for (let primitiveEndIndex = 2; primitiveEndIndex < triangleIndices.length; primitiveEndIndex += 3) {
const i0 = triangleIndices[primitiveEndIndex - 2];
const i1 = triangleIndices[primitiveEndIndex - 1];
const i2 = triangleIndices[primitiveEndIndex];
let i0needsVertexCopy = actualVertexIndices[i0] < currentSegmentCutoff;
let i1needsVertexCopy = actualVertexIndices[i1] < currentSegmentCutoff;
let i2needsVertexCopy = actualVertexIndices[i2] < currentSegmentCutoff;
const vertexCopyCount = (i0needsVertexCopy ? 1 : 0) + (i1needsVertexCopy ? 1 : 0) + (i2needsVertexCopy ? 1 : 0);
if (segment.vertexLength + vertexCopyCount > SegmentVector.MAX_VERTEX_ARRAY_LENGTH) {
segment = segmentsTriangles.createNewSegment(vertexArray, triangleIndexArray);
currentSegmentCutoff = totalVerticesCreated.count;
i0needsVertexCopy = true;
i1needsVertexCopy = true;
i2needsVertexCopy = true;
baseVertex = 0;
}
const actualIndex0 = copyOrReuseVertex(actualVertexIndices, flattened, addVertex, totalVerticesCreated, i0, i0needsVertexCopy, segment);
const actualIndex1 = copyOrReuseVertex(actualVertexIndices, flattened, addVertex, totalVerticesCreated, i1, i1needsVertexCopy, segment);
const actualIndex2 = copyOrReuseVertex(actualVertexIndices, flattened, addVertex, totalVerticesCreated, i2, i2needsVertexCopy, segment);
triangleIndexArray.emplaceBack(baseVertex + actualIndex0 - currentSegmentCutoff, baseVertex + actualIndex1 - currentSegmentCutoff, baseVertex + actualIndex2 - currentSegmentCutoff);
segment.primitiveLength++;
}
}
function fillSegmentsLines(segmentsLines, vertexArray, lineIndexArray, flattened, lineList, addVertex) {
const actualVertexIndices = [];
for (let i = 0; i < flattened.length / 2; i++) actualVertexIndices.push(-1);
const totalVerticesCreated = { count: 0 };
let currentSegmentCutoff = 0;
let segment = segmentsLines.getOrCreateLatestSegment(vertexArray, lineIndexArray);
let baseVertex = segment.vertexLength;
for (const currentLine of lineList) for (let lineVertex = 1; lineVertex < currentLine.length; lineVertex += 2) {
const i0 = currentLine[lineVertex - 1];
const i1 = currentLine[lineVertex];
let i0needsVertexCopy = actualVertexIndices[i0] < currentSegmentCutoff;
let i1needsVertexCopy = actualVertexIndices[i1] < currentSegmentCutoff;
const vertexCopyCount = (i0needsVertexCopy ? 1 : 0) + (i1needsVertexCopy ? 1 : 0);
if (segment.vertexLength + vertexCopyCount > SegmentVector.MAX_VERTEX_ARRAY_LENGTH) {
segment = segmentsLines.createNewSegment(vertexArray, lineIndexArray);
currentSegmentCutoff = totalVerticesCreated.count;
i0needsVertexCopy = true;
i1needsVertexCopy = true;
baseVertex = 0;
}
const actualIndex0 = copyOrReuseVertex(actualVertexIndices, flattened, addVertex, totalVerticesCreated, i0, i0needsVertexCopy, segment);
const actualIndex1 = copyOrReuseVertex(actualVertexIndices, flattened, addVertex, totalVerticesCreated, i1, i1needsVertexCopy, segment);
lineIndexArray.emplaceBack(baseVertex + actualIndex0 - currentSegmentCutoff, baseVertex + actualIndex1 - currentSegmentCutoff);
segment.primitiveLength++;
}
}
//#endregion
//#region src/data/bucket/fill_bucket.ts
const EARCUT_MAX_RINGS$1 = 500;
var FillBucket = class {
constructor(options) {
this.zoom = options.zoom;
this.overscaling = options.overscaling;
this.layers = options.layers;
this.layerIds = this.layers.map((layer) => layer.id);
this.index = options.index;
this.hasDependencies = false;
this.patternFeatures = [];
this.layoutVertexArray = new FillLayoutArray();
this.indexArray = new TriangleIndexArray();
this.indexArray2 = new LineIndexArray();
this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom);
this.segments = new SegmentVector();
this.segments2 = new SegmentVector();
this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id);
}
populate(features, options, canonical) {
this.hasDependencies = hasPattern("fill", this.layers, options);
const fillSortKey = this.layers[0].layout.get("fill-sort-key");
const sortFeaturesByKey = !fillSortKey.isConstant();
const bucketFeatures = [];
const globalProperties = new EvaluationParameters(this.zoom);
const needGeometry = this.layers[0]._featureFilter.needGeometry;
for (const { feature, id, index, sourceLayerIndex } of features) {
const evaluationFeature = toEvaluationFeature(feature, needGeometry);
if (!this.layers[0]._featureFilter.filter(globalProperties, evaluationFeature, canonical)) continue;
const sortKey = sortFeaturesByKey ? fillSortKey.evaluate(evaluationFeature, {}, canonical, options.availableImages) : void 0;
const bucketFeature = {
id,
properties: feature.properties,
type: feature.type,
sourceLayerIndex,
index,
geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
patterns: {},
sortKey
};
bucketFeatures.push(bucketFeature);
}
if (sortFeaturesByKey) bucketFeatures.sort((a, b) => a.sortKey - b.sortKey);
for (const bucketFeature of bucketFeatures) {
const { geometry, index, sourceLayerIndex } = bucketFeature;
if (this.hasDependencies) {
const patternFeature = addPatternDependencies("fill", this.layers, bucketFeature, { zoom: this.zoom }, options);
this.patternFeatures.push(patternFeature);
} else this.addFeature(bucketFeature, geometry, index, canonical, {}, options.subdivisionGranularity);
const feature = features[index].feature;
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
}
}
update(states, vtLayer, imagePositions) {
if (!this.stateDependentLayers.length) return;
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, { imagePositions });
}
addFeatures(options, canonical, imagePositions) {
for (const feature of this.patternFeatures) this.addFeature(feature, feature.geometry, feature.index, canonical, imagePositions, options.subdivisionGranularity);
}
isEmpty() {
return this.layoutVertexArray.length === 0;
}
uploadPending() {
return !this.uploaded || this.programConfigurations.needsUpload;
}
upload(context) {
if (!this.uploaded) {
this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, members$3);
this.indexBuffer = context.createIndexBuffer(this.indexArray);
this.indexBuffer2 = context.createIndexBuffer(this.indexArray2);
}
this.programConfigurations.upload(context);
this.uploaded = true;
}
destroy() {
if (!this.layoutVertexBuffer) return;
this.layoutVertexBuffer.destroy();
this.indexBuffer.destroy();
this.indexBuffer2.destroy();
this.programConfigurations.destroy();
this.segments.destroy();
this.segments2.destroy();
}
addFeature(feature, geometry, index, canonical, imagePositions, subdivisionGranularity) {
for (const polygon of classifyRings$1(geometry, EARCUT_MAX_RINGS$1)) {
const subdivided = subdividePolygon(polygon, canonical, subdivisionGranularity.fill.getGranularityForZoomLevel(canonical.z));
const vertexArray = this.layoutVertexArray;
fillLargeMeshArrays((x, y) => {
vertexArray.emplaceBack(x, y);
}, this.segments, this.layoutVertexArray, this.indexArray, subdivided.verticesFlattened, subdivided.indicesTriangles, this.segments2, this.indexArray2, subdivided.indicesLineList);
}
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {
imagePositions,
canonical
});
}
};
register("FillBucket", FillBucket, { omit: ["layers", "patternFeatures"] });
//#endregion
//#region src/style/style_layer/fill_style_layer_properties.g.ts
let layout$4;
const getLayout$3 = () => layout$4 = layout$4 || new Properties({ "fill-sort-key": new DataDrivenProperty(latest["layout_fill"]["fill-sort-key"], "fill-sort-key") });
let paint$4;
const getPaint$4 = () => paint$4 = paint$4 || new Properties({
"fill-antialias": new DataConstantProperty(latest["paint_fill"]["fill-antialias"], "fill-antialias"),
"fill-opacity": new DataDrivenProperty(latest["paint_fill"]["fill-opacity"], "fill-opacity"),
"fill-layer-opacity": new DataConstantProperty(latest["paint_fill"]["fill-layer-opacity"], "fill-layer-opacity"),
"fill-color": new DataDrivenProperty(latest["paint_fill"]["fill-color"], "fill-color"),
"fill-outline-color": new DataDrivenProperty(latest["paint_fill"]["fill-outline-color"], "fill-outline-color"),
"fill-translate": new DataConstantProperty(latest["paint_fill"]["fill-translate"], "fill-translate"),
"fill-translate-anchor": new DataConstantProperty(latest["paint_fill"]["fill-translate-anchor"], "fill-translate-anchor"),
"fill-pattern": new CrossFadedDataDrivenProperty(latest["paint_fill"]["fill-pattern"], "fill-pattern")
});
var fill_style_layer_properties_g_default = {
get paint() {
return getPaint$4();
},
get layout() {
return getLayout$3();
}
};
//#endregion
//#region src/style/style_layer/fill_style_layer.ts
const isFillStyleLayer = (layer) => layer.type === "fill";
var FillStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, fill_style_layer_properties_g_default, globalState);
}
recalculate(parameters, availableImages) {
super.recalculate(parameters, availableImages);
const outlineColor = this.paint._values["fill-outline-color"];
if (outlineColor.value.kind === "constant" && outlineColor.value.value === void 0) this.paint._values["fill-outline-color"] = this.paint._values["fill-color"];
}
createBucket(parameters) {
return new FillBucket(parameters);
}
queryRadius() {
return translateDistance(this.paint.get("fill-translate"));
}
queryIntersectsFeature({ queryGeometry, geometry, transform, pixelsToTileUnits }) {
return polygonIntersectsMultiPolygon(translate(queryGeometry, this.paint.get("fill-translate"), this.paint.get("fill-translate-anchor"), -transform.bearingInRadians, pixelsToTileUnits), geometry);
}
isTileClipped() {
return true;
}
};
//#endregion
//#region src/data/bucket/fill_extrusion_attributes.ts
const layout$3 = createLayout([{
name: "a_pos",
components: 2,
type: "Int16"
}, {
name: "a_normal_ed",
components: 4,
type: "Int16"
}], 4);
const centroidAttributes = createLayout([{
name: "a_centroid",
components: 2,
type: "Int16"
}], 4);
const members$2 = layout$3.members;
layout$3.size;
layout$3.alignment;
//#endregion
//#region src/geo/bounds.ts
/** A 2-d bounding box covering an X and Y range. */
var Bounds = class Bounds {
constructor() {
this.minX = Infinity;
this.maxX = -Infinity;
this.minY = Infinity;
this.maxY = -Infinity;
}
/**
* Expands this bounding box to include point.
*
* @param point - The point to include in this bounding box
* @returns This mutated bounding box
*/
extend(point) {
this.minX = Math.min(this.minX, point.x);
this.minY = Math.min(this.minY, point.y);
this.maxX = Math.max(this.maxX, point.x);
this.maxY = Math.max(this.maxY, point.y);
return this;
}
/**
* Expands this bounding box by a fixed amount in each direction.
*
* @param amount - The amount to expand the box by, or contract if negative
* @returns This mutated bounding box
*/
expandBy(amount) {
this.minX -= amount;
this.minY -= amount;
this.maxX += amount;
this.maxY += amount;
if (this.minX > this.maxX || this.minY > this.maxY) {
this.minX = Infinity;
this.maxX = -Infinity;
this.minY = Infinity;
this.maxY = -Infinity;
}
return this;
}
/**
* Shrinks this bounding box by a fixed amount in each direction.
*
* @param amount - The amount to shrink the box by
* @returns This mutated bounding box
*/
shrinkBy(amount) {
return this.expandBy(-amount);
}
/**
* Returns a new bounding box that contains all of the corners of this bounding
* box with a transform applied. Does not modify this bounding box.
*
* @param fn - The function to apply to each corner
* @returns A new bounding box containing all of the mapped points.
*/
map(fn) {
const result = new Bounds();
result.extend(fn(new Point(this.minX, this.minY)));
result.extend(fn(new Point(this.maxX, this.minY)));
result.extend(fn(new Point(this.minX, this.maxY)));
result.extend(fn(new Point(this.maxX, this.maxY)));
return result;
}
/**
* Creates a new bounding box that includes all points provided.
*
* @param points - The points to include inside the bounding box
* @returns The new bounding box
*/
static fromPoints(points) {
const result = new Bounds();
for (const p of points) result.extend(p);
return result;
}
contains(point) {
return point.x >= this.minX && point.x <= this.maxX && point.y >= this.minY && point.y <= this.maxY;
}
empty() {
return this.minX > this.maxX;
}
width() {
return this.maxX - this.minX;
}
height() {
return this.maxY - this.minY;
}
covers(other) {
return !this.empty() && !other.empty() && other.minX >= this.minX && other.maxX <= this.maxX && other.minY >= this.minY && other.maxY <= this.maxY;
}
intersects(other) {
return !this.empty() && !other.empty() && other.minX <= this.maxX && other.maxX >= this.minX && other.minY <= this.maxY && other.maxY >= this.minY;
}
};
//#endregion
//#region src/data/extent_bounds.ts
/**
* The bounding box covering the entire extent of a tile.
*/
const EXTENT_BOUNDS = Bounds.fromPoints([new Point(0, 0), new Point(EXTENT$1, EXTENT$1)]);
/**
* Whether an edge runs along one of the lines the tile was cut out of the world at. Clipping happens
* on the buffer rectangle, outside the tile, so an axis parallel edge out there belongs to the cut
* rather than to the feature. Such an edge is shared with the neighbouring tile, which cuts the same
* feature somewhere else, so geometry derived from it has to stay identical in both tiles.
* @param p1 - First vertex of the edge
* @param p2 - Second vertex of the edge
* @returns True when the edge lies on a clip line.
*/
function isBoundaryEdge(p1, p2) {
return p1.x === p2.x && (p1.x < 0 || p1.x > 8192) || p1.y === p2.y && (p1.y < 0 || p1.y > 8192);
}
/**
* Whether a ring lies completely off one side of the tile, and so cannot contribute a visible pixel.
* @param ring - Ring to test
* @returns True when every vertex is beyond the same edge of the tile.
*/
function isEntirelyOutside(ring) {
return ring.every((p) => p.x < 0) || ring.every((p) => p.x > 8192) || ring.every((p) => p.y < 0) || ring.every((p) => p.y > 8192);
}
//#endregion
//#region node_modules/@mapbox/vector-tile/index.js
/** @import {PbfReader} from 'pbf' */
/** @import {Feature} from 'geojson' */
var VectorTileFeature = class {
/**
* @param {PbfReader} pbf
* @param {number} end
* @param {number} extent
* @param {string[]} keys
* @param {(number | string | boolean)[]} values
*/
constructor(pbf, end, extent, keys, values) {
/** @type {Record<string, number | string | boolean>} */
this.properties = Object.create(null);
this.extent = extent;
/** @type {0 | 1 | 2 | 3} */
this.type = 0;
/** @type {number | undefined} */
this.id = void 0;
/** @private */
this._pbf = pbf;
/** @private */
this._geometry = -1;
/** @private */
this._keys = keys;
/** @private */
this._values = values;
while (pbf.pos < end) {
const tag = pbf.readVarint();
if (tag === 8) this.id = pbf.readVarint();
else if (tag === 18) {
const tagsEnd = pbf.readVarint() + pbf.pos;
while (pbf.pos < tagsEnd) {
const key = keys[pbf.readVarint()];
const value = values[pbf.readVarint()];
this.properties[key] = value;
}
} else if (tag === 24) this.type = pbf.readVarint();
else if (tag === 34) {
this._geometry = pbf.pos;
pbf.skip(tag);
} else pbf.skip(tag);
}
}
loadGeometry() {
if (this._geometry < 0) throw new Error("feature has no geometry");
const pbf = this._pbf;
pbf.pos = this._geometry;
const end = pbf.readVarint() + pbf.pos;
/** @type Point[][] */
const lines = [];
/** @type Point[] | undefined */
let line;
let cmd = 1;
let length = 0;
let x = 0;
let y = 0;
while (pbf.pos < end) {
if (length <= 0) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 7;
length = cmdLen >> 3;
if (length === 0) continue;
}
length--;
if (cmd === 1) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (line) lines.push(line);
line = [new Point(x, y)];
} else if (cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (line) line.push(new Point(x, y));
} else if (cmd === 7) {
if (line) line.push(line[0].clone());
} else throw new Error(`unknown command ${cmd}`);
}
if (line) lines.push(line);
return lines;
}
bbox() {
if (this._geometry < 0) throw new Error("feature has no geometry");
const pbf = this._pbf;
pbf.pos = this._geometry;
const end = pbf.readVarint() + pbf.pos;
let cmd = 1, length = 0, x = 0, y = 0, x1 = Infinity, x2 = -Infinity, y1 = Infinity, y2 = -Infinity;
while (pbf.pos < end) {
if (length <= 0) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 7;
length = cmdLen >> 3;
if (length === 0) continue;
}
length--;
if (cmd === 1 || cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (x < x1) x1 = x;
if (x > x2) x2 = x;
if (y < y1) y1 = y;
if (y > y2) y2 = y;
} else if (cmd !== 7) throw new Error(`unknown command ${cmd}`);
}
return [
x1,
y1,
x2,
y2
];
}
/**
* @param {number} x
* @param {number} y
* @param {number} z
* @return {Feature}
*/
toGeoJSON(x, y, z) {
const size = this.extent * Math.pow(2, z), x0 = this.extent * x, y0 = this.extent * y, vtCoords = this.loadGeometry();
/** @param {Point} p */
function projectPoint(p) {
return [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp((1 - (p.y + y0) * 2 / size) * Math.PI)) - 90];
}
/** @param {Point[]} line */
function projectLine(line) {
return line.map(projectPoint);
}
/** @type {Feature["geometry"]} */
let geometry;
if (this.type === 1) {
const points = [];
for (const line of vtCoords) points.push(line[0]);
const coordinates = projectLine(points);
geometry = points.length === 1 ? {
type: "Point",
coordinates: coordinates[0]
} : {
type: "MultiPoint",
coordinates
};
} else if (this.type === 2) {
const coordinates = vtCoords.map(projectLine);
geometry = coordinates.length === 1 ? {
type: "LineString",
coordinates: coordinates[0]
} : {
type: "MultiLineString",
coordinates
};
} else if (this.type === 3) {
const polygons = classifyRings(vtCoords);
const coordinates = [];
for (const polygon of polygons) coordinates.push(polygon.map(projectLine));
geometry = coordinates.length === 1 ? {
type: "Polygon",
coordinates: coordinates[0]
} : {
type: "MultiPolygon",
coordinates
};
} else throw new Error("unknown feature type");
/** @type {Feature} */
const result = {
type: "Feature",
geometry,
properties: this.properties
};
if (this.id != null) result.id = this.id;
return result;
}
};
/** @type {['Unknown', 'Point', 'LineString', 'Polygon']} */
VectorTileFeature.types = [
"Unknown",
"Point",
"LineString",
"Polygon"
];
/** classifies an array of rings into polygons with outer rings and holes
* @param {Point[][]} rings
*/
function classifyRings(rings) {
const len = rings.length;
if (len <= 1) return [rings];
const polygons = [];
let polygon, ccw;
for (let i = 0; i < len; i++) {
const area = signedArea(rings[i]);
if (area === 0) continue;
if (ccw === void 0) ccw = area < 0;
if (ccw === area < 0) {
if (polygon) polygons.push(polygon);
polygon = [rings[i]];
} else if (polygon) polygon.push(rings[i]);
}
if (polygon) polygons.push(polygon);
return polygons;
}
/** @param {Point[]} ring */
function signedArea(ring) {
let sum = 0;
for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
p1 = ring[i];
p2 = ring[j];
sum += (p2.x - p1.x) * (p1.y + p2.y);
}
return sum;
}
var VectorTileLayer = class {
/**
* @param {PbfReader} pbf
* @param {number} [end]
*/
constructor(pbf, end) {
this.version = 1;
this.name = "";
this.extent = 4096;
this.length = 0;
/** @private */
this._pbf = pbf;
/** @private
* @type {string[]} */
this._keys = [];
/** @private
* @type {(number | string | boolean)[]} */
this._values = [];
/** @private
* @type {number[]} */
this._features = [];
if (end === void 0) end = pbf.length;
while (pbf.pos < end) {
const tag = pbf.readVarint();
if (tag === 10) this.name = pbf.readString();
else if (tag === 18) {
this._features.push(pbf.pos);
pbf.skip(tag);
} else if (tag === 26) this._keys.push(pbf.readString());
else if (tag === 34) this._values.push(readValueMessage(pbf));
else if (tag === 40) this.extent = pbf.readVarint();
else if (tag === 120) this.version = pbf.readVarint();
else pbf.skip(tag);
}
this.length = this._features.length;
}
/** return feature `i` from this layer as a `VectorTileFeature`
* @param {number} i
*/
feature(i) {
if (i < 0 || i >= this._features.length) throw new Error("feature index out of bounds");
this._pbf.pos = this._features[i];
const end = this._pbf.readVarint() + this._pbf.pos;
return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values);
}
};
/**
* @param {PbfReader} pbf
*/
function readValueMessage(pbf) {
let value = null;
const end = pbf.readVarint() + pbf.pos;
while (pbf.pos < end) {
const tag = pbf.readVarint();
value = tag === 10 ? pbf.readString() : tag === 21 ? pbf.readFloat() : tag === 25 ? pbf.readDouble() : tag === 32 ? pbf.readVarint(true) : tag === 40 ? pbf.readVarint() : tag === 48 ? pbf.readSVarint() : tag === 56 ? pbf.readBoolean() : (pbf.skip(tag), null);
}
if (value == null) throw new Error("unknown feature value");
return value;
}
var VectorTile = class {
/**
* @param {PbfReader} pbf
* @param {number} [end]
*/
constructor(pbf, end = pbf.length) {
/** @type {Record<string, VectorTileLayer>} */
const layers = Object.create(null);
while (pbf.pos < end) {
const tag = pbf.readVarint();
if (tag === 26) {
const layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);
if (layer.length) layers[layer.name] = layer;
} else pbf.skip(tag);
}
this.layers = layers;
}
};
//#endregion
//#region src/geo/lng_lat.ts
const earthRadius = 6371008.8;
/**
* A `LngLat` object represents a given longitude and latitude coordinate, measured in degrees.
* These coordinates are based on the [WGS84 (EPSG:4326) standard](https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84).
*
* MapLibre GL JS uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match the
* [GeoJSON specification](https://tools.ietf.org/html/rfc7946).
*
* Note that any MapLibre GL JS method that accepts a `LngLat` object as an argument or option
* can also accept an `Array` of two numbers and will perform an implicit conversion.
* This flexible type is documented as {@link LngLatLike}.
*
* @group Geography and Geometry
*
* @example
* ```ts
* let ll = new LngLat(-123.9749, 40.7736);
* ll.lng; // = -123.9749
* ```
* @see [Get coordinates of the mouse pointer](https://maplibre.org/maplibre-gl-js/docs/examples/get-coordinates-of-the-mouse-pointer/)
*/
var LngLat = class LngLat {
/**
* @param lng - Longitude, measured in degrees.
* @param lat - Latitude, measured in degrees.
*/
constructor(lng, lat) {
if (isNaN(lng) || isNaN(lat)) throw new Error(`Invalid LngLat object: (${lng}, ${lat})`);
this.lng = +lng;
this.lat = +lat;
if (this.lat > 90 || this.lat < -90) throw new Error("Invalid LngLat latitude value: must be between -90 and 90");
}
/**
* Returns a new `LngLat` object whose longitude is wrapped to the range (-180, 180).
*
* @returns The wrapped `LngLat` object.
* @example
* ```ts
* let ll = new LngLat(286.0251, 40.7736);
* let wrapped = ll.wrap();
* wrapped.lng; // = -73.9749
* ```
*/
wrap() {
return new LngLat(wrap$1(this.lng, -180, 180), this.lat);
}
/**
* Returns the coordinates represented as an array of two numbers.
*
* @returns The coordinates represented as an array of longitude and latitude.
* @example
* ```ts
* let ll = new LngLat(-73.9749, 40.7736);
* ll.toArray(); // = [-73.9749, 40.7736]
* ```
*/
toArray() {
return [this.lng, this.lat];
}
/**
* Returns the coordinates represent as a string.
*
* @returns The coordinates represented as a string of the format `'LngLat(lng, lat)'`.
* @example
* ```ts
* let ll = new LngLat(-73.9749, 40.7736);
* ll.toString(); // = "LngLat(-73.9749, 40.7736)"
* ```
*/
toString() {
return `LngLat(${this.lng}, ${this.lat})`;
}
/**
* Returns the approximate distance between a pair of coordinates in meters
* Uses the Haversine Formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159)
*
* @param lngLat - coordinates to compute the distance to
* @returns Distance in meters between the two coordinates.
* @example
* ```ts
* let new_york = new LngLat(-74.0060, 40.7128);
* let los_angeles = new LngLat(-118.2437, 34.0522);
* new_york.distanceTo(los_angeles); // = 3935751.690893987, "true distance" using a non-spherical approximation is ~3966km
* ```
*/
distanceTo(lngLat) {
const rad = Math.PI / 180;
const lat1 = this.lat * rad;
const lat2 = lngLat.lat * rad;
const a = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos((lngLat.lng - this.lng) * rad);
return earthRadius * Math.acos(Math.min(a, 1));
}
/**
* Converts an array of two numbers or an object with `lng` and `lat` or `lon` and `lat` properties
* to a `LngLat` object.
*
* If a `LngLat` object is passed in, the function returns it unchanged.
*
* @param input - An array of two numbers or object to convert, or a `LngLat` object to return.
* @returns A new `LngLat` object, if a conversion occurred, or the original `LngLat` object.
* @example
* ```ts
* let arr = [-73.9749, 40.7736];
* let ll = LngLat.convert(arr);
* ll; // = LngLat {lng: -73.9749, lat: 40.7736}
* ```
*/
static convert(input) {
if (input instanceof LngLat) return input;
if (Array.isArray(input) && (input.length === 2 || input.length === 3)) return new LngLat(Number(input[0]), Number(input[1]));
if (!Array.isArray(input) && typeof input === "object" && input !== null) return new LngLat(Number("lng" in input ? input.lng : input.lon), Number(input.lat));
throw new Error("`LngLatLike` argument must be specified as a LngLat instance, an object {lng: <lng>, lat: <lat>}, an object {lon: <lng>, lat: <lat>}, or an array of [<lng>, <lat>]");
}
};
//#endregion
//#region src/geo/mercator_coordinate.ts
const earthCircumference = 2 * Math.PI * earthRadius;
function circumferenceAtLatitude(latitude) {
return earthCircumference * Math.cos(latitude * Math.PI / 180);
}
function mercatorXfromLng(lng) {
return (180 + lng) / 360;
}
function mercatorYfromLat(lat) {
return (180 - 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360))) / 360;
}
function mercatorZfromAltitude(altitude, lat) {
return altitude / circumferenceAtLatitude(lat);
}
function lngFromMercatorX(x) {
return x * 360 - 180;
}
function latFromMercatorY(y) {
const y2 = 180 - y * 360;
return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
}
function altitudeFromMercatorZ(z, y) {
return z * circumferenceAtLatitude(latFromMercatorY(y));
}
/**
* Determine the Mercator scale factor for a given latitude, see
* https://en.wikipedia.org/wiki/Mercator_projection#Scale_factor
*
* At the equator the scale factor will be 1, which increases at higher latitudes.
*
* @param lat - Latitude
* @returns scale factor
*/
function mercatorScale(lat) {
return 1 / Math.cos(lat * Math.PI / 180);
}
/**
* A `MercatorCoordinate` object represents a projected three dimensional position.
*
* `MercatorCoordinate` uses the web mercator projection ([EPSG:3857](https://epsg.io/3857)) with slightly different units:
*
* - the size of 1 unit is the width of the projected world instead of the "mercator meter"
* - the origin of the coordinate space is at the north-west corner instead of the middle
*
* For example, `MercatorCoordinate(0, 0, 0)` is the north-west corner of the mercator world and
* `MercatorCoordinate(1, 1, 0)` is the south-east corner. If you are familiar with
* [vector tiles](https://github.com/mapbox/vector-tile-spec) it may be helpful to think
* of the coordinate space as the `0/0/0` tile with an extent of `1`.
*
* The `z` dimension of `MercatorCoordinate` is conformal. A cube in the mercator coordinate space would be rendered as a cube.
*
* @group Geography and Geometry
*
* @example
* ```ts
* let nullIsland = new MercatorCoordinate(0.5, 0.5, 0);
* ```
* @see [Add a custom style layer](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-custom-style-layer/)
* @see [Add a 3D model using three.js](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-3d-model-using-threejs/)
* @see [Add a simple custom layer on a globe](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-simple-custom-layer-on-a-globe/)
*/
var MercatorCoordinate = class MercatorCoordinate {
/**
* @param x - The x component of the position.
* @param y - The y component of the position.
* @param z - The z component of the position.
*/
constructor(x, y, z = 0) {
this.x = +x;
this.y = +y;
this.z = +z;
}
/**
* Project a `LngLat` to a `MercatorCoordinate`.
*
* @param lngLatLike - The location to project.
* @param altitude - The altitude in meters of the position.
* @returns The projected mercator coordinate.
* @example
* ```ts
* let coord = MercatorCoordinate.fromLngLat({ lng: 0, lat: 0}, 0);
* coord; // MercatorCoordinate(0.5, 0.5, 0)
* ```
*/
static fromLngLat(lngLatLike, altitude = 0) {
const lngLat = LngLat.convert(lngLatLike);
return new MercatorCoordinate(mercatorXfromLng(lngLat.lng), mercatorYfromLat(lngLat.lat), mercatorZfromAltitude(altitude, lngLat.lat));
}
/**
* Returns the `LngLat` for the coordinate.
*
* @returns The `LngLat` object.
* @example
* ```ts
* let coord = new MercatorCoordinate(0.5, 0.5, 0);
* let lngLat = coord.toLngLat(); // LngLat(0, 0)
* ```
*/
toLngLat() {
return new LngLat(lngFromMercatorX(this.x), latFromMercatorY(this.y));
}
/**
* Returns the altitude in meters of the coordinate.
*
* @returns The altitude in meters.
* @example
* ```ts
* let coord = new MercatorCoordinate(0, 0, 0.02);
* coord.toAltitude(); // 6914.281956295339
* ```
*/
toAltitude() {
return altitudeFromMercatorZ(this.z, this.y);
}
/**
* Returns the distance of 1 meter in `MercatorCoordinate` units at this latitude.
*
* For coordinates in real world units using meters, this naturally provides the scale
* to transform into `MercatorCoordinate`s.
*
* @returns Distance of 1 meter in `MercatorCoordinate` units.
*/
meterInMercatorCoordinateUnits() {
return 1 / earthCircumference * mercatorScale(latFromMercatorY(this.y));
}
};
//#endregion
//#region src/geo/projection/mercator_utils.ts
const maxMercatorHorizonAngle = 89.25;
/**
* Returns mercator coordinates in range 0..1 for given coordinates inside a specified tile.
* @param inTileX - X coordinate in tile units - range [0..EXTENT].
* @param inTileY - Y coordinate in tile units - range [0..EXTENT].
* @param canonicalTileID - Tile canonical ID - mercator X, Y and zoom.
* @returns Mercator coordinates of the specified point in range [0..1].
*/
function tileCoordinatesToMercatorCoordinates(inTileX, inTileY, canonicalTileID) {
const scale = 1 / (1 << canonicalTileID.z);
return new MercatorCoordinate(inTileX / EXTENT$1 * scale + canonicalTileID.x * scale, inTileY / EXTENT$1 * scale + canonicalTileID.y * scale);
}
/**
* Returns LngLat for given in-tile coordinates and tile ID.
* @param inTileX - X coordinate in tile units - range [0..EXTENT].
* @param inTileY - Y coordinate in tile units - range [0..EXTENT].
* @param canonicalTileID - Tile canonical ID - mercator X, Y and zoom.
*/
function tileCoordinatesToLocation(inTileX, inTileY, canonicalTileID) {
return tileCoordinatesToMercatorCoordinates(inTileX, inTileY, canonicalTileID).toLngLat();
}
/**
* Convert from LngLat to world coordinates (Mercator coordinates scaled by world size).
* @param worldSize - Mercator world size computed from zoom level and tile size.
* @param lnglat - The location to convert.
* @returns Point
*/
function projectToWorldCoordinates(worldSize, lnglat) {
const lat = clamp$2(lnglat.lat, -85.051129, MAX_VALID_LATITUDE);
return new Point(mercatorXfromLng(lnglat.lng) * worldSize, mercatorYfromLat(lat) * worldSize);
}
/**
* Convert from world coordinates (mercator coordinates scaled by world size) to LngLat.
* @param worldSize - Mercator world size computed from zoom level and tile size.
* @param point - World coordinate.
* @returns LngLat
*/
function unprojectFromWorldCoordinates(worldSize, point) {
return new MercatorCoordinate(point.x / worldSize, point.y / worldSize).toLngLat();
}
/**
* Calculate pixel height of the visible horizon in relation to map-center (e.g. height/2),
* multiplied by a static factor to simulate the earth-radius.
* The calculated value is the horizontal line from the camera-height to sea-level.
* @returns Horizon above center in pixels.
*/
function getMercatorHorizon(transform) {
return transform.cameraToCenterDistance * Math.min(Math.tan(degreesToRadians(90 - transform.pitch)) * .85, Math.tan(degreesToRadians(maxMercatorHorizonAngle - transform.pitch)));
}
function calculateTileMatrix(unwrappedTileID, worldSize) {
const canonical = unwrappedTileID.canonical;
const scale = worldSize / zoomScale(canonical.z);
const unwrappedX = canonical.x + Math.pow(2, canonical.z) * unwrappedTileID.wrap;
const worldMatrix = /* @__PURE__ */ new Float64Array(16);
identity$2(worldMatrix);
translate$2(worldMatrix, worldMatrix, [
unwrappedX * scale,
canonical.y * scale,
0
]);
scale$5(worldMatrix, worldMatrix, [
scale / EXTENT$1,
scale / EXTENT$1,
1
]);
return worldMatrix;
}
function cameraMercatorCoordinateFromCenterAndRotation(center, elevation, pitch, bearing, distance) {
const centerMercator = MercatorCoordinate.fromLngLat(center, elevation);
const dMercator = distance * mercatorZfromAltitude(1, center.lat);
const { x, y, z } = cameraDirectionFromPitchBearing(pitch, bearing);
const dxMercator = dMercator * -x;
const dyMercator = dMercator * -y;
const dzMercator = dMercator * -z;
return new MercatorCoordinate(centerMercator.x + dxMercator, centerMercator.y + dyMercator, centerMercator.z + dzMercator);
}
function cameraDirectionFromPitchBearing(pitch, bearing) {
const pitchRadians = degreesToRadians(pitch);
const bearingRadians = degreesToRadians(bearing);
const z = Math.cos(-pitchRadians);
const h = Math.sin(pitchRadians);
return {
x: h * Math.sin(bearingRadians),
y: -h * Math.cos(bearingRadians),
z
};
}
//#endregion
//#region src/data/bucket/round_polygon_corners.ts
/**
* Rounds polygon corners by calculating arc points at each corner vertex.
* @param polygon - Collection of polygon rings (outer ring and hole rings)
* @param distanceInMeters - Desired corner rounding distance in meters
* @param canonical - Canonical tile ID used for meter to tile unit conversion
*/
function roundPolygonCorners(polygon, distanceInMeters, canonical) {
if (distanceInMeters <= 0 || !polygon || polygon.length === 0) return polygon;
const distanceInTileUnits = getTileUnitsForMeters(distanceInMeters, canonical);
return polygon.map((ring) => roundRing(ring, distanceInTileUnits));
}
function getTileUnitsForMeters(distanceInMeters, canonical) {
const centerLocation = tileCoordinatesToLocation(EXTENT$1 / 2, EXTENT$1 / 2, canonical);
const meterInMercator = MercatorCoordinate.fromLngLat(centerLocation).meterInMercatorCoordinateUnits();
const tileUnitsPerMercator = (1 << canonical.z) * EXTENT$1;
return distanceInMeters * meterInMercator * tileUnitsPerMercator;
}
/**
* Rounds the corners of a single ring.
*
* Corners that tile clipping created are left sharp: they belong to the cut rather than to the
* feature, and the neighbouring tile cuts the same feature elsewhere, so rounding them would leave
* the two halves out of step. Every vertex ends up on the integer tile grid, because triangulation,
* subdivision and the vertex buffers snap and deduplicate vertices there - arcs finer than a tile
* unit would otherwise be merged only after they were triangulated, turning the mesh into spikes.
*
* A ring that collapses into fewer than three distinct vertices is returned unchanged.
* @param ring - Ring to round, closed or open
* @param distanceInTileUnits - Corner rounding distance, already converted to tile units
*/
function roundRing(ring, distanceInTileUnits) {
if (!ring || ring.length < 3) return ring;
const isClosed = ring[0].x === ring[ring.length - 1].x && ring[0].y === ring[ring.length - 1].y;
const vertexCount = isClosed ? ring.length - 1 : ring.length;
if (vertexCount < 3) return ring;
const newRing = [];
for (let i = 0; i < vertexCount; i++) {
const previous = ring[(i - 1 + vertexCount) % vertexCount];
const current = ring[i];
const next = ring[(i + 1) % vertexCount];
if (isBoundaryEdge(previous, current) || isBoundaryEdge(current, next)) {
newRing.push(current.clone());
continue;
}
appendRoundCorner(newRing, previous, current, next, distanceInTileUnits);
}
const snapped = snapToIntegerGrid(newRing);
if (snapped.length < 3) return ring;
if (isClosed) snapped.push(snapped[0].clone());
return snapped;
}
/**
* Rounds every vertex to the integer tile grid, dropping vertices that collapse onto their neighbour.
* The ring is treated as closed, so the wrap-around duplicate is dropped as well.
* @param ring - Ring to snap
*/
function snapToIntegerGrid(ring) {
const snapped = [];
for (const p of ring) {
const point = p.round();
const previous = snapped[snapped.length - 1];
if (previous?.x === point.x && previous?.y === point.y) continue;
snapped.push(point);
}
while (snapped.length > 1 && snapped[0].x === snapped[snapped.length - 1].x && snapped[0].y === snapped[snapped.length - 1].y) snapped.pop();
return snapped;
}
/**
* Appends the arc that replaces one corner, or the corner itself when it is too shallow or too sharp
* to round.
* @param newRing - Ring being built, the arc points are appended to it
* @param prev - Vertex before the corner
* @param current - The corner
* @param next - Vertex after the corner
* @param distanceInTileUnits - Corner rounding distance, already converted to tile units
*/
function appendRoundCorner(newRing, prev, current, next, distanceInTileUnits) {
const ua = prev.sub(current);
const ub = next.sub(current);
const lenA = ua.mag();
const lenB = ub.mag();
if (lenA < 1e-6 || lenB < 1e-6) {
newRing.push(current.clone());
return;
}
ua._div(lenA);
ub._div(lenB);
const dot = ua.x * ub.x + ua.y * ub.y;
if (Math.abs(dot) > Math.cos(5 * Math.PI / 180)) {
newRing.push(current.clone());
return;
}
const maxEdgeLenPercent = .2;
const r = Math.min(distanceInTileUnits, lenA * maxEdgeLenPercent, lenB * maxEdgeLenPercent);
const tangentA = current.add(ua.mult(r));
const tangentB = current.add(ub.mult(r));
const cosHalfTheta = Math.sqrt((1 + dot) / 2);
const center = current.add(ua.add(ub)._unit()._mult(r / cosHalfTheta));
const sweepAngle = tangentA.sub(center).angleWith(tangentB.sub(center));
const numSegments = Math.max(2, Math.ceil(Math.abs(sweepAngle) / (Math.PI / 6) - 1e-6));
for (let s = 0; s <= numSegments; s++) newRing.push(tangentA.rotateAround(sweepAngle * (s / numSegments), center));
}
//#endregion
//#region src/data/bucket/fill_extrusion_bucket.ts
const EARCUT_MAX_RINGS = 500;
const FACTOR = Math.pow(2, 13);
function addVertex$1(vertexArray, x, y, nx, ny, nz, t, e) {
vertexArray.emplaceBack(x, y, Math.floor(nx * FACTOR) * 2 + t, ny * FACTOR * 2, nz * FACTOR * 2, Math.round(e));
}
var FillExtrusionBucket = class {
constructor(options) {
this.zoom = options.zoom;
this.overscaling = options.overscaling;
this.layers = options.layers;
this.layerIds = this.layers.map((layer) => layer.id);
this.index = options.index;
this.hasDependencies = false;
this.layoutVertexArray = new FillExtrusionLayoutArray();
this.centroidVertexArray = new PosArray();
this.indexArray = new TriangleIndexArray();
this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom);
this.segments = new SegmentVector();
this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id);
}
populate(features, options, canonical) {
this.features = [];
this.hasDependencies = hasPattern("fill-extrusion", this.layers, options);
const globalProperties = new EvaluationParameters(this.zoom);
const layer = this.layers[0];
const roundedCornerDistance = layer.layout.get("fill-extrusion-rounded-corner-distance");
const needGeometry = layer._featureFilter.needGeometry;
for (const { feature, id, index, sourceLayerIndex } of features) {
const evaluationFeature = toEvaluationFeature(feature, needGeometry);
if (!layer._featureFilter.filter(globalProperties, evaluationFeature, canonical)) continue;
const rawGeometry = needGeometry ? evaluationFeature.geometry : loadGeometry(feature);
const bucketFeature = {
id,
sourceLayerIndex,
index,
geometry: roundedCornerDistance > 0 ? roundPolygonCorners(rawGeometry, roundedCornerDistance, canonical) : rawGeometry,
properties: feature.properties,
type: feature.type,
patterns: {}
};
if (this.hasDependencies) this.features.push(addPatternDependencies("fill-extrusion", this.layers, bucketFeature, { zoom: this.zoom }, options));
else this.addFeature(bucketFeature, bucketFeature.geometry, index, canonical, {}, options.subdivisionGranularity);
options.featureIndex.insert(feature, bucketFeature.geometry, index, sourceLayerIndex, this.index, true);
}
}
addFeatures(options, canonical, imagePositions) {
for (const feature of this.features) {
const { geometry } = feature;
this.addFeature(feature, geometry, feature.index, canonical, imagePositions, options.subdivisionGranularity);
}
}
update(states, vtLayer, imagePositions) {
if (!this.stateDependentLayers.length) return;
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, { imagePositions });
}
isEmpty() {
return this.layoutVertexArray.length === 0 && this.centroidVertexArray.length === 0;
}
uploadPending() {
return !this.uploaded || this.programConfigurations.needsUpload;
}
upload(context) {
if (!this.uploaded) {
this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, members$2);
this.centroidVertexBuffer = context.createVertexBuffer(this.centroidVertexArray, centroidAttributes.members, true);
this.indexBuffer = context.createIndexBuffer(this.indexArray);
}
this.programConfigurations.upload(context);
this.uploaded = true;
}
destroy() {
if (!this.layoutVertexBuffer) return;
this.layoutVertexBuffer.destroy();
this.indexBuffer.destroy();
this.programConfigurations.destroy();
this.segments.destroy();
this.centroidVertexBuffer.destroy();
}
addFeature(feature, geometry, index, canonical, imagePositions, subdivisionGranularity) {
const layer = this.layers[0];
const roundedCornerDistance = layer.layout ? layer.layout.get("fill-extrusion-rounded-corner-distance") : 0;
const processedGeometry = roundedCornerDistance > 0 ? roundPolygonCorners(geometry, roundedCornerDistance, canonical) : geometry;
for (const polygon of classifyRings$1(processedGeometry, EARCUT_MAX_RINGS)) {
const centroid = {
x: 0,
y: 0,
sampleCount: 0
};
const oldVertexCount = this.layoutVertexArray.length;
this.processPolygon(centroid, canonical, feature, polygon, subdivisionGranularity);
const addedVertices = this.layoutVertexArray.length - oldVertexCount;
const centroidX = Math.floor(centroid.x / centroid.sampleCount);
const centroidY = Math.floor(centroid.y / centroid.sampleCount);
for (let i = 0; i < addedVertices; i++) this.centroidVertexArray.emplaceBack(centroidX, centroidY);
}
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {
imagePositions,
canonical
});
}
processPolygon(centroid, canonical, feature, polygon, subdivisionGranularity) {
if (polygon.length < 1) return;
if (isEntirelyOutside(polygon[0])) return;
for (const ring of polygon) {
if (ring.length === 0) continue;
accumulatePointsToCentroid(centroid, ring);
}
const segmentReference = { segment: this.segments.prepareSegment(4, this.layoutVertexArray, this.indexArray) };
const granularity = subdivisionGranularity.fill.getGranularityForZoomLevel(canonical.z);
const isPolygon = VectorTileFeature.types[feature.type] === "Polygon";
for (const ring of polygon) {
if (ring.length === 0) continue;
if (isEntirelyOutside(ring)) continue;
const subdividedRing = subdivideVertexLine(ring, granularity, isPolygon);
this._generateSideFaces(subdividedRing, segmentReference);
}
if (!isPolygon) return;
const subdividedPolygon = subdividePolygon(polygon, canonical, granularity, false);
const vertexArray = this.layoutVertexArray;
fillLargeMeshArrays((x, y) => {
addVertex$1(vertexArray, x, y, 0, 0, 1, 1, 0);
}, this.segments, this.layoutVertexArray, this.indexArray, subdividedPolygon.verticesFlattened, subdividedPolygon.indicesTriangles);
}
/**
* Generates side faces for the supplied geometry. Assumes `geometry` to be a line string, like the output of {@link subdivideVertexLine}.
* For rings, it is assumed that the first and last vertex of `geometry` are equal.
*/
_generateSideFaces(geometry, segmentReference) {
let edgeDistance = 0;
for (let p = 1; p < geometry.length; p++) {
const p1 = geometry[p];
const p2 = geometry[p - 1];
if (isBoundaryEdge(p1, p2)) continue;
if (segmentReference.segment.vertexLength + 4 > SegmentVector.MAX_VERTEX_ARRAY_LENGTH) segmentReference.segment = this.segments.prepareSegment(4, this.layoutVertexArray, this.indexArray);
const perp = p1.sub(p2)._perp()._unit();
const dist = p2.dist(p1);
if (edgeDistance + dist > 32768) edgeDistance = 0;
addVertex$1(this.layoutVertexArray, p1.x, p1.y, perp.x, perp.y, 0, 0, edgeDistance);
addVertex$1(this.layoutVertexArray, p1.x, p1.y, perp.x, perp.y, 0, 1, edgeDistance);
edgeDistance += dist;
addVertex$1(this.layoutVertexArray, p2.x, p2.y, perp.x, perp.y, 0, 0, edgeDistance);
addVertex$1(this.layoutVertexArray, p2.x, p2.y, perp.x, perp.y, 0, 1, edgeDistance);
const bottomRight = segmentReference.segment.vertexLength;
this.indexArray.emplaceBack(bottomRight, bottomRight + 2, bottomRight + 1);
this.indexArray.emplaceBack(bottomRight + 1, bottomRight + 2, bottomRight + 3);
segmentReference.segment.vertexLength += 4;
segmentReference.segment.primitiveLength += 2;
}
}
};
/**
* Accumulates geometry to centroid. Geometry can be either a polygon ring, a line string or a closed line string.
* In case of a polygon ring or line ring, the last vertex is ignored if it is the same as the first vertex.
*/
function accumulatePointsToCentroid(centroid, geometry) {
for (let i = 0; i < geometry.length; i++) {
const p = geometry[i];
if (i === geometry.length - 1 && geometry[0].x === p.x && geometry[0].y === p.y) continue;
centroid.x += p.x;
centroid.y += p.y;
centroid.sampleCount++;
}
}
register("FillExtrusionBucket", FillExtrusionBucket, { omit: ["layers", "features"] });
//#endregion
//#region src/style/style_layer/fill_extrusion_style_layer_properties.g.ts
let layout$2;
const getLayout$2 = () => layout$2 = layout$2 || new Properties({ "fill-extrusion-rounded-corner-distance": new DataConstantProperty(latest["layout_fill-extrusion"]["fill-extrusion-rounded-corner-distance"], "fill-extrusion-rounded-corner-distance") });
let paint$3;
const getPaint$3 = () => paint$3 = paint$3 || new Properties({
"fill-extrusion-opacity": new DataConstantProperty(latest["paint_fill-extrusion"]["fill-extrusion-opacity"], "fill-extrusion-opacity"),
"fill-extrusion-color": new DataDrivenProperty(latest["paint_fill-extrusion"]["fill-extrusion-color"], "fill-extrusion-color"),
"fill-extrusion-translate": new DataConstantProperty(latest["paint_fill-extrusion"]["fill-extrusion-translate"], "fill-extrusion-translate"),
"fill-extrusion-translate-anchor": new DataConstantProperty(latest["paint_fill-extrusion"]["fill-extrusion-translate-anchor"], "fill-extrusion-translate-anchor"),
"fill-extrusion-pattern": new CrossFadedDataDrivenProperty(latest["paint_fill-extrusion"]["fill-extrusion-pattern"], "fill-extrusion-pattern"),
"fill-extrusion-height": new DataDrivenProperty(latest["paint_fill-extrusion"]["fill-extrusion-height"], "fill-extrusion-height"),
"fill-extrusion-base": new DataDrivenProperty(latest["paint_fill-extrusion"]["fill-extrusion-base"], "fill-extrusion-base"),
"fill-extrusion-vertical-gradient": new DataConstantProperty(latest["paint_fill-extrusion"]["fill-extrusion-vertical-gradient"], "fill-extrusion-vertical-gradient")
});
var fill_extrusion_style_layer_properties_g_default = {
get paint() {
return getPaint$3();
},
get layout() {
return getLayout$2();
}
};
const isFillExtrusionStyleLayer = (layer) => layer.type === "fill-extrusion";
var FillExtrusionStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, fill_extrusion_style_layer_properties_g_default, globalState);
}
createBucket(parameters) {
return new FillExtrusionBucket(parameters);
}
queryRadius() {
return translateDistance(this.paint.get("fill-extrusion-translate"));
}
is3D() {
return true;
}
queryIntersectsFeature({ queryGeometry, feature, featureState, geometry, transform, pixelsToTileUnits, pixelPosMatrix }) {
const translatedPolygon = translate(queryGeometry, this.paint.get("fill-extrusion-translate"), this.paint.get("fill-extrusion-translate-anchor"), -transform.bearingInRadians, pixelsToTileUnits);
const height = this.paint.get("fill-extrusion-height").evaluate(feature, featureState);
const base = this.paint.get("fill-extrusion-base").evaluate(feature, featureState);
const projectedQueryGeometry = projectQueryGeometry(translatedPolygon, pixelPosMatrix, 0);
const projected = projectExtrusion(geometry, base, height, pixelPosMatrix);
const projectedBase = projected[0];
const projectedTop = projected[1];
return checkIntersection(projectedBase, projectedTop, projectedQueryGeometry);
}
};
function dot(a, b) {
return a.x * b.x + a.y * b.y;
}
function getIntersectionDistance(projectedQueryGeometry, projectedFace) {
if (projectedQueryGeometry.length === 1) {
let i = 0;
const a = projectedFace[i++];
let b;
while (!b || a.equals(b)) {
b = projectedFace[i++];
if (!b) return Infinity;
}
for (; i < projectedFace.length; i++) {
const c = projectedFace[i];
const p = projectedQueryGeometry[0];
const ab = b.sub(a);
const ac = c.sub(a);
const ap = p.sub(a);
const dotABAB = dot(ab, ab);
const dotABAC = dot(ab, ac);
const dotACAC = dot(ac, ac);
const dotAPAB = dot(ap, ab);
const dotAPAC = dot(ap, ac);
const denom = dotABAB * dotACAC - dotABAC * dotABAC;
const v = (dotACAC * dotAPAB - dotABAC * dotAPAC) / denom;
const w = (dotABAB * dotAPAC - dotABAC * dotAPAB) / denom;
const u = 1 - v - w;
const distance = a.z * u + b.z * v + c.z * w;
if (isFinite(distance)) return distance;
}
return Infinity;
} else {
let closestDistance = Infinity;
for (const p of projectedFace) closestDistance = Math.min(closestDistance, p.z);
return closestDistance;
}
}
function checkIntersection(projectedBase, projectedTop, projectedQueryGeometry) {
let closestDistance = Infinity;
if (polygonIntersectsMultiPolygon(projectedQueryGeometry, projectedTop)) closestDistance = getIntersectionDistance(projectedQueryGeometry, projectedTop[0]);
for (let r = 0; r < projectedTop.length; r++) {
const ringTop = projectedTop[r];
const ringBase = projectedBase[r];
for (let p = 0; p < ringTop.length - 1; p++) {
const topA = ringTop[p];
const topB = ringTop[p + 1];
const baseA = ringBase[p];
const face = [
topA,
topB,
ringBase[p + 1],
baseA,
topA
];
if (polygonIntersectsPolygon(projectedQueryGeometry, face)) closestDistance = Math.min(closestDistance, getIntersectionDistance(projectedQueryGeometry, face));
}
}
return closestDistance === Infinity ? false : closestDistance;
}
function projectExtrusion(geometry, zBase, zTop, m) {
const projectedBase = [];
const projectedTop = [];
const baseXZ = m[8] * zBase;
const baseYZ = m[9] * zBase;
const baseZZ = m[10] * zBase;
const baseWZ = m[11] * zBase;
const topXZ = m[8] * zTop;
const topYZ = m[9] * zTop;
const topZZ = m[10] * zTop;
const topWZ = m[11] * zTop;
for (const r of geometry) {
const ringBase = [];
const ringTop = [];
for (const p of r) {
const x = p.x;
const y = p.y;
const sX = m[0] * x + m[4] * y + m[12];
const sY = m[1] * x + m[5] * y + m[13];
const sZ = m[2] * x + m[6] * y + m[14];
const sW = m[3] * x + m[7] * y + m[15];
const baseX = sX + baseXZ;
const baseY = sY + baseYZ;
const baseZ = sZ + baseZZ;
const baseW = sW + baseWZ;
const topX = sX + topXZ;
const topY = sY + topYZ;
const topZ = sZ + topZZ;
const topW = sW + topWZ;
const b = new Point(baseX / baseW, baseY / baseW);
b.z = baseZ / baseW;
ringBase.push(b);
const t = new Point(topX / topW, topY / topW);
t.z = topZ / topW;
ringTop.push(t);
}
projectedBase.push(ringBase);
projectedTop.push(ringTop);
}
return [projectedBase, projectedTop];
}
function projectQueryGeometry(queryGeometry, pixelPosMatrix, z) {
const projectedQueryGeometry = [];
for (const p of queryGeometry) {
const v = [
p.x,
p.y,
z,
1
];
transformMat4$1(v, v, pixelPosMatrix);
projectedQueryGeometry.push(new Point(v[0] / v[3], v[1] / v[3]));
}
return projectedQueryGeometry;
}
//#endregion
//#region node_modules/@maplibre/geojson-vt/dist/geojson-vt.mjs
/**
* calculate simplification data using optimized Douglas-Peucker algorithm
* @param coords - flat array of coordinates
* @param first - index of the first coordinate in the segment
* @param last - index of the last coordinate in the segment
* @param sqTolerance - square tolerance value
*/
function simplify(coords, first, last, sqTolerance) {
let maxSqDist = sqTolerance;
const mid = first + (last - first >> 1);
let minPosToMid = last - first;
let index;
const ax = coords[first];
const ay = coords[first + 1];
const bx = coords[last];
const by = coords[last + 1];
for (let i = first + 3; i < last; i += 3) {
const d = getSqSegDist(coords[i], coords[i + 1], ax, ay, bx, by);
if (d > maxSqDist) {
index = i;
maxSqDist = d;
continue;
}
if (d === maxSqDist) {
const posToMid = Math.abs(i - mid);
if (posToMid < minPosToMid) {
index = i;
minPosToMid = posToMid;
}
}
}
if (maxSqDist > sqTolerance) {
if (index - first > 3) simplify(coords, first, index, sqTolerance);
coords[index + 2] = maxSqDist;
if (last - index > 3) simplify(coords, index, last, sqTolerance);
}
}
/**
* Claculates the square distance from a point to a segment
* @param px - x coordinate of the point
* @param py - y coordinate of the point
* @param x - x coordinate of the first segment endpoint
* @param y - y coordinate of the first segment endpoint
* @param bx - x coordinate of the second segment endpoint
* @param by - y coordinate of the second segment endpoint
* @returns square distance from a point to a segment
*/
function getSqSegDist(px, py, x, y, bx, by) {
let dx = bx - x;
let dy = by - y;
if (dx !== 0 || dy !== 0) {
const t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = bx;
y = by;
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = px - x;
dy = py - y;
return dx * dx + dy * dy;
}
/**
*
* @param id - the feature's ID
* @param type - the feature's type
* @param geom - the feature's geometry
* @param tags - the feature's properties
* @returns the created feature
*/
function createFeature(id, type, geom, tags) {
const data = {
type,
geom
};
const feature = {
id: id == null ? null : id,
type: data.type,
geometry: data.geom,
tags,
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
switch (data.type) {
case "Point":
case "MultiPoint":
calcLineBBox(feature, data.geom);
break;
case "LineString":
calcLineBBox(feature, data.geom.points);
break;
case "Polygon":
calcLineBBox(feature, data.geom[0].points);
break;
case "MultiLineString":
for (const line of data.geom) calcLineBBox(feature, line.points);
break;
case "MultiPolygon": for (const polygon of data.geom) calcLineBBox(feature, polygon[0].points);
}
return feature;
}
function optimizeLineMemory(line) {
const lineImmutable = line;
if (line.points.length > 64) lineImmutable.points = new Float64Array(line.points);
}
function calcLineBBox(feature, geom) {
for (let i = 0; i < geom.length; i += 3) {
feature.minX = Math.min(feature.minX, geom[i]);
feature.minY = Math.min(feature.minY, geom[i + 1]);
feature.maxX = Math.max(feature.maxX, geom[i]);
feature.maxY = Math.max(feature.maxY, geom[i + 1]);
}
}
const MAX_GEOMETRY_COLLECTION_DEPTH = 1024;
/**
* converts GeoJSON to internal source features (an intermediate projected JSON vector format with simplification data)
* @param data
* @param options
* @returns
*/
function convertToInternal(data, options) {
const features = [];
switch (data.type) {
case "FeatureCollection":
for (let i = 0; i < data.features.length; i++) featureToInternal(features, data.features[i], options, i);
break;
case "Feature":
featureToInternal(features, data, options);
break;
default: featureToInternal(features, {
type: "Feature",
geometry: data,
properties: void 0
}, options);
}
return features;
}
function featureToInternal(features, geojson, options, index, depth = 0) {
if (!geojson.geometry) return;
if (depth > MAX_GEOMETRY_COLLECTION_DEPTH) throw new Error("GeometryCollection nesting exceeds supported depth: 1024");
if (geojson.geometry.type === "GeometryCollection") {
convertGeometryCollection(features, geojson, geojson.geometry, options, index, depth + 1);
return;
}
if (!geojson.geometry.coordinates?.length) return;
const id = getFeatureId(geojson, options, index);
const tolerance = Math.pow(options.tolerance / ((1 << options.maxZoom) * options.extent), 2);
switch (geojson.geometry.type) {
case "Point":
convertPointFeature(features, id, geojson.geometry, geojson.properties);
return;
case "MultiPoint":
convertMultiPointFeature(features, id, geojson.geometry, geojson.properties);
return;
case "LineString":
convertLineStringFeature(features, id, geojson.geometry, tolerance, geojson.properties);
return;
case "MultiLineString":
convertMultiLineStringFeature(features, id, geojson.geometry, tolerance, options, geojson.properties);
return;
case "Polygon":
convertPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
return;
case "MultiPolygon":
convertMultiPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
return;
default: throw new Error("Input data is not a valid GeoJSON object.");
}
}
function getFeatureId(geojson, options, index) {
if (options.promoteId) return geojson.properties?.[options.promoteId];
if (options.generateId) return index || 0;
return geojson.id;
}
function convertGeometryCollection(features, geojson, geometry, options, index, depth = 0) {
for (const geom of geometry.geometries) featureToInternal(features, {
id: geojson.id,
type: "Feature",
geometry: geom,
properties: geojson.properties
}, options, index, depth);
}
function convertPointFeature(features, id, geom, properties) {
const out = [];
out.push(projectX(geom.coordinates[0]), projectY(geom.coordinates[1]), 0);
features.push(createFeature(id, "Point", out, properties));
}
function convertMultiPointFeature(features, id, geom, properties) {
const out = [];
for (const coords of geom.coordinates) out.push(projectX(coords[0]), projectY(coords[1]), 0);
features.push(createFeature(id, "MultiPoint", out, properties));
}
function convertLineStringFeature(features, id, geom, tolerance, properties) {
const out = { points: [] };
convertLine(geom.coordinates, out, tolerance, false);
features.push(createFeature(id, "LineString", out, properties));
}
function convertMultiLineStringFeature(features, id, geom, tolerance, options, properties) {
if (options.lineMetrics) for (const line of geom.coordinates) {
const out = { points: [] };
convertLine(line, out, tolerance, false);
features.push(createFeature(id, "LineString", out, properties));
}
else {
const out = [];
convertLines(geom.coordinates, out, tolerance, false);
features.push(createFeature(id, "MultiLineString", out, properties));
}
}
function convertPolygonFeature(features, id, geom, tolerance, properties) {
const out = [];
convertLines(geom.coordinates, out, tolerance, true);
features.push(createFeature(id, "Polygon", out, properties));
}
function convertMultiPolygonFeature(features, id, geom, tolerance, properties) {
const out = [];
for (const polygon of geom.coordinates) {
const polygonOut = [];
convertLines(polygon, polygonOut, tolerance, true);
out.push(polygonOut);
}
features.push(createFeature(id, "MultiPolygon", out, properties));
}
function convertLine(ring, out, tolerance, isPolygon) {
let x0, y0;
let size = 0;
for (let j = 0; j < ring.length; j++) {
const x = projectX(ring[j][0]);
const y = projectY(ring[j][1]);
out.points.push(x, y, 0);
if (j > 0) if (isPolygon) size += (x0 * y - x * y0) / 2;
else size += Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2));
x0 = x;
y0 = y;
}
const last = out.points.length - 3;
out.points[2] = 1;
if (tolerance > 0) simplify(out.points, 0, last, tolerance);
out.points[last + 2] = 1;
optimizeLineMemory(out);
out.size = Math.abs(size);
out.start = 0;
out.end = out.size;
}
function convertLines(rings, out, tolerance, isPolygon) {
for (let i = 0; i < rings.length; i++) {
const geom = { points: [] };
convertLine(rings[i], geom, tolerance, isPolygon);
out.push(geom);
}
}
/**
* Convert longitude to spherical mercator in [0..1] range
*/
function projectX(x) {
return x / 360 + .5;
}
/**
* Convert latitude to spherical mercator in [0..1] range
*/
function projectY(y) {
const sin = Math.sin(y * Math.PI / 180);
const y2 = .5 - .25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
return y2 < 0 ? 0 : y2 > 1 ? 1 : y2;
}
/**
* Converts internal source features back to GeoJSON format.
*/
function convertToGeoJSON(source) {
return {
type: "FeatureCollection",
features: source.map((feature) => featureToGeoJSON(feature))
};
}
/**
* Converts a single internal feature to GeoJSON format.
*/
function featureToGeoJSON(feature) {
const geojsonFeature = {
type: "Feature",
geometry: geometryToGeoJSON(feature),
properties: feature.tags
};
if (feature.id != null) geojsonFeature.id = feature.id;
return geojsonFeature;
}
/**
* Converts a single internal feature geometry to GeoJSON format.
*/
function geometryToGeoJSON(feature) {
const { type, geometry } = feature;
switch (type) {
case "Point": return {
type,
coordinates: unprojectPoint(geometry[0], geometry[1])
};
case "MultiPoint": return {
type,
coordinates: unprojectPoints(geometry)
};
case "LineString": return {
type,
coordinates: unprojectPoints(geometry.points)
};
case "MultiLineString":
case "Polygon": return {
type,
coordinates: geometry.map((ring) => unprojectPoints(ring.points))
};
case "MultiPolygon": return {
type,
coordinates: geometry.map((polygon) => polygon.map((ring) => unprojectPoints(ring.points)))
};
}
}
function unprojectPoints(coords) {
const result = [];
for (let i = 0; i < coords.length; i += 3) result.push(unprojectPoint(coords[i], coords[i + 1]));
return result;
}
function unprojectPoint(x, y) {
return [unprojectX(x), unprojectY(y)];
}
/**
* Convert spherical mercator in [0..1] range to longitude
*/
function unprojectX(x) {
return (x - .5) * 360;
}
/**
* Convert spherical mercator in [0..1] range to latitude
*/
function unprojectY(y) {
const y2 = (180 - y * 360) * Math.PI / 180;
return 360 * Math.atan(Math.exp(y2)) / Math.PI - 90;
}
/**
* clip features between two vertical or horizontal axis-parallel lines:
* | |
* ___|___ | /
* / | \____|____/
* | |
*
* @param features - the features to clip
* @param scale - the scale to divide start and end inputs
* @param start - the start of the clip range
* @param end - the end of the clip range
* @param axis - which axis to clip against
* @param minAll - the minimum for all features in the relevant axis
* @param maxAll - the maximum for all features in the relevant axis
*/
function clip(features, scale, start, end, axis, minAll, maxAll, options) {
start /= scale;
end /= scale;
if (minAll >= start && maxAll < end) return features;
if (maxAll < start || minAll >= end) return null;
const clipped = [];
for (const feature of features) {
const min = axis === 0 ? feature.minX : feature.minY;
const max = axis === 0 ? feature.maxX : feature.maxY;
if (min >= start && max < end) {
clipped.push(feature);
continue;
}
if (max < start || min >= end) continue;
switch (feature.type) {
case "Point":
case "MultiPoint":
clipPointFeature(feature, clipped, start, end, axis);
continue;
case "LineString":
clipLineStringFeature(feature, clipped, start, end, axis, options);
continue;
case "MultiLineString":
clipMultiLineStringFeature(feature, clipped, start, end, axis);
continue;
case "Polygon":
clipPolygonFeature(feature, clipped, start, end, axis);
continue;
case "MultiPolygon":
clipMultiPolygonFeature(feature, clipped, start, end, axis);
continue;
}
}
if (!clipped.length) return null;
return clipped;
}
function clipPointFeature(feature, clipped, start, end, axis) {
const geom = [];
clipPoints$1(feature.geometry, geom, start, end, axis);
if (!geom.length) return;
const type = geom.length === 3 ? "Point" : "MultiPoint";
clipped.push(createFeature(feature.id, type, geom, feature.tags));
}
function clipLineStringFeature(feature, clipped, start, end, axis, options) {
const geom = [];
clipLine$1(feature.geometry, geom, start, end, axis, false, options.lineMetrics);
if (!geom.length) return;
if (options.lineMetrics) {
for (const line of geom) clipped.push(createFeature(feature.id, "LineString", line, feature.tags));
return;
}
if (geom.length > 1) {
clipped.push(createFeature(feature.id, "MultiLineString", geom, feature.tags));
return;
}
clipped.push(createFeature(feature.id, "LineString", geom[0], feature.tags));
}
function clipMultiLineStringFeature(feature, clipped, start, end, axis) {
const geom = [];
clipLines$1(feature.geometry, geom, start, end, axis, false);
if (!geom.length) return;
if (geom.length === 1) {
clipped.push(createFeature(feature.id, "LineString", geom[0], feature.tags));
return;
}
clipped.push(createFeature(feature.id, "MultiLineString", geom, feature.tags));
}
function clipPolygonFeature(feature, clipped, start, end, axis) {
const geom = [];
clipLines$1(feature.geometry, geom, start, end, axis, true);
if (!geom.length) return;
clipped.push(createFeature(feature.id, "Polygon", geom, feature.tags));
}
function clipMultiPolygonFeature(feature, clipped, start, end, axis) {
const geom = [];
for (const polygon of feature.geometry) {
const newPolygon = [];
clipLines$1(polygon, newPolygon, start, end, axis, true);
if (!newPolygon.length) continue;
geom.push(newPolygon);
}
if (!geom.length) return;
clipped.push(createFeature(feature.id, "MultiPolygon", geom, feature.tags));
}
function clipPoints$1(geom, newGeom, start, end, axis) {
for (let i = 0; i < geom.length; i += 3) {
const a = geom[i + axis];
if (a >= start && a <= end) addPoint(newGeom, geom[i], geom[i + 1], geom[i + 2]);
}
}
function clipLine$1(geom, newGeom, start, end, axis, isPolygon, trackMetrics) {
let slice = newSlice(geom);
const intersect = axis === 0 ? intersectX : intersectY;
let len = geom.start;
let segLen, t;
for (let i = 0; i < geom.points.length - 3; i += 3) {
const ax = geom.points[i];
const ay = geom.points[i + 1];
const az = geom.points[i + 2];
const bx = geom.points[i + 3];
const by = geom.points[i + 4];
const a = axis === 0 ? ax : ay;
const b = axis === 0 ? bx : by;
let exited = false;
if (trackMetrics) segLen = Math.sqrt(Math.pow(ax - bx, 2) + Math.pow(ay - by, 2));
if (a < start) {
if (b > start) {
t = intersect(slice, ax, ay, bx, by, start);
if (trackMetrics) slice.start = len + segLen * t;
}
} else if (a > end) {
if (b < end) {
t = intersect(slice, ax, ay, bx, by, end);
if (trackMetrics) slice.start = len + segLen * t;
}
} else addPoint(slice.points, ax, ay, az);
if (b < start && a >= start) {
t = intersect(slice, ax, ay, bx, by, start);
exited = true;
}
if (b > end && a <= end) {
t = intersect(slice, ax, ay, bx, by, end);
exited = true;
}
if (!isPolygon && exited) {
if (trackMetrics) slice.end = len + segLen * t;
newGeom.push(slice);
slice = newSlice(geom);
}
if (trackMetrics) len += segLen;
}
let last = geom.points.length - 3;
const ax = geom.points[last];
const ay = geom.points[last + 1];
const az = geom.points[last + 2];
const a = axis === 0 ? ax : ay;
if (a >= start && a <= end) addPoint(slice.points, ax, ay, az);
last = slice.points.length - 3;
if (isPolygon && last >= 3 && (slice.points[last] !== slice.points[0] || slice.points[last + 1] !== slice.points[1])) addPoint(slice.points, slice.points[0], slice.points[1], slice.points[2]);
if (slice.points.length) {
optimizeLineMemory(slice);
newGeom.push(slice);
}
}
function newSlice(line) {
return {
points: [],
size: line.size,
start: line.start,
end: line.end
};
}
function clipLines$1(geom, newGeom, start, end, axis, isPolygon) {
for (const line of geom) clipLine$1(line, newGeom, start, end, axis, isPolygon, false);
}
function addPoint(out, x, y, z) {
out.push(x, y, z);
}
function intersectX(out, ax, ay, bx, by, x) {
const t = (x - ax) / (bx - ax);
addPoint(out.points, x, ay + (by - ay) * t, 1);
return t;
}
function intersectY(out, ax, ay, bx, by, y) {
const t = (y - ay) / (by - ay);
addPoint(out.points, ax + (bx - ax) * t, y, 1);
return t;
}
function wrap(features, options) {
const buffer = options.buffer / options.extent;
let merged = features;
const left = clip(features, 1, -1 - buffer, buffer, 0, -1, 2, options);
const right = clip(features, 1, 1 - buffer, 2 + buffer, 0, -1, 2, options);
if (!left && !right) return merged;
merged = clip(features, 1, -buffer, 1 + buffer, 0, -1, 2, options) || [];
if (left) merged = shiftFeatureCoords(left, 1).concat(merged);
if (right) merged = merged.concat(shiftFeatureCoords(right, -1));
return merged;
}
function shiftFeatureCoords(features, offset) {
const newFeatures = [];
for (const feature of features) switch (feature.type) {
case "Point":
case "MultiPoint": {
const newGeometry = shiftPointCoords(feature.geometry, offset);
newFeatures.push(createFeature(feature.id, feature.type, newGeometry, feature.tags));
continue;
}
case "LineString": {
const newGeometry = shiftLineCoords(feature.geometry, offset);
newFeatures.push(createFeature(feature.id, feature.type, newGeometry, feature.tags));
continue;
}
case "MultiLineString":
case "Polygon": {
const newGeometry = [];
for (const line of feature.geometry) newGeometry.push(shiftLineCoords(line, offset));
newFeatures.push(createFeature(feature.id, feature.type, newGeometry, feature.tags));
continue;
}
case "MultiPolygon": {
const newGeometry = [];
for (const polygon of feature.geometry) {
const newPolygon = [];
for (const line of polygon) newPolygon.push(shiftLineCoords(line, offset));
newGeometry.push(newPolygon);
}
newFeatures.push(createFeature(feature.id, feature.type, newGeometry, feature.tags));
continue;
}
}
return newFeatures;
}
function shiftPointCoords(coords, offset) {
const newCoords = [];
for (let i = 0; i < coords.length; i += 3) newCoords.push(coords[i] + offset, coords[i + 1], coords[i + 2]);
return newCoords;
}
function shiftLineCoords(line, offset) {
const newLine = {
points: [],
size: line.size
};
if (line.start !== void 0) {
newLine.start = line.start;
newLine.end = line.end;
}
for (let i = 0; i < line.points.length; i += 3) newLine.points.push(line.points[i] + offset, line.points[i + 1], line.points[i + 2]);
optimizeLineMemory(newLine);
return newLine;
}
/**
* Applies a GeoJSON Source Diff to an existing set of simplified features
* @param source
* @param dataDiff
* @param options
* @returns
*/
function applySourceDiff(source, dataDiff, options) {
const diff = diffToHashed(dataDiff, options);
let affected = [];
if (diff.removeAll) {
affected = source;
source = [];
}
if (diff.remove.size || diff.add.size) {
const removeFeatures = [];
for (const feature of source) if (diff.remove.has(feature.id) || diff.add.has(feature.id)) removeFeatures.push(feature);
if (removeFeatures.length) {
affected = affected.concat(removeFeatures);
const removeIds = new Set(removeFeatures.map((f) => f.id));
source = source.filter((f) => !removeIds.has(f.id));
}
if (diff.add.size) {
let addFeatures = convertToInternal({
type: "FeatureCollection",
features: Array.from(diff.add.values())
}, options);
addFeatures = wrap(addFeatures, options);
affected = affected.concat(addFeatures);
source = source.concat(addFeatures);
}
}
if (diff.update.size) {
const oldFeaturesMap = /* @__PURE__ */ new Map();
let keepFeatures = [];
for (const feature of source) if (diff.update.has(feature.id)) oldFeaturesMap.set(feature.id, [...oldFeaturesMap.get(feature.id) || [], feature]);
else keepFeatures.push(feature);
for (const [id, update] of diff.update) {
const oldFeatures = oldFeaturesMap.get(id);
if (!oldFeatures || oldFeatures.length === 0) continue;
const updatedFeatures = getUpdatedFeatures(oldFeatures, update, options);
affected = affected.concat(oldFeatures, updatedFeatures);
keepFeatures = keepFeatures.concat(updatedFeatures);
}
source = keepFeatures;
}
return {
affected,
source
};
}
/**
* Gets updated simplified feature(s) based on a diff update object.
* @param vtFeatures - the original features
* @param update - the update object to apply
* @param options - the options to use for the wrap method
* @returns Updated features. If geometry is updated, returns new feature(s) converted from geojson and wrapped. If only properties are updated, returns feature(s) with tags updated.
*/
function getUpdatedFeatures(vtFeatures, update, options) {
const changeGeometry = !!update.newGeometry;
const changeProps = update.removeAllProperties || update.removeProperties?.length > 0 || update.addOrUpdateProperties?.length > 0;
if (changeGeometry) {
const vtFeature = vtFeatures[0];
let features = convertToInternal({
type: "FeatureCollection",
features: [{
type: "Feature",
id: vtFeature.id,
geometry: update.newGeometry,
properties: changeProps ? applyPropertyUpdates(vtFeature.tags, update) : vtFeature.tags
}]
}, options);
features = wrap(features, options);
return features;
}
if (changeProps) {
const updated = [];
for (const vtFeature of vtFeatures) {
const feature = { ...vtFeature };
feature.tags = applyPropertyUpdates(feature.tags, update);
updated.push(feature);
}
return updated;
}
return vtFeatures;
}
/**
* helper to apply property updates from a diff update object to a properties object
*/
function applyPropertyUpdates(tags, update) {
if (update.removeAllProperties) return {};
const properties = { ...tags || {} };
if (update.removeProperties) for (const key of update.removeProperties) delete properties[key];
if (update.addOrUpdateProperties) for (const { key, value } of update.addOrUpdateProperties) properties[key] = value;
return properties;
}
/**
* Convert a GeoJSON Source Diff to an idempotent hashed representation using Sets and Maps
*/
function diffToHashed(diff, options) {
if (!diff) return {
remove: /* @__PURE__ */ new Set(),
add: /* @__PURE__ */ new Map(),
update: /* @__PURE__ */ new Map()
};
return {
removeAll: diff.removeAll,
remove: new Set(diff.remove || []),
add: new Map(diff.add?.map((feature) => [options.promoteId ? feature.properties[options.promoteId] : feature.id, feature])),
update: new Map(diff.update?.map((update) => [update.id, update]))
};
}
const ARRAY_TYPES = [
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array
];
/** @typedef {Int8ArrayConstructor | Uint8ArrayConstructor | Uint8ClampedArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor} TypedArrayConstructor */
/** @typedef {Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array} TypedArray */
const VERSION = 1;
const HEADER_SIZE = 8;
const STACK = /* @__PURE__ */ new Uint32Array(96);
var KDBush = class KDBush {
/**
* Creates an index from raw `ArrayBuffer` data.
* @param {ArrayBufferLike} data
*/
static from(data) {
if (!data || data.byteLength === void 0 || data.buffer) throw new Error("Data must be an instance of ArrayBuffer or SharedArrayBuffer.");
const [magic, versionAndType] = new Uint8Array(data, 0, 2);
if (magic !== 219) throw new Error("Data does not appear to be in a KDBush format.");
const version = versionAndType >> 4;
if (version !== VERSION) throw new Error(`Got v${version} data when expected v${VERSION}.`);
const ArrayType = ARRAY_TYPES[versionAndType & 15];
if (!ArrayType) throw new Error("Unrecognized array type.");
const [nodeSize] = new Uint16Array(data, 2, 1);
const [numItems] = new Uint32Array(data, 4, 1);
return new KDBush(numItems, nodeSize, ArrayType, void 0, data);
}
/**
* Creates an index that will hold a given number of items.
* @param {number} numItems
* @param {number} [nodeSize=64] Size of the KD-tree node (64 by default).
* @param {TypedArrayConstructor} [ArrayType=Float64Array] The array type used for coordinates storage (`Float64Array` by default).
* @param {ArrayBufferConstructor | SharedArrayBufferConstructor} [ArrayBufferType=ArrayBuffer] The array buffer type used for storage (`ArrayBuffer` by default).
* @param {ArrayBufferLike} [data] (For internal use only)
*/
constructor(numItems, nodeSize = 64, ArrayType = Float64Array, ArrayBufferType = ArrayBuffer, data) {
if (isNaN(numItems) || numItems < 0) throw new Error(`Unexpected numItems value: ${numItems}.`);
this.numItems = +numItems;
this.nodeSize = Math.min(Math.max(+nodeSize, 2), 65535);
this.ArrayType = ArrayType;
this.IndexArrayType = numItems < 65536 ? Uint16Array : Uint32Array;
const arrayTypeIndex = ARRAY_TYPES.indexOf(this.ArrayType);
const coordsByteSize = numItems * 2 * this.ArrayType.BYTES_PER_ELEMENT;
const idsByteSize = numItems * this.IndexArrayType.BYTES_PER_ELEMENT;
const padCoords = (8 - idsByteSize % 8) % 8;
if (arrayTypeIndex < 0) throw new Error(`Unexpected typed array class: ${ArrayType}.`);
if (data) {
this.data = data;
this.ids = new this.IndexArrayType(data, HEADER_SIZE, numItems);
this.coords = new ArrayType(data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
this._pos = numItems * 2;
this._finished = true;
} else {
const data = this.data = new ArrayBufferType(HEADER_SIZE + coordsByteSize + idsByteSize + padCoords);
this.ids = new this.IndexArrayType(data, HEADER_SIZE, numItems);
this.coords = new ArrayType(data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
this._pos = 0;
this._finished = false;
new Uint8Array(data, 0, 2).set([219, 16 + arrayTypeIndex]);
new Uint16Array(data, 2, 1)[0] = nodeSize;
new Uint32Array(data, 4, 1)[0] = numItems;
}
}
/**
* Add a point to the index.
* @param {number} x
* @param {number} y
* @returns {number} An incremental index associated with the added item (starting from `0`).
*/
add(x, y) {
const index = this._pos >> 1;
this.ids[index] = index;
this.coords[this._pos++] = x;
this.coords[this._pos++] = y;
return index;
}
/**
* Perform indexing of the added points.
*/
finish() {
const numAdded = this._pos >> 1;
if (numAdded !== this.numItems) throw new Error(`Added ${numAdded} items when expected ${this.numItems}.`);
sort(this.ids, this.coords, this.nodeSize, 0, this.numItems - 1, 0);
this._finished = true;
return this;
}
/**
* Search the index for items within a given bounding box.
* @param {number} minX
* @param {number} minY
* @param {number} maxX
* @param {number} maxY
* @returns {number[]} An array of indices correponding to the found items.
*/
range(minX, minY, maxX, maxY) {
if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
const { ids, coords, nodeSize } = this;
STACK[0] = 0;
STACK[1] = ids.length - 1;
STACK[2] = 0;
let sp = 3;
const result = [];
while (sp > 0) {
const axis = STACK[--sp];
const right = STACK[--sp];
const left = STACK[--sp];
if (right - left <= nodeSize) {
for (let i = left; i <= right; i++) {
const x = coords[2 * i];
const y = coords[2 * i + 1];
if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[i]);
}
continue;
}
const m = left + right >> 1;
const x = coords[2 * m];
const y = coords[2 * m + 1];
if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[m]);
if (axis === 0 ? minX <= x : minY <= y) {
STACK[sp++] = left;
STACK[sp++] = m - 1;
STACK[sp++] = 1 - axis;
}
if (axis === 0 ? maxX >= x : maxY >= y) {
STACK[sp++] = m + 1;
STACK[sp++] = right;
STACK[sp++] = 1 - axis;
}
}
return result;
}
/**
* Search the index for items within a given radius.
* @param {number} qx
* @param {number} qy
* @param {number} r Query radius.
* @returns {number[]} An array of indices correponding to the found items.
*/
within(qx, qy, r) {
const result = [];
this.withinInto(qx, qy, r, result);
return result;
}
/**
* Search the index for items within a given radius, writing matching ids into `out`
* via indexed assignment (`out[i] = id`). Accepts any indexed-writable container —
* a typed array sized to the expected upper bound (allocation-free, fast) or a plain
* `Array` (which will grow as needed). Returns the number of matches written.
* @param {number} qx
* @param {number} qy
* @param {number} r Query radius.
* @param {number[] | TypedArray} out Container to write matching ids into.
* @returns {number} The number of matches written to `out`.
*/
withinInto(qx, qy, r, out) {
if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
const { ids, coords, nodeSize } = this;
STACK[0] = 0;
STACK[1] = ids.length - 1;
STACK[2] = 0;
let sp = 3;
let count = 0;
const r2 = r * r;
while (sp > 0) {
const axis = STACK[--sp];
const right = STACK[--sp];
const left = STACK[--sp];
if (right - left <= nodeSize) {
for (let i = left; i <= right; i++) if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) out[count++] = ids[i];
continue;
}
const m = left + right >> 1;
const x = coords[2 * m];
const y = coords[2 * m + 1];
if (sqDist(x, y, qx, qy) <= r2) out[count++] = ids[m];
if (axis === 0 ? qx - r <= x : qy - r <= y) {
STACK[sp++] = left;
STACK[sp++] = m - 1;
STACK[sp++] = 1 - axis;
}
if (axis === 0 ? qx + r >= x : qy + r >= y) {
STACK[sp++] = m + 1;
STACK[sp++] = right;
STACK[sp++] = 1 - axis;
}
}
return count;
}
};
/**
* @param {Uint16Array | Uint32Array} ids
* @param {TypedArray} coords
* @param {number} nodeSize
* @param {number} left
* @param {number} right
* @param {number} axis
*/
function sort(ids, coords, nodeSize, left, right, axis) {
if (right - left <= nodeSize) return;
const m = left + right >> 1;
select(ids, coords, m, left, right, axis);
sort(ids, coords, nodeSize, left, m - 1, 1 - axis);
sort(ids, coords, nodeSize, m + 1, right, 1 - axis);
}
/**
* Custom Floyd-Rivest selection algorithm: sort ids and coords so that
* [left..k-1] items are smaller than k-th item (on either x or y axis)
* @param {Uint16Array | Uint32Array} ids
* @param {TypedArray} coords
* @param {number} k
* @param {number} left
* @param {number} right
* @param {number} axis
*/
function select(ids, coords, k, left, right, axis) {
while (right > left) {
if (right - left > 600) {
const n = right - left + 1;
const m = k - left + 1;
const z = Math.log(n);
const s = .5 * Math.exp(2 * z / 3);
const sd = .5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
select(ids, coords, k, Math.max(left, Math.floor(k - m * s / n + sd)), Math.min(right, Math.floor(k + (n - m) * s / n + sd)), axis);
}
const t = coords[2 * k + axis];
let i = left;
let j = right;
swapItem(ids, coords, left, k);
if (coords[2 * right + axis] > t) swapItem(ids, coords, left, right);
while (i < j) {
swapItem(ids, coords, i, j);
i++;
j--;
while (coords[2 * i + axis] < t) i++;
while (coords[2 * j + axis] > t) j--;
}
if (coords[2 * left + axis] === t) swapItem(ids, coords, left, j);
else {
j++;
swapItem(ids, coords, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
/**
* @param {Uint16Array | Uint32Array} ids
* @param {TypedArray} coords
* @param {number} i
* @param {number} j
*/
function swapItem(ids, coords, i, j) {
swap(ids, i, j);
swap(coords, 2 * i, 2 * j);
swap(coords, 2 * i + 1, 2 * j + 1);
}
/**
* @param {TypedArray} arr
* @param {number} i
* @param {number} j
*/
function swap(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
/**
* @param {number} ax
* @param {number} ay
* @param {number} bx
* @param {number} by
*/
function sqDist(ax, ay, bx, by) {
const dx = ax - bx;
const dy = ay - by;
return dx * dx + dy * dy;
}
const defaultClusterOptions = {
minZoom: 0,
maxZoom: 16,
minPoints: 2,
radius: 40,
extent: 512,
nodeSize: 64,
log: false,
generateId: false,
reduce: null,
map: (props) => props
};
const OFFSET_ZOOM = 2;
const OFFSET_ID = 3;
const OFFSET_PARENT = 4;
const OFFSET_NUM = 5;
const OFFSET_PROP = 6;
/**
* This class allow clustering of geojson points.
*/
var ClusterTileIndex = class {
constructor(options) {
this.options = Object.assign(Object.create(defaultClusterOptions), options);
this.trees = new Array(this.options.maxZoom + 1);
this.stride = this.options.reduce ? 7 : 6;
this.clusterProps = [];
this.points = [];
}
/**
* Loads GeoJSON point features and builds the internal clustering index.
* @param points - GeoJSON point features to cluster.
*/
load(points) {
const features = [];
for (const point of points) {
if (!point.geometry) continue;
const [lng, lat] = point.geometry.coordinates;
const [x, y] = [projectX(lng), projectY(lat)];
const feature = {
id: point.id,
type: "Point",
geometry: [x, y],
tags: point.properties
};
features.push(feature);
}
this.createIndex(features);
}
/**
* @internal
* Loads internal GeoJSONVT point features from a data source and builds the clustering index.
* @param features - {@link GeoJSONVTInternalFeature} data source features to filter and cluster.
*/
initialize(features) {
const points = [];
for (const feature of features) {
if (feature.type !== "Point") continue;
points.push(feature);
}
this.createIndex(points);
}
/**
* @internal
* Updates the cluster data by rebuilding.
* @param features
*/
updateIndex(features, _affected, options) {
this.options = Object.assign(Object.create(defaultClusterOptions), options.clusterOptions);
this.initialize(features);
}
createIndex(points) {
const { log, minZoom, maxZoom } = this.options;
if (log) console.time("total time");
const timerId = `prepare ${points.length} points`;
if (log) console.time(timerId);
this.points = points;
const data = [];
for (let i = 0; i < points.length; i++) {
const p = points[i];
if (!p?.geometry) continue;
let [x, y] = p.geometry;
x = Math.fround(x);
y = Math.fround(y);
data.push(x, y, Infinity, i, -1, 1);
if (this.options.reduce) data.push(0);
}
let tree = this.trees[maxZoom + 1] = this.createTree(data);
if (log) console.timeEnd(timerId);
for (let z = maxZoom; z >= minZoom; z--) {
const now = Date.now();
tree = this.trees[z] = this.createTree(this.cluster(tree, z));
if (log) console.log("z%d: %d clusters in %dms", z, tree.numItems, Date.now() - now);
}
if (log) console.timeEnd("total time");
}
/**
* Returns clusters and/or points within a bounding box at a given zoom level.
* @param bbox - Bounding box in `[westLng, southLat, eastLng, northLat]` order.
* @param zoom - Zoom level to query.
*/
getClusters(bbox, zoom) {
return this.getClustersInternal(bbox, zoom).map((f) => featureToGeoJSON(f));
}
getClustersInternal(bbox, zoom) {
let minLng = ((bbox[0] + 180) % 360 + 360) % 360 - 180;
const minLat = Math.max(-90, Math.min(90, bbox[1]));
let maxLng = bbox[2] === 180 ? 180 : ((bbox[2] + 180) % 360 + 360) % 360 - 180;
const maxLat = Math.max(-90, Math.min(90, bbox[3]));
if (bbox[2] - bbox[0] >= 360) {
minLng = -180;
maxLng = 180;
} else if (minLng > maxLng) {
const easternHem = this.getClustersInternal([
minLng,
minLat,
180,
maxLat
], zoom);
const westernHem = this.getClustersInternal([
-180,
minLat,
maxLng,
maxLat
], zoom);
return easternHem.concat(westernHem);
}
const tree = this.trees[this.limitZoom(zoom)];
const ids = tree.range(projectX(minLng), projectY(maxLat), projectX(maxLng), projectY(minLat));
const data = tree.flatData;
const clusters = [];
for (const id of ids) {
const k = this.stride * id;
clusters.push(data[k + OFFSET_NUM] > 1 ? getClusterFeature(data, k, this.clusterProps) : this.points[data[k + OFFSET_ID]]);
}
return clusters;
}
/**
* Returns the immediate children (clusters or points) of a cluster as GeoJSON.
* @param clusterId - The target cluster id.
*/
getChildren(clusterId) {
const originId = this.getOriginId(clusterId);
const originZoom = this.getOriginZoom(clusterId);
const clusterError = /* @__PURE__ */ new Error("No cluster with the specified id: " + clusterId);
const tree = this.trees[originZoom];
if (!tree) throw clusterError;
const data = tree.flatData;
if (originId * this.stride >= data.length) throw clusterError;
const r = this.options.radius / (this.options.extent * Math.pow(2, originZoom - 1));
const x = data[originId * this.stride];
const y = data[originId * this.stride + 1];
const ids = tree.within(x, y, r);
const children = [];
for (const id of ids) {
const k = id * this.stride;
if (data[k + OFFSET_PARENT] === clusterId) children.push(data[k + OFFSET_NUM] > 1 ? getClusterGeoJSON(data, k, this.clusterProps) : featureToGeoJSON(this.points[data[k + OFFSET_ID]]));
}
if (children.length === 0) throw clusterError;
return children;
}
/**
* Returns leaf point features under a cluster, paginated by `limit` and `offset`.
* @param clusterId - The target cluster id.
* @param limit - Maximum number of points to return (defaults to `10`).
* @param offset - Number of points to skip before collecting results (defaults to `0`).
*/
getLeaves(clusterId, limit, offset) {
limit = limit || 10;
offset = offset || 0;
const leaves = [];
this.appendLeaves(leaves, clusterId, limit, offset, 0);
return leaves;
}
/**
* Generates a vector-tile-like representation of a single tile.
* @param z - Tile zoom.
* @param x - Tile x coordinate.
* @param y - Tile y coordinate.
*/
getTile(z, x, y) {
const tree = this.trees[this.limitZoom(z)];
if (!tree) return null;
const z2 = Math.pow(2, z);
const { extent, radius } = this.options;
const p = radius / extent;
const top = (y - p) / z2;
const bottom = (y + 1 + p) / z2;
const tile = {
transformed: true,
features: [],
source: null,
x,
y,
z
};
this.addTileFeatures(tree.range((x - p) / z2, top, (x + 1 + p) / z2, bottom), tree.flatData, x, y, z2, tile);
if (x === 0) this.addTileFeatures(tree.range(1 - p / z2, top, 1, bottom), tree.flatData, z2, y, z2, tile);
if (x === z2 - 1) this.addTileFeatures(tree.range(0, top, p / z2, bottom), tree.flatData, -1, y, z2, tile);
return tile;
}
/**
* Returns the zoom level at which a cluster expands into multiple children.
* @param clusterId - The target cluster id.
*/
getClusterExpansionZoom(clusterId) {
return this.getOriginZoom(clusterId);
}
appendLeaves(result, clusterId, limit, offset, skipped) {
const children = this.getChildren(clusterId);
for (const child of children) {
const props = child.properties;
if (props?.cluster) if (skipped + props.point_count <= offset) skipped += props.point_count;
else skipped = this.appendLeaves(result, props.cluster_id, limit, offset, skipped);
else if (skipped < offset) skipped++;
else result.push(child);
if (result.length === limit) break;
}
return skipped;
}
createTree(data) {
const tree = new KDBush(data.length / this.stride | 0, this.options.nodeSize, Float32Array);
for (let i = 0; i < data.length; i += this.stride) tree.add(data[i], data[i + 1]);
tree.finish();
tree.flatData = data;
tree.data = null;
return tree;
}
addTileFeatures(ids, data, x, y, z2, tile) {
for (const i of ids) {
const k = i * this.stride;
const isCluster = data[k + OFFSET_NUM] > 1;
let tags;
let px;
let py;
if (isCluster) {
tags = getClusterProperties(data, k, this.clusterProps);
px = data[k];
py = data[k + 1];
} else {
const p = this.points[data[k + OFFSET_ID]];
tags = p.tags;
[px, py] = p.geometry;
}
const f = {
type: 1,
geometry: [[Math.round(this.options.extent * (px * z2 - x)), Math.round(this.options.extent * (py * z2 - y))]],
tags
};
let id;
if (isCluster || this.options.generateId) id = data[k + OFFSET_ID];
else id = this.points[data[k + OFFSET_ID]].id;
if (id !== void 0) f.id = id;
tile.features.push(f);
}
}
limitZoom(z) {
return Math.max(this.options.minZoom, Math.min(Math.floor(+z), this.options.maxZoom + 1));
}
cluster(tree, zoom) {
const { radius, extent, reduce, minPoints } = this.options;
const r = radius / (extent * Math.pow(2, zoom));
const data = tree.flatData;
const nextData = [];
const stride = this.stride;
for (let i = 0; i < data.length; i += stride) {
if (data[i + OFFSET_ZOOM] <= zoom) continue;
data[i + OFFSET_ZOOM] = zoom;
const x = data[i];
const y = data[i + 1];
const neighborIds = tree.within(data[i], data[i + 1], r);
const numPointsOrigin = data[i + OFFSET_NUM];
let numPoints = numPointsOrigin;
for (const neighborId of neighborIds) {
const k = neighborId * stride;
if (data[k + OFFSET_ZOOM] > zoom) numPoints += data[k + OFFSET_NUM];
}
if (numPoints > numPointsOrigin && numPoints >= minPoints) {
let wx = x * numPointsOrigin;
let wy = y * numPointsOrigin;
let clusterProperties;
let clusterPropIndex = -1;
const id = ((i / stride | 0) << 5) + (zoom + 1) + this.points.length;
for (const neighborId of neighborIds) {
const k = neighborId * stride;
if (data[k + OFFSET_ZOOM] <= zoom) continue;
data[k + OFFSET_ZOOM] = zoom;
const numPoints2 = data[k + OFFSET_NUM];
wx += data[k] * numPoints2;
wy += data[k + 1] * numPoints2;
data[k + OFFSET_PARENT] = id;
if (reduce) {
if (!clusterProperties) {
clusterProperties = this.map(data, i, true);
clusterPropIndex = this.clusterProps.length;
this.clusterProps.push(clusterProperties);
}
reduce(clusterProperties, this.map(data, k));
}
}
data[i + OFFSET_PARENT] = id;
nextData.push(wx / numPoints, wy / numPoints, Infinity, id, -1, numPoints);
if (reduce) nextData.push(clusterPropIndex);
} else {
for (let j = 0; j < stride; j++) nextData.push(data[i + j]);
if (numPoints > 1) for (const neighborId of neighborIds) {
const k = neighborId * stride;
if (data[k + OFFSET_ZOOM] <= zoom) continue;
data[k + OFFSET_ZOOM] = zoom;
for (let j = 0; j < stride; j++) nextData.push(data[k + j]);
}
}
}
return nextData;
}
getOriginId(clusterId) {
return clusterId - this.points.length >> 5;
}
getOriginZoom(clusterId) {
return (clusterId - this.points.length) % 32;
}
map(data, i, clone) {
if (data[i + OFFSET_NUM] > 1) {
const props = this.clusterProps[data[i + OFFSET_PROP]];
return clone ? Object.assign({}, props) : props;
}
const original = this.points[data[i + OFFSET_ID]].tags;
const result = this.options.map(original);
return clone && result === original ? Object.assign({}, result) : result;
}
};
function getClusterFeature(data, i, clusterProps) {
return {
id: data[i + OFFSET_ID],
type: "Point",
tags: getClusterProperties(data, i, clusterProps),
geometry: [data[i], data[i + 1]]
};
}
function getClusterGeoJSON(data, i, clusterProps) {
return {
type: "Feature",
id: data[i + OFFSET_ID],
properties: getClusterProperties(data, i, clusterProps),
geometry: {
type: "Point",
coordinates: [unprojectX(data[i]), unprojectY(data[i + 1])]
}
};
}
function getClusterProperties(data, i, clusterProps) {
const count = data[i + OFFSET_NUM];
const abbrev = count >= 1e4 ? `${Math.round(count / 1e3)}k` : count >= 1e3 ? `${Math.round(count / 100) / 10}k` : count;
const propIndex = data[i + OFFSET_PROP];
const properties = propIndex === -1 ? {} : Object.assign({}, clusterProps[propIndex]);
return Object.assign(properties, {
cluster: true,
cluster_id: data[i + OFFSET_ID],
point_count: count,
point_count_abbreviated: abbrev
});
}
const GEOJSONVT_CLIP_START = "geojsonvt_clip_start";
const GEOJSONVT_CLIP_END = "geojsonvt_clip_end";
/**
* Creates a tile object from the given features
* @param features - the features to include in the tile
* @param z
* @param tx
* @param ty
* @param options - the options object
* @returns the created tile
*/
function createTile(features, z, tx, ty, options) {
const tolerance = z === options.maxZoom ? 0 : options.tolerance / ((1 << z) * options.extent);
const tile = {
transformed: false,
features: [],
source: null,
x: tx,
y: ty,
z,
minX: 2,
minY: 1,
maxX: -1,
maxY: 0,
numPoints: 0,
numSimplified: 0,
numFeatures: features.length
};
for (const feature of features) addFeature$1(tile, feature, tolerance, options);
return tile;
}
function addFeature$1(tile, feature, tolerance, options) {
tile.minX = Math.min(tile.minX, feature.minX);
tile.minY = Math.min(tile.minY, feature.minY);
tile.maxX = Math.max(tile.maxX, feature.maxX);
tile.maxY = Math.max(tile.maxY, feature.maxY);
switch (feature.type) {
case "Point":
case "MultiPoint":
addPointsTileFeature(tile, feature);
return;
case "LineString":
addLineTileFeautre(tile, feature, tolerance, options);
return;
case "MultiLineString":
case "Polygon":
addLinesTileFeature(tile, feature, tolerance);
return;
case "MultiPolygon":
addMultiPolygonTileFeature(tile, feature, tolerance);
return;
}
}
function addPointsTileFeature(tile, feature) {
const geometry = [];
for (let i = 0; i < feature.geometry.length; i += 3) {
geometry.push(feature.geometry[i], feature.geometry[i + 1]);
tile.numPoints++;
tile.numSimplified++;
}
if (!geometry.length) return;
const tileFeature = {
type: 1,
tags: feature.tags || null,
geometry
};
if (feature.id !== null) tileFeature.id = feature.id;
tile.features.push(tileFeature);
}
function addLineTileFeautre(tile, feature, tolerance, options) {
const geometry = [];
addLine(geometry, feature.geometry, tile, tolerance, false, false);
if (!geometry.length) return;
let tags = feature.tags || null;
if (options.lineMetrics) {
tags = {};
for (const key in feature.tags) tags[key] = feature.tags[key];
tags[GEOJSONVT_CLIP_START] = feature.geometry.start / feature.geometry.size;
tags[GEOJSONVT_CLIP_END] = feature.geometry.end / feature.geometry.size;
}
const tileFeature = {
type: 2,
tags,
geometry
};
if (feature.id !== null) tileFeature.id = feature.id;
tile.features.push(tileFeature);
}
function addLinesTileFeature(tile, feature, tolerance) {
const geometry = [];
for (let i = 0; i < feature.geometry.length; i++) addLine(geometry, feature.geometry[i], tile, tolerance, feature.type === "Polygon", i === 0);
if (!geometry.length) return;
const tileFeature = {
type: feature.type === "Polygon" ? 3 : 2,
tags: feature.tags || null,
geometry
};
if (feature.id !== null) tileFeature.id = feature.id;
tile.features.push(tileFeature);
}
function addMultiPolygonTileFeature(tile, feature, tolerance) {
const geometry = [];
for (let k = 0; k < feature.geometry.length; k++) {
const polygon = feature.geometry[k];
for (let i = 0; i < polygon.length; i++) addLine(geometry, polygon[i], tile, tolerance, true, i === 0);
}
if (!geometry.length) return;
const tileFeature = {
type: 3,
tags: feature.tags || null,
geometry
};
if (feature.id !== null) tileFeature.id = feature.id;
tile.features.push(tileFeature);
}
function addLine(result, geom, tile, tolerance, isPolygon, isOuter) {
const sqTolerance = tolerance * tolerance;
if (tolerance > 0 && geom.size < (isPolygon ? sqTolerance : tolerance)) {
tile.numPoints += geom.points.length / 3;
return;
}
const ring = [];
for (let i = 0; i < geom.points.length; i += 3) {
if (tolerance === 0 || geom.points[i + 2] > sqTolerance) {
tile.numSimplified++;
ring.push(geom.points[i], geom.points[i + 1]);
}
tile.numPoints++;
}
if (isPolygon) rewind(ring, isOuter);
result.push(ring);
}
function rewind(ring, clockwise) {
let area = 0;
for (let i = 0, len = ring.length, j = len - 2; i < len; j = i, i += 2) area += (ring[i] - ring[j]) * (ring[i + 1] + ring[j + 1]);
if (area > 0 !== clockwise) return;
for (let i = 0, len = ring.length; i < len / 2; i += 2) {
const x = ring[i];
const y = ring[i + 1];
ring[i] = ring[len - 2 - i];
ring[i + 1] = ring[len - 1 - i];
ring[len - 2 - i] = x;
ring[len - 1 - i] = y;
}
}
/**
* Transforms the coordinates of each feature in the given tile from
* mercator-projected space into (extent x extent) tile space.
* @param tile - the tile to transform, this gets modified in place
* @param extent - the tile extent (usually 4096)
* @returns the transformed tile
*/
function transformTile(tile, extent) {
if (tile.transformed) return tile;
const z2 = 1 << tile.z;
const tx = tile.x;
const ty = tile.y;
for (const feature of tile.features) if (feature.type === 1) transformPointFeature(feature, extent, z2, tx, ty);
else transformNonPointFeature(feature, extent, z2, tx, ty);
tile.transformed = true;
return tile;
}
/**
* Transforms a single point feature from mercator-projected space into (extent x extent) tile space.
*/
function transformPointFeature(feature, extent, z2, tx, ty) {
const transformed = feature;
const geometry = feature.geometry;
const point = [];
for (let i = 0; i < geometry.length; i += 2) point.push(transformPoint(geometry[i], geometry[i + 1], extent, z2, tx, ty));
transformed.geometry = point;
return transformed;
}
/**
* Transforms a single non-point feature from mercator-projected space into (extent x extent) tile space.
*/
function transformNonPointFeature(feature, extent, z2, tx, ty) {
const transformed = feature;
const geometry = feature.geometry;
const nonPoint = [];
for (const geom of geometry) {
const ring = [];
for (let i = 0; i < geom.length; i += 2) ring.push(transformPoint(geom[i], geom[i + 1], extent, z2, tx, ty));
nonPoint.push(ring);
}
transformed.geometry = nonPoint;
return transformed;
}
function transformPoint(x, y, extent, z2, tx, ty) {
return [Math.round(extent * (x * z2 - tx)), Math.round(extent * (y * z2 - ty))];
}
var TileIndex = class {
constructor(options) {
this.options = options;
this.total = 0;
this.stats = {};
this.tiles = {};
this.tileCoords = [];
this.stats = {};
this.total = 0;
}
initialize(features) {
this.splitTile(features, 0, 0, 0);
if (this.options.debug) {
if (features.length) console.log("features: %d, points: %d", this.tiles[0].numFeatures, this.tiles[0].numPoints);
console.timeEnd("generate tiles");
console.log("tiles generated:", this.total, JSON.stringify(this.stats));
}
}
/** {@inheritdoc} */
updateIndex(source, affected, options) {
if (options.debug > 1) {
console.log("invalidating tiles");
console.time("invalidating");
}
this.invalidateTiles(affected);
if (options.debug > 1) console.timeEnd("invalidating");
const [z, x, y] = [
0,
0,
0
];
const rootTile = createTile(source, z, x, y, options);
rootTile.source = source;
const id = toID(z, x, y);
this.tiles[id] = rootTile;
this.tileCoords.push({
z,
x,
y,
id
});
if (options.debug) {
const key = `z${z}`;
this.stats[key] = (this.stats[key] || 0) + 1;
this.total++;
}
}
/** {@inheritdoc} */
getClusterExpansionZoom(_clusterId) {
return null;
}
/** {@inheritdoc} */
getChildren(_clusterId) {
return null;
}
/** {@inheritdoc} */
getLeaves(_clusterId, _limit, _offset) {
return null;
}
/** {@inheritdoc} */
getTile(z, x, y) {
const { extent, debug } = this.options;
const z2 = 1 << z;
x = x + z2 & z2 - 1;
const id = toID(z, x, y);
if (this.tiles[id]) return transformTile(this.tiles[id], extent);
if (debug > 1) console.log("drilling down to z%d-%d-%d", z, x, y);
let z0 = z;
let x0 = x;
let y0 = y;
let parent;
while (!parent && z0 > 0) {
z0--;
x0 = x0 >> 1;
y0 = y0 >> 1;
parent = this.tiles[toID(z0, x0, y0)];
}
if (!parent?.source) return null;
if (debug > 1) {
console.log("found parent tile z%d-%d-%d", z0, x0, y0);
console.time("drilling down");
}
this.splitTile(parent.source, z0, x0, y0, z, x, y);
if (debug > 1) console.timeEnd("drilling down");
if (!this.tiles[id]) return null;
return transformTile(this.tiles[id], extent);
}
/**
* splits features from a parent tile to sub-tiles.
* z, x, and y are the coordinates of the parent tile
* cz, cx, and cy are the coordinates of the target tile
*
* If no target tile is specified, splitting stops when we reach the maximum
* zoom or the number of points is low as specified in the options.
* @internal
* @param features - features to split
* @param z - tile zoom level
* @param x - tile x coordinate
* @param y - tile y coordinate
* @param cz - target tile zoom level
* @param cx - target tile x coordinate
* @param cy - target tile y coordinate
*/
splitTile(features, z, x, y, cz, cx, cy) {
const stack = [
features,
z,
x,
y
];
const options = this.options;
const debug = options.debug;
while (stack.length) {
y = stack.pop();
x = stack.pop();
z = stack.pop();
features = stack.pop();
const z2 = 1 << z;
const id = toID(z, x, y);
let tile = this.tiles[id];
if (!tile) {
if (debug > 1) console.time("creation");
tile = this.tiles[id] = createTile(features, z, x, y, options);
this.tileCoords.push({
z,
x,
y,
id
});
if (debug) {
if (debug > 1) {
console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)", z, x, y, tile.numFeatures, tile.numPoints, tile.numSimplified);
console.timeEnd("creation");
}
const key = `z${z}`;
this.stats[key] = (this.stats[key] || 0) + 1;
this.total++;
}
}
tile.source = features;
if (cz == null) {
if (z === options.indexMaxZoom || tile.numPoints <= options.indexMaxPoints) continue;
} else if (z === options.maxZoom || z === cz) continue;
else if (cz != null) {
const zoomSteps = cz - z;
if (x !== cx >> zoomSteps || y !== cy >> zoomSteps) continue;
}
tile.source = null;
if (!features.length) continue;
if (debug > 1) console.time("clipping");
const k1 = .5 * options.buffer / options.extent;
const k2 = .5 - k1;
const k3 = .5 + k1;
const k4 = 1 + k1;
let tl = null;
let bl = null;
let tr = null;
let br = null;
const left = clip(features, z2, x - k1, x + k3, 0, tile.minX, tile.maxX, options);
const right = clip(features, z2, x + k2, x + k4, 0, tile.minX, tile.maxX, options);
if (left) {
tl = clip(left, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, options);
bl = clip(left, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, options);
}
if (right) {
tr = clip(right, z2, y - k1, y + k3, 1, tile.minY, tile.maxY, options);
br = clip(right, z2, y + k2, y + k4, 1, tile.minY, tile.maxY, options);
}
if (debug > 1) console.timeEnd("clipping");
stack.push(tl || [], z + 1, x * 2, y * 2);
stack.push(bl || [], z + 1, x * 2, y * 2 + 1);
stack.push(tr || [], z + 1, x * 2 + 1, y * 2);
stack.push(br || [], z + 1, x * 2 + 1, y * 2 + 1);
}
}
/**
* Invalidates (removes) tiles affected by the provided features
* @internal
* @param features
*/
invalidateTiles(features) {
if (!features.length) return;
const options = this.options;
const { debug } = options;
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
for (const feature of features) {
minX = Math.min(minX, feature.minX);
maxX = Math.max(maxX, feature.maxX);
minY = Math.min(minY, feature.minY);
maxY = Math.max(maxY, feature.maxY);
}
const k1 = options.buffer / options.extent;
const removedLookup = /* @__PURE__ */ new Set();
for (const id in this.tiles) {
const tile = this.tiles[id];
const z2 = 1 << tile.z;
const tileMinX = (tile.x - k1) / z2;
const tileMaxX = (tile.x + 1 + k1) / z2;
const tileMinY = (tile.y - k1) / z2;
const tileMaxY = (tile.y + 1 + k1) / z2;
if (maxX < tileMinX || minX >= tileMaxX || maxY < tileMinY || minY >= tileMaxY) continue;
let intersects = false;
for (const feature of features) if (feature.maxX >= tileMinX && feature.minX < tileMaxX && feature.maxY >= tileMinY && feature.minY < tileMaxY) {
intersects = true;
break;
}
if (!intersects) continue;
if (debug) {
if (debug > 1) console.log("invalidate tile z%d-%d-%d (features: %d, points: %d, simplified: %d)", tile.z, tile.x, tile.y, tile.numFeatures, tile.numPoints, tile.numSimplified);
const key = `z${tile.z}`;
this.stats[key] = (this.stats[key] || 0) - 1;
this.total--;
}
delete this.tiles[id];
removedLookup.add(id);
}
if (removedLookup.size) this.tileCoords = this.tileCoords.filter((c) => !removedLookup.has(c.id));
}
};
function toID(z, x, y) {
return ((1 << z) * y + x) * 32 + z;
}
const defaultOptions = {
maxZoom: 14,
indexMaxZoom: 5,
indexMaxPoints: 1e5,
tolerance: 3,
extent: 4096,
buffer: 64,
lineMetrics: false,
promoteId: null,
generateId: false,
updateable: false,
cluster: false,
clusterOptions: defaultClusterOptions,
debug: 0
};
/**
* Main class for creating and managing a vector tile index from GeoJSON data.
*/
var GeoJSONVT = class {
constructor(data, options) {
options = this.options = Object.assign({}, defaultOptions, options);
const debug = options.debug;
if (debug) console.time("preprocess data");
if (options.maxZoom < 0 || options.maxZoom > 24) throw new Error("maxZoom should be in the 0-24 range");
if (options.promoteId && options.generateId) throw new Error("promoteId and generateId cannot be used together.");
let features = convertToInternal(data, options);
if (debug) {
console.timeEnd("preprocess data");
console.log("index: maxZoom: %d, maxPoints: %d", options.indexMaxZoom, options.indexMaxPoints);
console.time("generate tiles");
}
features = wrap(features, options);
if (options.updateable) this.source = features;
this.initializeIndex(features, options);
}
initializeIndex(features, options) {
this.tileIndex = options.cluster ? new ClusterTileIndex(options.clusterOptions) : new TileIndex(options);
if (!features.length) return;
this.tileIndex.initialize(features);
}
/**
* Given z, x, and y tile coordinates, returns the corresponding tile with geometries in tile coordinates, much like MVT data is stored.
* @param z - tile zoom level
* @param x - tile x coordinate
* @param y - tile y coordinate
* @returns the transformed tile or null if not found
*/
getTile(z, x, y) {
z = +z;
x = +x;
y = +y;
if (z < 0 || z > 24) return null;
return this.tileIndex.getTile(z, x, y);
}
/**
* Updates the source data feature set using a {@link GeoJSONVTSourceDiff}
* @param diff - the source diff object
*/
updateData(diff, filter) {
const options = this.options;
if (!options.updateable) throw new Error("to update tile geojson `updateable` option must be set to true");
let { affected, source } = applySourceDiff(this.source, diff, options);
if (filter) ({affected, source} = this.filterUpdate(source, affected, filter));
if (!affected.length) return;
this.source = source;
this.tileIndex.updateIndex(source, affected, options);
}
/**
* Filter an update using a predicate function. Returns the affected and updated source features.
*/
filterUpdate(source, affected, predicate) {
const removeIds = /* @__PURE__ */ new Set();
for (const feature of source) {
if (feature.id == void 0) continue;
if (predicate(featureToGeoJSON(feature))) continue;
affected.push(feature);
removeIds.add(feature.id);
}
source = source.filter((feature) => !removeIds.has(feature.id));
return {
affected,
source
};
}
/**
* Returns source data as GeoJSON - only available when `updateable` option is set to true.
*/
getData() {
if (!this.options.updateable) throw new Error("to retrieve data the `updateable` option must be set to true");
return convertToGeoJSON(this.source);
}
/**
* Update supercluster options and regenerate the index.
* @param cluster - whether to enable clustering
* @param clusterOptions - {@link SuperclusterOptions}
*/
updateClusterOptions(cluster, clusterOptions) {
const wasCluster = this.options.cluster;
this.options.cluster = cluster;
this.options.clusterOptions = clusterOptions;
if (wasCluster == cluster) {
this.tileIndex.updateIndex(this.source, [], this.options);
return;
}
this.initializeIndex(this.source, this.options);
}
/**
* Returns the zoom level at which a cluster expands into multiple children.
* @param clusterId - The target cluster id.
* @returns the expansion zoom or null in case of non-clustered source
*/
getClusterExpansionZoom(clusterId) {
return this.tileIndex.getClusterExpansionZoom(clusterId);
}
/**
* Returns the immediate children (clusters or points) of a cluster as GeoJSON.
* @param clusterId - The target cluster id.
* @returns the immediate children or null in case of non-clustered source
*/
getClusterChildren(clusterId) {
return this.tileIndex.getChildren(clusterId);
}
/**
* Returns leaf point features under a cluster, paginated by `limit` and `offset`.
* @param clusterId - The target cluster id.
* @param limit - Maximum number of points to return (defaults to `10`).
* @param offset - Number of points to skip before collecting results (defaults to `0`).
* @returns leaf point features under a cluster or null in case of non-clustered source
*/
getClusterLeaves(clusterId, limit, offset) {
return this.tileIndex.getLeaves(clusterId, limit, offset);
}
};
//#endregion
//#region src/data/bucket/line_attributes.ts
const lineLayoutAttributes = createLayout([{
name: "a_pos_normal",
components: 2,
type: "Int16"
}, {
name: "a_data",
components: 4,
type: "Uint8"
}], 4);
const members$1 = lineLayoutAttributes.members;
lineLayoutAttributes.size;
lineLayoutAttributes.alignment;
//#endregion
//#region src/data/bucket/line_attributes_ext.ts
const lineLayoutAttributesExt = createLayout([{
name: "a_uv_x",
components: 1,
type: "Float32"
}, {
name: "a_split_index",
components: 1,
type: "Float32"
}]);
const members = lineLayoutAttributesExt.members;
lineLayoutAttributesExt.size;
lineLayoutAttributesExt.alignment;
//#endregion
//#region src/data/bucket/line_bucket.ts
const EXTRUDE_SCALE = 63;
const COS_HALF_SHARP_CORNER = Math.cos(75 / 2 * (Math.PI / 180));
const SHARP_CORNER_OFFSET = 15;
const DEG_PER_TRIANGLE = 20;
const LINE_DISTANCE_SCALE = 1 / 2;
const MAX_LINE_DISTANCE = Math.pow(2, 14) / LINE_DISTANCE_SCALE;
/**
* @internal
* Line bucket class
*/
var LineBucket = class {
constructor(options) {
this.zoom = options.zoom;
this.overscaling = options.overscaling;
this.layers = options.layers;
this.layerIds = this.layers.map((layer) => layer.id);
this.index = options.index;
this.hasDependencies = false;
this.patternFeatures = [];
this.lineClipsArray = [];
this.gradients = {};
for (const layer of this.layers) this.gradients[layer.id] = {};
this.layoutVertexArray = new LineLayoutArray();
this.layoutVertexArray2 = new LineExtLayoutArray();
this.indexArray = new TriangleIndexArray();
this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom);
this.segments = new SegmentVector();
this.maxLineLength = 0;
this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id);
}
populate(features, options, canonical) {
this.hasDependencies = hasPattern("line", this.layers, options) || this.hasLineDasharray(this.layers);
const lineSortKey = this.layers[0].layout.get("line-sort-key");
const sortFeaturesByKey = !lineSortKey.isConstant();
const bucketFeatures = [];
const globalProperties = new EvaluationParameters(this.zoom);
const needGeometry = this.layers[0]._featureFilter.needGeometry;
for (const { feature, id, index, sourceLayerIndex } of features) {
const evaluationFeature = toEvaluationFeature(feature, needGeometry);
if (!this.layers[0]._featureFilter.filter(globalProperties, evaluationFeature, canonical)) continue;
const sortKey = sortFeaturesByKey ? lineSortKey.evaluate(evaluationFeature, {}, canonical) : void 0;
const bucketFeature = {
id,
properties: feature.properties,
type: feature.type,
sourceLayerIndex,
index,
geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
patterns: {},
dashes: {},
sortKey
};
bucketFeatures.push(bucketFeature);
}
if (sortFeaturesByKey) bucketFeatures.sort((a, b) => {
return a.sortKey - b.sortKey;
});
for (const bucketFeature of bucketFeatures) {
const { geometry, index, sourceLayerIndex } = bucketFeature;
if (this.hasDependencies) {
if (hasPattern("line", this.layers, options)) addPatternDependencies("line", this.layers, bucketFeature, { zoom: this.zoom }, options);
else if (this.hasLineDasharray(this.layers)) this.addLineDashDependencies(this.layers, bucketFeature, this.zoom, options);
this.patternFeatures.push(bucketFeature);
} else this.addFeature(bucketFeature, geometry, index, canonical, {}, {}, options.subdivisionGranularity);
const feature = features[index].feature;
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
}
}
update(states, vtLayer, imagePositions, dashPositions) {
if (!this.stateDependentLayers.length) return;
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, {
imagePositions,
dashPositions
});
}
addFeatures(options, canonical, imagePositions, dashPositions) {
for (const feature of this.patternFeatures) this.addFeature(feature, feature.geometry, feature.index, canonical, imagePositions, dashPositions, options.subdivisionGranularity);
}
isEmpty() {
return this.layoutVertexArray.length === 0;
}
uploadPending() {
return !this.uploaded || this.programConfigurations.needsUpload;
}
upload(context) {
if (!this.uploaded) {
if (this.layoutVertexArray2.length !== 0) this.layoutVertexBuffer2 = context.createVertexBuffer(this.layoutVertexArray2, members);
this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, members$1);
this.indexBuffer = context.createIndexBuffer(this.indexArray);
}
this.programConfigurations.upload(context);
this.uploaded = true;
}
destroy() {
if (!this.layoutVertexBuffer) return;
this.layoutVertexBuffer.destroy();
this.indexBuffer.destroy();
this.programConfigurations.destroy();
this.segments.destroy();
}
lineFeatureClips(feature) {
if (!!feature.properties && Object.hasOwn(feature.properties, "geojsonvt_clip_start") && Object.hasOwn(feature.properties, "geojsonvt_clip_end")) return {
start: +feature.properties[GEOJSONVT_CLIP_START],
end: +feature.properties[GEOJSONVT_CLIP_END]
};
}
addFeature(feature, geometry, index, canonical, imagePositions, dashPositions, subdivisionGranularity) {
const layout = this.layers[0].layout;
const join = layout.get("line-join").evaluate(feature, {});
const cap = layout.get("line-cap").evaluate(feature, {});
const miterLimit = layout.get("line-miter-limit").evaluate(feature, {});
const roundLimit = layout.get("line-round-limit").evaluate(feature, {});
this.lineClips = this.lineFeatureClips(feature);
for (const line of geometry) this.addLine(line, feature, join, cap, miterLimit, roundLimit, canonical, subdivisionGranularity);
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {
imagePositions,
dashPositions,
canonical
});
}
addLine(vertices, feature, join, cap, miterLimit, roundLimit, canonical, subdivisionGranularity) {
this.distance = 0;
this.scaledDistance = 0;
this.totalDistance = 0;
const granularity = canonical ? subdivisionGranularity.line.getGranularityForZoomLevel(canonical.z) : 1;
vertices = subdivideVertexLine(vertices, granularity);
if (this.lineClips) {
this.lineClipsArray.push(this.lineClips);
for (let i = 0; i < vertices.length - 1; i++) this.totalDistance += vertices[i].dist(vertices[i + 1]);
this.updateScaledDistance();
this.maxLineLength = Math.max(this.maxLineLength, this.totalDistance);
}
const isPolygon = VectorTileFeature.types[feature.type] === "Polygon";
let len = vertices.length;
while (len >= 2 && vertices[len - 1].equals(vertices[len - 2])) len--;
let first = 0;
while (first < len - 1 && vertices[first].equals(vertices[first + 1])) first++;
if (len - first < (isPolygon ? 3 : 2)) return;
if (join === "bevel") miterLimit = 1.05;
const sharpCornerOffset = this.overscaling <= 16 ? SHARP_CORNER_OFFSET * EXTENT$1 / (512 * this.overscaling) : 0;
const segment = this.segments.prepareSegment(len * 10, this.layoutVertexArray, this.indexArray);
let currentVertex;
let prevVertex;
let nextVertex;
let prevNormal;
let nextNormal;
this.e1 = this.e2 = -1;
if (isPolygon) {
currentVertex = vertices[len - 2];
nextNormal = vertices[first].sub(currentVertex)._unit()._perp();
}
for (let i = first; i < len; i++) {
nextVertex = i === len - 1 ? isPolygon ? vertices[first + 1] : void 0 : vertices[i + 1];
if (nextVertex && vertices[i].equals(nextVertex)) continue;
if (nextNormal) prevNormal = nextNormal;
if (currentVertex) prevVertex = currentVertex;
currentVertex = vertices[i];
nextNormal = nextVertex ? nextVertex.sub(currentVertex)._unit()._perp() : prevNormal;
prevNormal ||= nextNormal;
let joinNormal = prevNormal.add(nextNormal);
if (joinNormal.x !== 0 || joinNormal.y !== 0) joinNormal._unit();
const cosAngle = prevNormal.x * nextNormal.x + prevNormal.y * nextNormal.y;
const cosHalfAngle = joinNormal.x * nextNormal.x + joinNormal.y * nextNormal.y;
const miterLength = cosHalfAngle !== 0 ? 1 / cosHalfAngle : Infinity;
const approxAngle = 2 * Math.sqrt(2 - 2 * cosHalfAngle);
const isSharpCorner = cosHalfAngle < COS_HALF_SHARP_CORNER && prevVertex && nextVertex;
const lineTurnsLeft = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x > 0;
if (isSharpCorner && i > first) {
const prevSegmentLength = currentVertex.dist(prevVertex);
if (prevSegmentLength > 2 * sharpCornerOffset) {
const newPrevVertex = currentVertex.sub(currentVertex.sub(prevVertex)._mult(sharpCornerOffset / prevSegmentLength)._round());
this.updateDistance(prevVertex, newPrevVertex);
this.addCurrentVertex(newPrevVertex, prevNormal, 0, 0, segment);
prevVertex = newPrevVertex;
}
}
const middleVertex = prevVertex && nextVertex;
let currentJoin = middleVertex ? join : isPolygon ? "butt" : cap;
if (middleVertex && currentJoin === "round") {
if (miterLength < roundLimit) currentJoin = "miter";
else if (miterLength <= 2) currentJoin = "fakeround";
}
if (currentJoin === "miter" && miterLength > miterLimit) currentJoin = "bevel";
if (currentJoin === "bevel") {
if (miterLength > 2) currentJoin = "flipbevel";
if (miterLength < miterLimit) currentJoin = "miter";
}
if (prevVertex) this.updateDistance(prevVertex, currentVertex);
if (currentJoin === "miter") {
joinNormal._mult(miterLength);
this.addCurrentVertex(currentVertex, joinNormal, 0, 0, segment);
} else if (currentJoin === "flipbevel") {
if (miterLength > 100) joinNormal = nextNormal.mult(-1);
else {
const bevelLength = miterLength * prevNormal.add(nextNormal).mag() / prevNormal.sub(nextNormal).mag();
joinNormal._perp()._mult(bevelLength * (lineTurnsLeft ? -1 : 1));
}
this.addCurrentVertex(currentVertex, joinNormal, 0, 0, segment);
this.addCurrentVertex(currentVertex, joinNormal.mult(-1), 0, 0, segment);
} else if (currentJoin === "bevel" || currentJoin === "fakeround") {
const offset = -Math.sqrt(miterLength * miterLength - 1);
const offsetA = lineTurnsLeft ? offset : 0;
const offsetB = lineTurnsLeft ? 0 : offset;
if (prevVertex) this.addCurrentVertex(currentVertex, prevNormal, offsetA, offsetB, segment);
if (currentJoin === "fakeround") {
const n = Math.round(approxAngle * 180 / Math.PI / DEG_PER_TRIANGLE);
for (let m = 1; m < n; m++) {
let t = m / n;
if (t !== .5) {
const t2 = t - .5;
const A = 1.0904 + cosAngle * (-3.2452 + cosAngle * (3.55645 - cosAngle * 1.43519));
const B = .848013 + cosAngle * (-1.06021 + cosAngle * .215638);
t = t + t * t2 * (t - 1) * (A * t2 * t2 + B);
}
const extrude = nextNormal.sub(prevNormal)._mult(t)._add(prevNormal)._unit()._mult(lineTurnsLeft ? -1 : 1);
this.addHalfVertex(currentVertex, extrude.x, extrude.y, false, lineTurnsLeft, 0, segment);
}
}
if (nextVertex) this.addCurrentVertex(currentVertex, nextNormal, -offsetA, -offsetB, segment);
} else if (currentJoin === "butt") this.addCurrentVertex(currentVertex, joinNormal, 0, 0, segment);
else if (currentJoin === "square") {
const offset = prevVertex ? 1 : -1;
this.addCurrentVertex(currentVertex, joinNormal, offset, offset, segment);
} else if (currentJoin === "round") {
if (prevVertex) {
this.addCurrentVertex(currentVertex, prevNormal, 0, 0, segment);
this.addCurrentVertex(currentVertex, prevNormal, 1, 1, segment, true);
}
if (nextVertex) {
this.addCurrentVertex(currentVertex, nextNormal, -1, -1, segment, true);
this.addCurrentVertex(currentVertex, nextNormal, 0, 0, segment);
}
}
if (isSharpCorner && i < len - 1) {
const nextSegmentLength = currentVertex.dist(nextVertex);
if (nextSegmentLength > 2 * sharpCornerOffset) {
const newCurrentVertex = currentVertex.add(nextVertex.sub(currentVertex)._mult(sharpCornerOffset / nextSegmentLength)._round());
this.updateDistance(currentVertex, newCurrentVertex);
this.addCurrentVertex(newCurrentVertex, nextNormal, 0, 0, segment);
currentVertex = newCurrentVertex;
}
}
}
}
/**
* Add two vertices to the buffers.
*
* @param p - the line vertex to add buffer vertices for
* @param normal - vertex normal
* @param endLeft - extrude to shift the left vertex along the line
* @param endRight - extrude to shift the left vertex along the line
* @param segment - the segment object to add the vertex to
* @param round - whether this is a round cap
*/
addCurrentVertex(p, normal, endLeft, endRight, segment, round = false) {
const leftX = normal.x + normal.y * endLeft;
const leftY = normal.y - normal.x * endLeft;
const rightX = -normal.x + normal.y * endRight;
const rightY = -normal.y - normal.x * endRight;
this.addHalfVertex(p, leftX, leftY, round, false, endLeft, segment);
this.addHalfVertex(p, rightX, rightY, round, true, -endRight, segment);
if (this.distance > MAX_LINE_DISTANCE / 2 && this.totalDistance === 0) {
this.distance = 0;
this.updateScaledDistance();
this.addCurrentVertex(p, normal, endLeft, endRight, segment, round);
}
}
addHalfVertex({ x, y }, extrudeX, extrudeY, round, up, dir, segment) {
const linesofarScaled = (this.lineClips ? this.scaledDistance * (MAX_LINE_DISTANCE - 1) : this.scaledDistance) * LINE_DISTANCE_SCALE;
this.layoutVertexArray.emplaceBack((x << 1) + (round ? 1 : 0), (y << 1) + (up ? 1 : 0), Math.round(EXTRUDE_SCALE * extrudeX) + 128, Math.round(EXTRUDE_SCALE * extrudeY) + 128, (dir === 0 ? 0 : dir < 0 ? -1 : 1) + 1 | (linesofarScaled & 63) << 2, linesofarScaled >> 6);
if (this.lineClips) {
const uvX = (this.scaledDistance - this.lineClips.start) / (this.lineClips.end - this.lineClips.start);
this.layoutVertexArray2.emplaceBack(uvX, this.lineClipsArray.length);
}
const e = segment.vertexLength++;
if (this.e1 >= 0 && this.e2 >= 0) {
this.indexArray.emplaceBack(this.e1, e, this.e2);
segment.primitiveLength++;
}
if (up) this.e2 = e;
else this.e1 = e;
}
updateScaledDistance() {
this.scaledDistance = this.lineClips ? this.lineClips.start + (this.lineClips.end - this.lineClips.start) * this.distance / this.totalDistance : this.distance;
}
updateDistance(prev, next) {
this.distance += prev.dist(next);
this.updateScaledDistance();
}
hasLineDasharray(layers) {
for (const layer of layers) {
const dasharrayProperty = layer.paint.get("line-dasharray");
if (dasharrayProperty && !dasharrayProperty.isConstant()) return true;
}
return false;
}
addLineDashDependencies(layers, bucketFeature, zoom, options) {
for (const layer of layers) {
const dasharrayProperty = layer.paint.get("line-dasharray");
if (!dasharrayProperty || dasharrayProperty.value.kind === "constant") continue;
const round = layer.layout.get("line-cap").evaluate(bucketFeature, {}) === "round";
const min = {
dasharray: dasharrayProperty.value.evaluate({ zoom: zoom - 1 }, bucketFeature, {}),
round
};
const mid = {
dasharray: dasharrayProperty.value.evaluate({ zoom }, bucketFeature, {}),
round
};
const max = {
dasharray: dasharrayProperty.value.evaluate({ zoom: zoom + 1 }, bucketFeature, {}),
round
};
const minKey = `${min.dasharray.join(",")},${min.round}`;
const midKey = `${mid.dasharray.join(",")},${mid.round}`;
const maxKey = `${max.dasharray.join(",")},${max.round}`;
options.dashDependencies[minKey] = min;
options.dashDependencies[midKey] = mid;
options.dashDependencies[maxKey] = max;
bucketFeature.dashes[layer.id] = {
min: minKey,
mid: midKey,
max: maxKey
};
}
}
};
register("LineBucket", LineBucket, { omit: ["layers", "patternFeatures"] });
//#endregion
//#region src/style/style_layer/line_style_layer_properties.g.ts
let layout$1;
const getLayout$1 = () => layout$1 = layout$1 || new Properties({
"line-cap": new DataDrivenProperty(latest["layout_line"]["line-cap"], "line-cap"),
"line-join": new DataDrivenProperty(latest["layout_line"]["line-join"], "line-join"),
"line-miter-limit": new DataDrivenProperty(latest["layout_line"]["line-miter-limit"], "line-miter-limit"),
"line-round-limit": new DataDrivenProperty(latest["layout_line"]["line-round-limit"], "line-round-limit"),
"line-sort-key": new DataDrivenProperty(latest["layout_line"]["line-sort-key"], "line-sort-key")
});
let paint$2;
const getPaint$2 = () => paint$2 = paint$2 || new Properties({
"line-opacity": new DataDrivenProperty(latest["paint_line"]["line-opacity"], "line-opacity"),
"line-layer-opacity": new DataConstantProperty(latest["paint_line"]["line-layer-opacity"], "line-layer-opacity"),
"line-color": new DataDrivenProperty(latest["paint_line"]["line-color"], "line-color"),
"line-translate": new DataConstantProperty(latest["paint_line"]["line-translate"], "line-translate"),
"line-translate-anchor": new DataConstantProperty(latest["paint_line"]["line-translate-anchor"], "line-translate-anchor"),
"line-width": new DataDrivenProperty(latest["paint_line"]["line-width"], "line-width"),
"line-gap-width": new DataDrivenProperty(latest["paint_line"]["line-gap-width"], "line-gap-width"),
"line-offset": new DataDrivenProperty(latest["paint_line"]["line-offset"], "line-offset"),
"line-blur": new DataDrivenProperty(latest["paint_line"]["line-blur"], "line-blur"),
"line-dasharray": new CrossFadedDataDrivenProperty(latest["paint_line"]["line-dasharray"], "line-dasharray"),
"line-pattern": new CrossFadedDataDrivenProperty(latest["paint_line"]["line-pattern"], "line-pattern"),
"line-gradient": new ColorRampProperty(latest["paint_line"]["line-gradient"], "line-gradient")
});
var line_style_layer_properties_g_default = {
get paint() {
return getPaint$2();
},
get layout() {
return getLayout$1();
}
};
//#endregion
//#region src/style/style_layer/line_style_layer.ts
var LineFloorwidthProperty = class extends DataDrivenProperty {
possiblyEvaluate(value, parameters) {
parameters = new EvaluationParameters(Math.floor(parameters.zoom), {
now: parameters.now,
fadeDuration: parameters.fadeDuration,
zoomHistory: parameters.zoomHistory,
transition: parameters.transition
});
return super.possiblyEvaluate(value, parameters);
}
evaluate(value, globals, feature, featureState) {
globals = extend({}, globals, { zoom: Math.floor(globals.zoom) });
return super.evaluate(value, globals, feature, featureState);
}
};
let lineFloorwidthProperty;
const isLineStyleLayer = (layer) => layer.type === "line";
var LineStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, line_style_layer_properties_g_default, globalState);
this.gradientVersion = 0;
if (!lineFloorwidthProperty) {
lineFloorwidthProperty = new LineFloorwidthProperty(line_style_layer_properties_g_default.paint.properties["line-width"].specification, "line-floorwidth");
lineFloorwidthProperty.useIntegerZoom = true;
}
}
_handleSpecialPaintPropertyUpdate(name) {
if (name === "line-gradient") {
const expression = this.gradientExpression();
if (isZoomExpression(expression)) this.stepInterpolant = expression._styleExpression.expression instanceof Step;
else this.stepInterpolant = false;
this.gradientVersion = (this.gradientVersion + 1) % Number.MAX_SAFE_INTEGER;
}
}
gradientExpression() {
return this._transitionablePaint._values["line-gradient"].value.expression;
}
recalculate(parameters, availableImages) {
super.recalculate(parameters, availableImages);
this.paint._values["line-floorwidth"] = lineFloorwidthProperty.possiblyEvaluate(this._transitioningPaint._values["line-width"].value, parameters);
}
createBucket(parameters) {
return new LineBucket(parameters);
}
queryRadius(bucket) {
const lineBucket = bucket;
const width = getLineWidth(getMaximumPaintValue("line-width", this, lineBucket), getMaximumPaintValue("line-gap-width", this, lineBucket));
const offset = getMaximumPaintValue("line-offset", this, lineBucket);
return width / 2 + Math.abs(offset) + translateDistance(this.paint.get("line-translate"));
}
queryIntersectsFeature({ queryGeometry, feature, featureState, geometry, transform, pixelsToTileUnits }) {
const translatedPolygon = translate(queryGeometry, this.paint.get("line-translate"), this.paint.get("line-translate-anchor"), -transform.bearingInRadians, pixelsToTileUnits);
const halfWidth = pixelsToTileUnits / 2 * getLineWidth(this.paint.get("line-width").evaluate(feature, featureState), this.paint.get("line-gap-width").evaluate(feature, featureState));
const lineOffset = this.paint.get("line-offset").evaluate(feature, featureState);
if (lineOffset) geometry = offsetLine(geometry, lineOffset * pixelsToTileUnits);
return polygonIntersectsBufferedMultiLine(translatedPolygon, geometry, halfWidth);
}
isTileClipped() {
return true;
}
};
function getLineWidth(lineWidth, lineGapWidth) {
if (lineGapWidth > 0) return lineGapWidth + 2 * lineWidth;
else return lineWidth;
}
//#endregion
//#region src/data/bucket/symbol_attributes.ts
const symbolLayoutAttributes = createLayout([
{
name: "a_pos_offset",
components: 4,
type: "Int16"
},
{
name: "a_data",
components: 4,
type: "Uint16"
},
{
name: "a_pixeloffset",
components: 4,
type: "Int16"
}
], 4);
const dynamicLayoutAttributes = createLayout([{
name: "a_projected_pos",
components: 3,
type: "Float32"
}], 4);
createLayout([{
name: "a_fade_opacity",
components: 1,
type: "Uint32"
}], 4);
const collisionVertexAttributes = createLayout([
{
name: "a_placed",
components: 2,
type: "Uint8"
},
{
name: "a_shift",
components: 2,
type: "Float32"
},
{
name: "a_box_real",
components: 2,
type: "Int16"
}
]);
createLayout([
{
type: "Int16",
name: "anchorPointX"
},
{
type: "Int16",
name: "anchorPointY"
},
{
type: "Int16",
name: "x1"
},
{
type: "Int16",
name: "y1"
},
{
type: "Int16",
name: "x2"
},
{
type: "Int16",
name: "y2"
},
{
type: "Uint32",
name: "featureIndex"
},
{
type: "Uint16",
name: "sourceLayerIndex"
},
{
type: "Uint16",
name: "bucketIndex"
}
]);
const collisionBoxLayout = createLayout([
{
name: "a_pos",
components: 2,
type: "Int16"
},
{
name: "a_anchor_pos",
components: 2,
type: "Int16"
},
{
name: "a_extrude",
components: 2,
type: "Int16"
}
], 4);
const collisionCircleLayout = createLayout([
{
name: "a_pos",
components: 2,
type: "Float32"
},
{
name: "a_radius",
components: 1,
type: "Float32"
},
{
name: "a_flags",
components: 2,
type: "Int16"
}
], 4);
createLayout([{
name: "triangle",
components: 3,
type: "Uint16"
}]);
createLayout([
{
type: "Int16",
name: "anchorX"
},
{
type: "Int16",
name: "anchorY"
},
{
type: "Uint16",
name: "glyphStartIndex"
},
{
type: "Uint16",
name: "numGlyphs"
},
{
type: "Uint32",
name: "vertexStartIndex"
},
{
type: "Uint32",
name: "lineStartIndex"
},
{
type: "Uint32",
name: "lineLength"
},
{
type: "Uint16",
name: "segment"
},
{
type: "Uint16",
name: "lowerSize"
},
{
type: "Uint16",
name: "upperSize"
},
{
type: "Float32",
name: "lineOffsetX"
},
{
type: "Float32",
name: "lineOffsetY"
},
{
type: "Uint8",
name: "writingMode"
},
{
type: "Uint8",
name: "placedOrientation"
},
{
type: "Uint8",
name: "hidden"
},
{
type: "Uint32",
name: "crossTileID"
},
{
type: "Int16",
name: "associatedIconIndex"
}
]);
createLayout([
{
type: "Int16",
name: "anchorX"
},
{
type: "Int16",
name: "anchorY"
},
{
type: "Int16",
name: "rightJustifiedTextSymbolIndex"
},
{
type: "Int16",
name: "centerJustifiedTextSymbolIndex"
},
{
type: "Int16",
name: "leftJustifiedTextSymbolIndex"
},
{
type: "Int16",
name: "verticalPlacedTextSymbolIndex"
},
{
type: "Int16",
name: "placedIconSymbolIndex"
},
{
type: "Int16",
name: "verticalPlacedIconSymbolIndex"
},
{
type: "Uint16",
name: "key"
},
{
type: "Uint16",
name: "textBoxStartIndex"
},
{
type: "Uint16",
name: "textBoxEndIndex"
},
{
type: "Uint16",
name: "verticalTextBoxStartIndex"
},
{
type: "Uint16",
name: "verticalTextBoxEndIndex"
},
{
type: "Uint16",
name: "iconBoxStartIndex"
},
{
type: "Uint16",
name: "iconBoxEndIndex"
},
{
type: "Uint16",
name: "verticalIconBoxStartIndex"
},
{
type: "Uint16",
name: "verticalIconBoxEndIndex"
},
{
type: "Uint16",
name: "featureIndex"
},
{
type: "Uint16",
name: "numHorizontalGlyphVertices"
},
{
type: "Uint16",
name: "numVerticalGlyphVertices"
},
{
type: "Uint16",
name: "numIconVertices"
},
{
type: "Uint16",
name: "numVerticalIconVertices"
},
{
type: "Uint16",
name: "useRuntimeCollisionCircles"
},
{
type: "Uint32",
name: "crossTileID"
},
{
type: "Float32",
name: "textBoxScale"
},
{
type: "Float32",
name: "collisionCircleDiameter"
},
{
type: "Uint16",
name: "textAnchorOffsetStartIndex"
},
{
type: "Uint16",
name: "textAnchorOffsetEndIndex"
}
]);
createLayout([{
type: "Float32",
name: "offsetX"
}]);
createLayout([
{
type: "Int16",
name: "x"
},
{
type: "Int16",
name: "y"
},
{
type: "Int16",
name: "tileUnitDistanceFromAnchor"
}
]);
createLayout([{
type: "Uint16",
name: "textAnchor"
}, {
type: "Float32",
components: 2,
name: "textOffset"
}]);
//#endregion
//#region src/symbol/transform_text.ts
function transformTextInternal(text, layer, feature) {
const transform = layer.layout.get("text-transform").evaluate(feature, {});
if (transform === "uppercase") text = text.toLocaleUpperCase();
else if (transform === "lowercase") text = text.toLocaleLowerCase();
if (rtlWorkerPlugin.applyArabicShaping) text = rtlWorkerPlugin.applyArabicShaping(text);
return text;
}
function transformText(text, layer, feature) {
for (const section of text.sections) section.text = transformTextInternal(section.text, layer, feature);
return text;
}
//#endregion
//#region src/symbol/merge_lines.ts
function mergeLines(features) {
const leftIndex = {};
const rightIndex = {};
const mergedFeatures = [];
let mergedIndex = 0;
function add(k) {
mergedFeatures.push(features[k]);
mergedIndex++;
}
function mergeFromRight(leftKey, rightKey, geom) {
const i = rightIndex[leftKey];
delete rightIndex[leftKey];
rightIndex[rightKey] = i;
mergedFeatures[i].geometry[0].pop();
mergedFeatures[i].geometry[0] = mergedFeatures[i].geometry[0].concat(geom[0]);
return i;
}
function mergeFromLeft(leftKey, rightKey, geom) {
const i = leftIndex[rightKey];
delete leftIndex[rightKey];
leftIndex[leftKey] = i;
mergedFeatures[i].geometry[0].shift();
mergedFeatures[i].geometry[0] = geom[0].concat(mergedFeatures[i].geometry[0]);
return i;
}
function getKey(text, geom, onRight) {
const point = onRight ? geom[0][geom[0].length - 1] : geom[0][0];
return `${text}:${point.x}:${point.y}`;
}
for (let k = 0; k < features.length; k++) {
const feature = features[k];
const geom = feature.geometry;
const text = feature.text ? feature.text.toString() : null;
if (!text) {
add(k);
continue;
}
const leftKey = getKey(text, geom), rightKey = getKey(text, geom, true);
if (leftKey in rightIndex && rightKey in leftIndex && rightIndex[leftKey] !== leftIndex[rightKey]) {
const j = mergeFromLeft(leftKey, rightKey, geom);
const i = mergeFromRight(leftKey, rightKey, mergedFeatures[j].geometry);
delete leftIndex[leftKey];
delete rightIndex[rightKey];
rightIndex[getKey(text, mergedFeatures[i].geometry, true)] = i;
mergedFeatures[j].geometry = null;
} else if (leftKey in rightIndex) mergeFromRight(leftKey, rightKey, geom);
else if (rightKey in leftIndex) mergeFromLeft(leftKey, rightKey, geom);
else {
add(k);
leftIndex[leftKey] = mergedIndex - 1;
rightIndex[rightKey] = mergedIndex - 1;
}
}
return mergedFeatures.filter((f) => f.geometry);
}
//#endregion
//#region src/util/verticalize_punctuation.ts
const verticalizedCharacterMap = {
"!": "︕",
"#": "#",
"$": "$",
"%": "%",
"&": "&",
"(": "︵",
")": "︶",
"*": "*",
"+": "+",
",": "︐",
"-": "︲",
".": "・",
"/": "/",
":": "︓",
";": "︔",
"<": "︿",
"=": "=",
">": "﹀",
"?": "︖",
"@": "@",
"[": "﹇",
"\\": "\",
"]": "﹈",
"^": "^",
"_": "︳",
"`": "`",
"{": "︷",
"|": "―",
"}": "︸",
"~": "~",
"¢": "¢",
"£": "£",
"¥": "¥",
"¦": "¦",
"¬": "¬",
"¯": " ̄",
"–": "︲",
"—": "︱",
"‘": "﹃",
"’": "﹄",
"“": "﹁",
"”": "﹂",
"…": "︙",
"⋯": "︙",
"‧": "・",
"₩": "₩",
"、": "︑",
"。": "︒",
"〈": "︿",
"〉": "﹀",
"《": "︽",
"》": "︾",
"「": "﹁",
"」": "﹂",
"『": "﹃",
"』": "﹄",
"【": "︻",
"】": "︼",
"〔": "︹",
"〕": "︺",
"〖": "︗",
"〗": "︘",
"!": "︕",
"(": "︵",
")": "︶",
",": "︐",
"-": "︲",
".": "・",
":": "︓",
";": "︔",
"<": "︿",
">": "﹀",
"?": "︖",
"[": "﹇",
"]": "﹈",
"_": "︳",
"{": "︷",
"|": "―",
"}": "︸",
"⦅": "︵",
"⦆": "︶",
"。": "︒",
"「": "﹁",
"」": "﹂"
};
function verticalizePunctuation(input) {
let output = "";
let prevChar = {
premature: true,
value: void 0
};
const chars = input[Symbol.iterator]();
let char = chars.next();
const nextChars = input[Symbol.iterator]();
nextChars.next();
let nextChar = nextChars.next();
while (!char.done) {
if ((nextChar.done || !charHasRotatedVerticalOrientation(nextChar.value.codePointAt(0)) || verticalizedCharacterMap[nextChar.value]) && (prevChar.premature || !charHasRotatedVerticalOrientation(prevChar.value.codePointAt(0)) || verticalizedCharacterMap[prevChar.value]) && verticalizedCharacterMap[char.value]) output += verticalizedCharacterMap[char.value];
else output += char.value;
prevChar = {
value: char.value,
premature: false
};
char = chars.next();
nextChar = nextChars.next();
}
return output;
}
//#endregion
//#region src/symbol/tagged_string.ts
const PUAbegin = 57344;
const PUAend = 63743;
const breakable = {
[10]: true,
[32]: true,
[38]: true,
[41]: true,
[43]: true,
[45]: true,
[47]: true,
[173]: true,
[183]: true,
[8203]: true,
[8208]: true,
[8211]: true,
[8231]: true
};
const breakableBefore = { [40]: true };
function getGlyphAdvance(codePoint, section, glyphMap, imagePositions, spacing, layoutTextSize) {
if ("fontStack" in section) {
const glyph = glyphMap[section.fontStack]?.[codePoint];
if (!glyph) return 0;
return glyph.metrics.advance * section.scale + spacing;
} else {
const imagePosition = imagePositions[section.imageName];
if (!imagePosition) return 0;
return imagePosition.displaySize[0] * section.scale * 24 / layoutTextSize + spacing;
}
}
function calculateBadness(lineWidth, targetWidth, penalty, isLastBreak) {
const raggedness = Math.pow(lineWidth - targetWidth, 2);
if (isLastBreak) {
if (lineWidth < targetWidth) return raggedness / 2;
else return raggedness * 2;
}
return raggedness + Math.abs(penalty) * penalty;
}
function calculatePenalty(codePoint, nextCodePoint, penalizableIdeographicBreak) {
let penalty = 0;
if (codePoint === 10) penalty -= 1e4;
if (penalizableIdeographicBreak) penalty += 150;
if (codePoint === 40 || codePoint === 65288) penalty += 50;
if (nextCodePoint === 41 || nextCodePoint === 65289) penalty += 50;
return penalty;
}
function evaluateBreak(breakIndex, breakX, targetWidth, potentialBreaks, penalty, isLastBreak) {
let bestPriorBreak = null;
let bestBreakBadness = calculateBadness(breakX, targetWidth, penalty, isLastBreak);
for (const potentialBreak of potentialBreaks) {
const breakBadness = calculateBadness(breakX - potentialBreak.x, targetWidth, penalty, isLastBreak) + potentialBreak.badness;
if (breakBadness <= bestBreakBadness) {
bestPriorBreak = potentialBreak;
bestBreakBadness = breakBadness;
}
}
return {
index: breakIndex,
x: breakX,
priorBreak: bestPriorBreak,
badness: bestBreakBadness
};
}
function leastBadBreaks(lastLineBreak) {
if (!lastLineBreak) return [];
return leastBadBreaks(lastLineBreak.priorBreak).concat(lastLineBreak.index);
}
var TaggedString = class TaggedString {
constructor(text = "", sections = [], sectionIndex = []) {
this.text = text;
this.sections = sections;
this.sectionIndex = sectionIndex;
this.imageSectionID = null;
}
static fromFeature(text, defaultFontStack) {
const result = new TaggedString();
for (const section of text.sections) if (!section.image) result.addTextSection(section, defaultFontStack);
else result.addImageSection(section);
return result;
}
length() {
return [...this.text].length;
}
getSection(index) {
return this.sections[this.sectionIndex[index]];
}
getSectionIndex(index) {
return this.sectionIndex[index];
}
verticalizePunctuation() {
this.text = verticalizePunctuation(this.text);
}
/**
* Returns whether the text contains zero-width spaces.
*
* Some tilesets such as Streets insert ZWSPs as hints for line
* breaking in CJK text.
*/
hasZeroWidthSpaces() {
return this.text.includes("");
}
trim() {
const leadingWhitespace = this.text.match(/^\s*/);
const leadingLength = leadingWhitespace ? leadingWhitespace[0].length : 0;
const trailingWhitespace = this.text.match(/\S\s*$/);
const trailingLength = trailingWhitespace ? trailingWhitespace[0].length - 1 : 0;
this.text = this.text.substring(leadingLength, this.text.length - trailingLength);
this.sectionIndex = this.sectionIndex.slice(leadingLength, this.sectionIndex.length - trailingLength);
}
substring(start, end) {
const text = [...this.text].slice(start, end).join("");
const sectionIndex = this.sectionIndex.slice(start, end);
return new TaggedString(text, this.sections, sectionIndex);
}
/**
* Converts a UTF-16 character index to a UTF-16 code unit (JavaScript character index).
*/
toCodeUnitIndex(unicodeIndex) {
return [...this.text].slice(0, unicodeIndex).join("").length;
}
toString() {
return this.text;
}
getMaxScale() {
return this.sectionIndex.reduce((max, index) => Math.max(max, this.sections[index].scale), 0);
}
getMaxImageSize(imagePositions) {
let maxImageWidth = 0;
let maxImageHeight = 0;
for (let i = 0; i < this.length(); i++) {
const section = this.getSection(i);
if ("imageName" in section) {
const imagePosition = imagePositions[section.imageName];
if (!imagePosition) continue;
const size = imagePosition.displaySize;
maxImageWidth = Math.max(maxImageWidth, size[0]);
maxImageHeight = Math.max(maxImageHeight, size[1]);
}
}
return {
maxImageWidth,
maxImageHeight
};
}
addTextSection(section, defaultFontStack) {
this.text += section.text;
this.sections.push({
scale: section.scale || 1,
verticalAlign: section.verticalAlign || "bottom",
fontStack: section.fontStack || defaultFontStack
});
const index = this.sections.length - 1;
this.sectionIndex.push(...[...section.text].map(() => index));
}
addImageSection(section) {
const imageName = section.image ? section.image.name : "";
if (imageName.length === 0) {
warnOnce("Can't add FormattedSection with an empty image.");
return;
}
const nextImageSectionCharCode = this.getNextImageSectionCharCode();
if (!nextImageSectionCharCode) {
warnOnce(`Reached maximum number of images 6401`);
return;
}
this.text += String.fromCharCode(nextImageSectionCharCode);
this.sections.push({
scale: 1,
verticalAlign: section.verticalAlign || "bottom",
imageName
});
this.sectionIndex.push(this.sections.length - 1);
}
getNextImageSectionCharCode() {
if (!this.imageSectionID) {
this.imageSectionID = PUAbegin;
return this.imageSectionID;
}
if (this.imageSectionID >= PUAend) return null;
return ++this.imageSectionID;
}
determineLineBreaks(spacing, maxWidth, glyphMap, imagePositions, layoutTextSize) {
const potentialLineBreaks = [];
const targetWidth = this.determineAverageLineWidth(spacing, maxWidth, glyphMap, imagePositions, layoutTextSize);
const hasZeroWidthSpaces = this.hasZeroWidthSpaces();
let currentX = 0;
let i = 0;
const chars = this.text[Symbol.iterator]();
let char = chars.next();
const nextChars = this.text[Symbol.iterator]();
nextChars.next();
let nextChar = nextChars.next();
const nextNextChars = this.text[Symbol.iterator]();
nextNextChars.next();
nextNextChars.next();
let nextNextChar = nextNextChars.next();
while (!char.done) {
const section = this.getSection(i);
const codePoint = char.value.codePointAt(0);
if (!charIsWhitespace(codePoint)) currentX += getGlyphAdvance(codePoint, section, glyphMap, imagePositions, spacing, layoutTextSize);
if (!nextChar.done) {
const ideographicBreak = codePointAllowsIdeographicBreaking(codePoint);
const nextCodePoint = nextChar.value.codePointAt(0);
if (breakable[codePoint] || ideographicBreak || "imageName" in section || !nextNextChar.done && breakableBefore[nextCodePoint]) potentialLineBreaks.push(evaluateBreak(i + 1, currentX, targetWidth, potentialLineBreaks, calculatePenalty(codePoint, nextCodePoint, ideographicBreak && hasZeroWidthSpaces), false));
}
i++;
char = chars.next();
nextChar = nextChars.next();
nextNextChar = nextNextChars.next();
}
return leastBadBreaks(evaluateBreak(this.length(), currentX, targetWidth, potentialLineBreaks, 0, true));
}
determineAverageLineWidth(spacing, maxWidth, glyphMap, imagePositions, layoutTextSize) {
let totalWidth = 0;
let index = 0;
for (const char of this.text) {
const section = this.getSection(index);
totalWidth += getGlyphAdvance(char.codePointAt(0), section, glyphMap, imagePositions, spacing, layoutTextSize);
index++;
}
const lineCount = Math.max(1, Math.ceil(totalWidth / maxWidth));
return totalWidth / lineCount;
}
};
//#endregion
//#region node_modules/pbf/index.js
const SHIFT_LEFT_32 = 4294967296;
const SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
const TEXT_DECODER_MIN_LENGTH$1 = 12;
const utf8TextDecoder$1 = typeof TextDecoder === "undefined" ? null : new TextDecoder("utf-8");
const PBF_VARINT = 0;
const PBF_FIXED64 = 1;
const PBF_BYTES = 2;
const PBF_FIXED32 = 5;
var PbfReader = class {
/**
* @param {Uint8Array | ArrayBuffer} buf
*/
constructor(buf) {
this.buf = ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf);
this.dataView = new DataView(this.buf.buffer, this.buf.byteOffset, this.buf.byteLength);
this.pos = 0;
this.type = 0;
this._valueStart = -1;
this.length = this.buf.length;
}
/**
* @template T
* @param {(tag: number, result: T, pbf: PbfReader) => void} readField
* @param {T} result
* @param {number} [end]
*/
readFields(readField, result, end = this.length) {
let field;
while (field = this.nextField(end)) readField(field, result, this);
return result;
}
/**
* @template T
* @param {(tag: number, result: T, pbf: PbfReader) => void} readField
* @param {T} result
*/
readMessage(readField, result) {
return this.readFields(readField, result, this.readVarint() + this.pos);
}
readFixed32() {
const val = this.dataView.getUint32(this.pos, true);
this.pos += 4;
return val;
}
readSFixed32() {
const val = this.dataView.getInt32(this.pos, true);
this.pos += 4;
return val;
}
readFixed64() {
const val = this.dataView.getUint32(this.pos, true) + this.dataView.getUint32(this.pos + 4, true) * SHIFT_LEFT_32;
this.pos += 8;
return val;
}
readSFixed64() {
const val = this.dataView.getUint32(this.pos, true) + this.dataView.getInt32(this.pos + 4, true) * SHIFT_LEFT_32;
this.pos += 8;
return val;
}
readFloat() {
const val = this.dataView.getFloat32(this.pos, true);
this.pos += 4;
return val;
}
readDouble() {
const val = this.dataView.getFloat64(this.pos, true);
this.pos += 8;
return val;
}
/**
* @param {boolean} [isSigned]
*/
readVarint(isSigned) {
const buf = this.buf;
const b0 = buf[this.pos++];
if (b0 < 128) return b0;
let val = b0 & 127, b;
b = buf[this.pos++];
val |= (b & 127) << 7;
if (b < 128) return val;
b = buf[this.pos++];
val |= (b & 127) << 14;
if (b < 128) return val;
b = buf[this.pos++];
val |= (b & 127) << 21;
if (b < 128) return val;
b = buf[this.pos];
val |= (b & 15) << 28;
return readVarintRemainder(val, isSigned, this);
}
readSVarint() {
const num = this.readVarint();
return num % 2 === 1 ? (num + 1) / -2 : num / 2;
}
readBoolean() {
return Boolean(this.readVarint());
}
readString() {
const end = this.readVarint() + this.pos;
const pos = this.pos;
this.pos = end;
if (end - pos >= TEXT_DECODER_MIN_LENGTH$1 && utf8TextDecoder$1) return utf8TextDecoder$1.decode(this.buf.subarray(pos, end));
return readUtf8$1(this.buf, pos, end);
}
readBytes() {
const end = this.readVarint() + this.pos, buffer = this.buf.subarray(this.pos, end);
this.pos = end;
return buffer;
}
/**
* @param {number[]} [arr]
* @param {boolean} [isSigned]
*/
readPackedVarint(arr = [], isSigned) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readVarint(isSigned));
return arr;
}
/** @param {number[]} [arr] */
readPackedSVarint(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readSVarint());
return arr;
}
/** @param {boolean[]} [arr] */
readPackedBoolean(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readBoolean());
return arr;
}
/** @param {number[]} [arr] */
readPackedFloat(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readFloat());
return arr;
}
/** @param {number[]} [arr] */
readPackedDouble(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readDouble());
return arr;
}
/** @param {number[]} [arr] */
readPackedFixed32(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readFixed32());
return arr;
}
/** @param {number[]} [arr] */
readPackedSFixed32(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readSFixed32());
return arr;
}
/** @param {number[]} [arr] */
readPackedFixed64(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readFixed64());
return arr;
}
/** @param {number[]} [arr] */
readPackedSFixed64(arr = []) {
const end = this.readPackedEnd();
while (this.pos < end) arr.push(this.readSFixed64());
return arr;
}
readPackedEnd() {
return this.type === PBF_BYTES ? this.readVarint() + this.pos : this.pos + 1;
}
/**
* Advance to the next field. Returns the field number, or 0 at end-of-message.
* @param {number} [end]
*/
nextField(end = this.length) {
if (this.pos === this._valueStart) this.skip(this.type);
if (this.pos >= end) return 0;
const tag = this.readVarint();
this.type = tag & 7;
this._valueStart = this.pos;
return tag >>> 3;
}
/** @param {number} val */
skip(val) {
const type = val & 7;
if (type === PBF_VARINT) while (this.buf[this.pos++] > 127);
else if (type === PBF_BYTES) this.pos = this.readVarint() + this.pos;
else if (type === PBF_FIXED32) this.pos += 4;
else if (type === PBF_FIXED64) this.pos += 8;
else throw new Error(`Unimplemented type: ${type}`);
}
};
var PbfWriter = class {
/**
* @param {Uint8Array | ArrayBuffer} [buf]
*/
constructor(buf = /* @__PURE__ */ new Uint8Array(16)) {
this.buf = ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf);
this.dataView = new DataView(this.buf.buffer, this.buf.byteOffset, this.buf.byteLength);
this.pos = 0;
this.length = this.buf.length;
}
/**
* @param {number} tag
* @param {number} type
*/
writeTag(tag, type) {
this.writeVarint(tag << 3 | type);
}
/** @param {number} min */
realloc(min) {
let length = this.length || 16;
while (length < this.pos + min) length *= 2;
if (length !== this.length) {
const buf = new Uint8Array(length);
buf.set(this.buf);
this.buf = buf;
this.dataView = new DataView(buf.buffer);
this.length = length;
}
}
finish() {
this.length = this.pos;
this.pos = 0;
return this.buf.subarray(0, this.length);
}
/** @param {number} val */
writeFixed32(val) {
this.realloc(4);
this.dataView.setInt32(this.pos, val, true);
this.pos += 4;
}
/** @param {number} val */
writeSFixed32(val) {
this.realloc(4);
this.dataView.setInt32(this.pos, val, true);
this.pos += 4;
}
/** @param {number} val */
writeFixed64(val) {
this.realloc(8);
this.dataView.setInt32(this.pos, val & -1, true);
this.dataView.setInt32(this.pos + 4, Math.floor(val * SHIFT_RIGHT_32), true);
this.pos += 8;
}
/** @param {number} val */
writeSFixed64(val) {
this.realloc(8);
this.dataView.setInt32(this.pos, val & -1, true);
this.dataView.setInt32(this.pos + 4, Math.floor(val * SHIFT_RIGHT_32), true);
this.pos += 8;
}
/** @param {number} val */
writeVarint(val) {
val = +val || 0;
if (val >= 0 && val < 128) {
if (this.pos >= this.length) this.realloc(1);
this.buf[this.pos++] = val;
return;
}
if (val > 268435455 || val < 0) {
writeBigVarint(val, this);
return;
}
this.realloc(4);
this.buf[this.pos++] = val & 127 | (val > 127 ? 128 : 0);
if (val <= 127) return;
this.buf[this.pos++] = (val >>>= 7) & 127 | (val > 127 ? 128 : 0);
if (val <= 127) return;
this.buf[this.pos++] = (val >>>= 7) & 127 | (val > 127 ? 128 : 0);
if (val <= 127) return;
this.buf[this.pos++] = val >>> 7 & 127;
}
/** @param {number} val */
writeSVarint(val) {
this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
}
/** @param {boolean} val */
writeBoolean(val) {
this.writeVarint(+val);
}
/** @param {string} str */
writeString(str) {
str = String(str);
this.realloc(str.length * 4);
this.pos++;
const startPos = this.pos;
this.pos = writeUtf8(this.buf, str, this.pos);
const len = this.pos - startPos;
if (len >= 128) makeRoomForExtraLength(startPos, len, this);
this.pos = startPos - 1;
this.writeVarint(len);
this.pos += len;
}
/** @param {number} val */
writeFloat(val) {
this.realloc(4);
this.dataView.setFloat32(this.pos, val, true);
this.pos += 4;
}
/** @param {number} val */
writeDouble(val) {
this.realloc(8);
this.dataView.setFloat64(this.pos, val, true);
this.pos += 8;
}
/** @param {Uint8Array} buffer */
writeBytes(buffer) {
const len = buffer.length;
this.writeVarint(len);
this.realloc(len);
this.buf.set(buffer, this.pos);
this.pos += len;
}
/**
* @template T
* @param {(obj: T, pbf: PbfWriter) => void} fn
* @param {T} obj
*/
writeRawMessage(fn, obj) {
this.pos++;
const startPos = this.pos;
fn(obj, this);
const len = this.pos - startPos;
if (len >= 128) makeRoomForExtraLength(startPos, len, this);
this.pos = startPos - 1;
this.writeVarint(len);
this.pos += len;
}
/**
* @template T
* @param {number} tag
* @param {(obj: T, pbf: PbfWriter) => void} fn
* @param {T} obj
*/
writeMessage(tag, fn, obj) {
this.writeTag(tag, PBF_BYTES);
this.writeRawMessage(fn, obj);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedVarint(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedVarint, arr);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedSVarint(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedSVarint, arr);
}
/**
* @param {number} tag
* @param {boolean[]} arr
*/
writePackedBoolean(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedBoolean, arr);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedFloat(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedFloat, arr);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedDouble(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedDouble, arr);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedFixed32(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedFixed32, arr);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedSFixed32(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedSFixed32, arr);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedFixed64(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedFixed64, arr);
}
/**
* @param {number} tag
* @param {number[]} arr
*/
writePackedSFixed64(tag, arr) {
if (arr.length) this.writeMessage(tag, writePackedSFixed64, arr);
}
/**
* @param {number} tag
* @param {Uint8Array} buffer
*/
writeBytesField(tag, buffer) {
this.writeTag(tag, PBF_BYTES);
this.writeBytes(buffer);
}
/**
* @param {number} tag
* @param {number} val
*/
writeFixed32Field(tag, val) {
this.writeTag(tag, PBF_FIXED32);
this.writeFixed32(val);
}
/**
* @param {number} tag
* @param {number} val
*/
writeSFixed32Field(tag, val) {
this.writeTag(tag, PBF_FIXED32);
this.writeSFixed32(val);
}
/**
* @param {number} tag
* @param {number} val
*/
writeFixed64Field(tag, val) {
this.writeTag(tag, PBF_FIXED64);
this.writeFixed64(val);
}
/**
* @param {number} tag
* @param {number} val
*/
writeSFixed64Field(tag, val) {
this.writeTag(tag, PBF_FIXED64);
this.writeSFixed64(val);
}
/**
* @param {number} tag
* @param {number} val
*/
writeVarintField(tag, val) {
this.writeTag(tag, PBF_VARINT);
this.writeVarint(val);
}
/**
* @param {number} tag
* @param {number} val
*/
writeSVarintField(tag, val) {
this.writeTag(tag, PBF_VARINT);
this.writeSVarint(val);
}
/**
* @param {number} tag
* @param {string} str
*/
writeStringField(tag, str) {
this.writeTag(tag, PBF_BYTES);
this.writeString(str);
}
/**
* @param {number} tag
* @param {number} val
*/
writeFloatField(tag, val) {
this.writeTag(tag, PBF_FIXED32);
this.writeFloat(val);
}
/**
* @param {number} tag
* @param {number} val
*/
writeDoubleField(tag, val) {
this.writeTag(tag, PBF_FIXED64);
this.writeDouble(val);
}
/**
* @param {number} tag
* @param {boolean} val
*/
writeBooleanField(tag, val) {
this.writeVarintField(tag, +val);
}
};
/**
* @param {number} l
* @param {boolean | undefined} s
* @param {PbfReader} p
*/
function readVarintRemainder(l, s, p) {
const buf = p.buf;
let h, b;
b = buf[p.pos++];
h = (b & 112) >> 4;
if (b < 128) return toNum(l, h, s);
b = buf[p.pos++];
h |= (b & 127) << 3;
if (b < 128) return toNum(l, h, s);
b = buf[p.pos++];
h |= (b & 127) << 10;
if (b < 128) return toNum(l, h, s);
b = buf[p.pos++];
h |= (b & 127) << 17;
if (b < 128) return toNum(l, h, s);
b = buf[p.pos++];
h |= (b & 127) << 24;
if (b < 128) return toNum(l, h, s);
b = buf[p.pos++];
h |= (b & 1) << 31;
if (b < 128) return toNum(l, h, s);
throw new Error("Expected varint not more than 10 bytes");
}
/**
* @param {number} low
* @param {number} high
* @param {boolean} [isSigned]
*/
function toNum(low, high, isSigned) {
return isSigned ? high * 4294967296 + (low >>> 0) : (high >>> 0) * 4294967296 + (low >>> 0);
}
/**
* @param {number} val
* @param {PbfWriter} pbf
*/
function writeBigVarint(val, pbf) {
let low, high;
if (val >= 0) {
low = val % 4294967296 | 0;
high = val / 4294967296 | 0;
} else {
low = ~(-val % 4294967296);
high = ~(-val / 4294967296);
if (low ^ 4294967295) low = low + 1 | 0;
else {
low = 0;
high = high + 1 | 0;
}
}
if (val >= 0x10000000000000000 || val < -0x10000000000000000) throw new Error("Given varint doesn't fit into 10 bytes");
pbf.realloc(10);
writeBigVarintLow(low, high, pbf);
writeBigVarintHigh(high, pbf);
}
/**
* @param {number} high
* @param {number} low
* @param {PbfWriter} pbf
*/
function writeBigVarintLow(low, high, pbf) {
pbf.buf[pbf.pos++] = low & 127 | 128;
low >>>= 7;
pbf.buf[pbf.pos++] = low & 127 | 128;
low >>>= 7;
pbf.buf[pbf.pos++] = low & 127 | 128;
low >>>= 7;
pbf.buf[pbf.pos++] = low & 127 | 128;
low >>>= 7;
pbf.buf[pbf.pos] = low & 127;
}
/**
* @param {number} high
* @param {PbfWriter} pbf
*/
function writeBigVarintHigh(high, pbf) {
const lsb = (high & 7) << 4;
pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 128 : 0);
if (!high) return;
pbf.buf[pbf.pos++] = high & 127 | ((high >>>= 7) ? 128 : 0);
if (!high) return;
pbf.buf[pbf.pos++] = high & 127 | ((high >>>= 7) ? 128 : 0);
if (!high) return;
pbf.buf[pbf.pos++] = high & 127 | ((high >>>= 7) ? 128 : 0);
if (!high) return;
pbf.buf[pbf.pos++] = high & 127 | ((high >>>= 7) ? 128 : 0);
if (!high) return;
pbf.buf[pbf.pos++] = high & 127;
}
/**
* @param {number} startPos
* @param {number} len
* @param {PbfWriter} pbf
*/
function makeRoomForExtraLength(startPos, len, pbf) {
const extraLen = len <= 16383 ? 1 : len <= 2097151 ? 2 : len <= 268435455 ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));
pbf.realloc(extraLen);
pbf.buf.copyWithin(startPos + extraLen, startPos, pbf.pos);
}
/**
* Packed varints often dominate encode time, so write the bytes inline
* through a local buffer pointer rather than calling writeVarint per element,
* falling back to writeVarint only for negatives or near the buffer's end.
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedVarint(arr, pbf) {
const n = arr.length;
let buf = pbf.buf, pos = pbf.pos, limit = pbf.length;
for (let i = 0; i < n; i++) {
let val = arr[i];
if (val < 0 || pos + 10 > limit) {
pbf.pos = pos;
pbf.writeVarint(val);
buf = pbf.buf;
pos = pbf.pos;
limit = pbf.length;
continue;
}
while (val > 127) {
buf[pos++] = val % 128 | 128;
val = Math.floor(val / 128);
}
buf[pos++] = val;
}
pbf.pos = pos;
}
/**
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedSVarint(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]);
}
/**
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedFloat(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]);
}
/**
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedDouble(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]);
}
/**
* @param {boolean[]} arr
* @param {PbfWriter} pbf
*/
function writePackedBoolean(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]);
}
/**
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedFixed32(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]);
}
/**
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedSFixed32(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]);
}
/**
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedFixed64(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]);
}
/**
* @param {number[]} arr
* @param {PbfWriter} pbf
*/
function writePackedSFixed64(arr, pbf) {
for (let i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]);
}
/**
* @param {Uint8Array} buf
* @param {number} pos
* @param {number} end
*/
function readUtf8$1(buf, pos, end) {
let str = "";
let i = pos;
while (i < end) {
const b0 = buf[i];
let c = null;
let bytesPerSequence = b0 > 239 ? 4 : b0 > 223 ? 3 : b0 > 191 ? 2 : 1;
if (i + bytesPerSequence > end) break;
let b1, b2, b3;
if (bytesPerSequence === 1) {
if (b0 < 128) c = b0;
} else if (bytesPerSequence === 2) {
b1 = buf[i + 1];
if ((b1 & 192) === 128) {
c = (b0 & 31) << 6 | b1 & 63;
if (c <= 127) c = null;
}
} else if (bytesPerSequence === 3) {
b1 = buf[i + 1];
b2 = buf[i + 2];
if ((b1 & 192) === 128 && (b2 & 192) === 128) {
c = (b0 & 15) << 12 | (b1 & 63) << 6 | b2 & 63;
if (c <= 2047 || c >= 55296 && c <= 57343) c = null;
}
} else if (bytesPerSequence === 4) {
b1 = buf[i + 1];
b2 = buf[i + 2];
b3 = buf[i + 3];
if ((b1 & 192) === 128 && (b2 & 192) === 128 && (b3 & 192) === 128) {
c = (b0 & 15) << 18 | (b1 & 63) << 12 | (b2 & 63) << 6 | b3 & 63;
if (c <= 65535 || c >= 1114112) c = null;
}
}
if (c === null) {
c = 65533;
bytesPerSequence = 1;
} else if (c > 65535) {
c -= 65536;
str += String.fromCharCode(c >>> 10 & 1023 | 55296);
c = 56320 | c & 1023;
}
str += String.fromCharCode(c);
i += bytesPerSequence;
}
return str;
}
/**
* @param {Uint8Array} buf
* @param {string} str
* @param {number} pos
*/
function writeUtf8(buf, str, pos) {
for (let i = 0, c, lead; i < str.length; i++) {
c = str.charCodeAt(i);
if (c > 55295 && c < 57344) {
if (lead) {
if (c < 56320) {
buf[pos++] = 239;
buf[pos++] = 191;
buf[pos++] = 189;
lead = c;
continue;
} else {
c = lead - 55296 << 10 | c - 56320 | 65536;
lead = null;
}
} else {
if (c > 56319 || i + 1 === str.length) {
buf[pos++] = 239;
buf[pos++] = 191;
buf[pos++] = 189;
} else lead = c;
continue;
}
} else if (lead) {
buf[pos++] = 239;
buf[pos++] = 191;
buf[pos++] = 189;
lead = null;
}
if (c < 128) buf[pos++] = c;
else {
if (c < 2048) buf[pos++] = c >> 6 | 192;
else {
if (c < 65536) buf[pos++] = c >> 12 | 224;
else {
buf[pos++] = c >> 18 | 240;
buf[pos++] = c >> 12 & 63 | 128;
}
buf[pos++] = c >> 6 & 63 | 128;
}
buf[pos++] = c & 63 | 128;
}
}
return pos;
}
function readFontstacks(tag, glyphs, pbf) {
if (tag === 1) pbf.readMessage(readFontstack, glyphs);
}
function readFontstack(tag, glyphs, pbf) {
if (tag === 3) {
const { id, bitmap, width, height, left, top, advance } = pbf.readMessage(readGlyph, {});
glyphs.push({
id,
bitmap: new AlphaImage({
width: width + 6,
height: height + 6
}, bitmap),
metrics: {
width,
height,
left,
top,
advance
}
});
}
}
function readGlyph(tag, glyph, pbf) {
if (tag === 1) glyph.id = pbf.readVarint();
else if (tag === 2) glyph.bitmap = pbf.readBytes();
else if (tag === 3) glyph.width = pbf.readVarint();
else if (tag === 4) glyph.height = pbf.readVarint();
else if (tag === 5) glyph.left = pbf.readSVarint();
else if (tag === 6) glyph.top = pbf.readSVarint();
else if (tag === 7) glyph.advance = pbf.readVarint();
}
function parseGlyphPbf(data) {
return new PbfReader(data).readFields(readFontstacks, []);
}
function isStyleImageWebGLData(data) {
return typeof data?.renderWithWebGL === "function";
}
function renderStyleImage(image) {
const { userImage } = image;
if (!userImage?.render) return false;
if (!userImage.render()) return false;
if (!isStyleImageWebGLData(userImage.data)) image.data.replace(new Uint8Array(userImage.data.buffer));
return true;
}
//#endregion
//#region node_modules/potpack/index.js
/**
* @typedef {Object} PotpackBox
* @property {number} w Box width.
* @property {number} h Box height.
* @property {number} [x] X coordinate in the resulting container.
* @property {number} [y] Y coordinate in the resulting container.
*/
/**
* @typedef {Object} PotpackStats
* @property {number} w Width of the resulting container.
* @property {number} h Height of the resulting container.
* @property {number} fill The space utilization value (0 to 1). Higher is better.
*/
/**
* Packs 2D rectangles into a near-square container.
*
* Mutates the {@link boxes} array: it's sorted (by height/width),
* and box objects are augmented with `x`, `y` coordinates.
*
* @param {PotpackBox[]} boxes
* @return {PotpackStats}
*/
function potpack(boxes) {
let area = 0;
let maxWidth = 0;
for (const box of boxes) {
area += box.w * box.h;
maxWidth = Math.max(maxWidth, box.w);
}
boxes.sort((a, b) => b.h - a.h);
const spaces = [{
x: 0,
y: 0,
w: Math.max(Math.ceil(Math.sqrt(area / .95)), maxWidth),
h: Infinity
}];
let width = 0;
let height = 0;
for (const box of boxes) for (let i = spaces.length - 1; i >= 0; i--) {
const space = spaces[i];
if (box.w > space.w || box.h > space.h) continue;
box.x = space.x;
box.y = space.y;
height = Math.max(height, box.y + box.h);
width = Math.max(width, box.x + box.w);
if (box.w === space.w && box.h === space.h) {
const last = spaces.pop();
if (last && i < spaces.length) spaces[i] = last;
} else if (box.h === space.h) {
space.x += box.w;
space.w -= box.w;
} else if (box.w === space.w) {
space.y += box.h;
space.h -= box.h;
} else {
spaces.push({
x: space.x + box.w,
y: space.y,
w: space.w - box.w,
h: box.h
});
space.y += box.h;
space.h -= box.h;
}
break;
}
return {
w: width,
h: height,
fill: area / (width * height) || 0
};
}
var ImagePosition = class {
constructor(paddedRect, { pixelRatio, version, isWebGLImage = false, stretchX, stretchY, content, textFitWidth, textFitHeight }) {
this.paddedRect = paddedRect;
this.pixelRatio = pixelRatio;
this.stretchX = stretchX;
this.stretchY = stretchY;
this.content = content;
this.version = version;
this.needsFirstWebGLRender = isWebGLImage;
this.textFitWidth = textFitWidth;
this.textFitHeight = textFitHeight;
}
get tl() {
return [this.paddedRect.x + 1, this.paddedRect.y + 1];
}
get br() {
return [this.paddedRect.x + this.paddedRect.w - 1, this.paddedRect.y + this.paddedRect.h - 1];
}
get tlbr() {
return this.tl.concat(this.br);
}
get displaySize() {
return [(this.paddedRect.w - 2) / this.pixelRatio, (this.paddedRect.h - 2) / this.pixelRatio];
}
};
/**
* A single tile's packed copy of the icons and patterns its features reference, built in the
* worker so that the symbol layout can bake the positions within it straight into the vertex
* buffers. Each tile owns one, along with the texture it is uploaded to - as opposed to
* {@link ImageManager}, which owns the images of the whole style.
* @internal
*/
var ImageAtlas = class {
constructor(icons, patterns) {
const iconPositions = {}, patternPositions = {};
this.haveRenderCallbacks = [];
this.patchedUpdateVersion = -1;
const bins = [];
this.addImages(icons, iconPositions, bins);
this.addImages(patterns, patternPositions, bins);
const { w, h } = potpack(bins);
const image = new RGBAImage({
width: w || 1,
height: h || 1
});
for (const id in icons) {
const src = icons[id];
if (src.isWebGLImage) continue;
const bin = iconPositions[id].paddedRect;
RGBAImage.copy(src.data, image, {
x: 0,
y: 0
}, {
x: bin.x + 1,
y: bin.y + 1
}, src.data);
}
for (const id in patterns) {
const src = patterns[id];
const bin = patternPositions[id].paddedRect;
const x = bin.x + 1, y = bin.y + 1, w = src.data.width, h = src.data.height;
RGBAImage.copy(src.data, image, {
x: 0,
y: 0
}, {
x,
y
}, src.data);
RGBAImage.copy(src.data, image, {
x: 0,
y: h - 1
}, {
x,
y: y - 1
}, {
width: w,
height: 1
});
RGBAImage.copy(src.data, image, {
x: 0,
y: 0
}, {
x,
y: y + h
}, {
width: w,
height: 1
});
RGBAImage.copy(src.data, image, {
x: w - 1,
y: 0
}, {
x: x - 1,
y
}, {
width: 1,
height: h
});
RGBAImage.copy(src.data, image, {
x: 0,
y: 0
}, {
x: x + w,
y
}, {
width: 1,
height: h
});
}
this.image = image;
this.iconPositions = iconPositions;
this.patternPositions = patternPositions;
}
addImages(images, positions, bins) {
for (const id in images) {
const src = images[id];
const bin = {
x: 0,
y: 0,
w: src.data.width + 2,
h: src.data.height + 2
};
bins.push(bin);
positions[id] = new ImagePosition(bin, src);
if (src.hasRenderCallback) this.haveRenderCallbacks.push(id);
}
}
/**
* Brings this atlas' texture back in sync with the images it was built from, re-uploading the
* ones that were replaced in the meantime.
*
* This runs for every in-view tile on every frame, so the common case of nothing having
* changed has to cost nothing: a single comparison against {@link ImageManager.updateVersion}
* ends the call. When something did change, only the images this atlas actually holds are
* looked at - a handful per tile - and {@link ImageAtlas.patchUpdatedImage} then skips the
* ones whose version still matches, leaving just the genuinely stale ones to upload.
*
* The render callbacks are dispatched first because they may update images themselves, and
* those updates have to be part of the version this call catches up with - otherwise an
* animated image would always be uploaded a frame late.
*/
patchUpdatedImages(imageManager, texture) {
imageManager.dispatchRenderCallbacks(this.haveRenderCallbacks);
if (this.patchedUpdateVersion === imageManager.updateVersion) return;
this.patchedUpdateVersion = imageManager.updateVersion;
for (const name in this.iconPositions) this.patchUpdatedImage(this.iconPositions[name], imageManager.getImage(name), texture);
for (const name in this.patternPositions) this.patchUpdatedImage(this.patternPositions[name], imageManager.getImage(name), texture);
}
patchUpdatedImage(position, image, texture) {
if (!position || !image) return;
if (!position.needsFirstWebGLRender && position.version === image.version) return;
position.needsFirstWebGLRender = false;
position.version = image.version;
const [x, y] = position.tl;
const data = image.userImage?.data;
if (!isStyleImageWebGLData(data)) {
texture.update(image.data, void 0, {
x,
y
});
return;
}
const { width, height } = image.data;
texture.context.setCustomLayerDefaults();
data.renderWithWebGL({
gl: texture.context.gl,
texture: texture.texture,
x,
y,
width,
height
});
texture.context.setDirty();
}
};
register("ImagePosition", ImagePosition);
register("ImageAtlas", ImageAtlas);
//#endregion
//#region src/symbol/shaping.ts
var WritingMode = /* @__PURE__ */ function(WritingMode) {
WritingMode[WritingMode["none"] = 0] = "none";
WritingMode[WritingMode["horizontal"] = 1] = "horizontal";
WritingMode[WritingMode["vertical"] = 2] = "vertical";
WritingMode[WritingMode["horizontalOnly"] = 3] = "horizontalOnly";
return WritingMode;
}(WritingMode || {});
function isEmpty(positionedLines) {
for (const line of positionedLines) if (line.positionedGlyphs.length !== 0) return false;
return true;
}
function breakLines(input, lineBreakPoints) {
const lines = [];
let start = 0;
for (const lineBreak of lineBreakPoints) {
lines.push(input.substring(start, lineBreak));
start = lineBreak;
}
if (start < input.length()) lines.push(input.substring(start, input.length()));
return lines;
}
function shapeText(text, glyphMap, glyphPositions, imagePositions, defaultFontStack, maxWidth, lineHeight, textAnchor, textJustify, spacing, translate, writingMode, allowVerticalPlacement, layoutTextSize, layoutTextSizeThisZoom) {
const logicalInput = TaggedString.fromFeature(text, defaultFontStack);
if (writingMode === 2) logicalInput.verticalizePunctuation();
let lines;
let lineBreaks = logicalInput.determineLineBreaks(spacing, maxWidth, glyphMap, imagePositions, layoutTextSize);
const { processBidirectionalText, processStyledBidirectionalText } = rtlWorkerPlugin;
if (processBidirectionalText && logicalInput.sections.length === 1) {
lines = [];
lineBreaks = lineBreaks.map((index) => logicalInput.toCodeUnitIndex(index));
const untaggedLines = processBidirectionalText(logicalInput.toString(), lineBreaks);
for (const line of untaggedLines) {
const sectionIndex = [...line].map(() => 0);
lines.push(new TaggedString(line, logicalInput.sections, sectionIndex));
}
} else if (processStyledBidirectionalText) {
lines = [];
lineBreaks = lineBreaks.map((index) => logicalInput.toCodeUnitIndex(index));
let i = 0;
const sectionIndex = [];
for (const char of logicalInput.text) {
sectionIndex.push(...Array(char.length).fill(logicalInput.sectionIndex[i]));
i++;
}
const processedLines = processStyledBidirectionalText(logicalInput.text, sectionIndex, lineBreaks);
for (const line of processedLines) {
const sectionIndex = [];
let elapsedChars = "";
for (const char of line[0]) {
sectionIndex.push(line[1][elapsedChars.length]);
elapsedChars += char;
}
lines.push(new TaggedString(line[0], logicalInput.sections, sectionIndex));
}
} else lines = breakLines(logicalInput, lineBreaks);
const positionedLines = [];
const shaping = {
positionedLines,
text: logicalInput.toString(),
top: translate[1],
bottom: translate[1],
left: translate[0],
right: translate[0],
writingMode,
iconsInText: false,
verticalizable: false
};
shapeLines(shaping, glyphMap, glyphPositions, imagePositions, lines, lineHeight, textAnchor, textJustify, writingMode, spacing, allowVerticalPlacement, layoutTextSizeThisZoom);
if (isEmpty(positionedLines)) return false;
return shaping;
}
function getAnchorAlignment(anchor) {
let horizontalAlign = .5, verticalAlign = .5;
switch (anchor) {
case "right":
case "top-right":
case "bottom-right":
horizontalAlign = 1;
break;
case "left":
case "top-left":
case "bottom-left": horizontalAlign = 0;
}
switch (anchor) {
case "bottom":
case "bottom-right":
case "bottom-left":
verticalAlign = 1;
break;
case "top":
case "top-right":
case "top-left": verticalAlign = 0;
}
return {
horizontalAlign,
verticalAlign
};
}
function calculateLineContentSize(imagePositions, line, layoutTextSizeFactor) {
const maxGlyphSize = line.getMaxScale() * 24;
const { maxImageWidth, maxImageHeight } = line.getMaxImageSize(imagePositions);
const horizontalLineContentHeight = Math.max(maxGlyphSize, maxImageHeight * layoutTextSizeFactor);
return {
verticalLineContentWidth: Math.max(maxGlyphSize, maxImageWidth * layoutTextSizeFactor),
horizontalLineContentHeight
};
}
function getVerticalAlignFactor(verticalAlign) {
switch (verticalAlign) {
case "top": return 0;
case "center": return .5;
default: return 1;
}
}
function getRectAndMetrics(glyphPosition, glyphMap, section, codePoint) {
if (glyphPosition?.rect) return glyphPosition;
const glyph = glyphMap[section.fontStack]?.[codePoint];
if (!glyph) return null;
return {
rect: null,
metrics: glyph.metrics
};
}
function isLineVertical(writingMode, allowVerticalPlacement, codePoint) {
return !(writingMode === 1 || !allowVerticalPlacement && !codePointHasUprightVerticalOrientation(codePoint) || allowVerticalPlacement && (charIsWhitespace(codePoint) || charInComplexShapingScript(codePoint)));
}
function shapeLines(shaping, glyphMap, glyphPositions, imagePositions, lines, lineHeight, textAnchor, textJustify, writingMode, spacing, allowVerticalPlacement, layoutTextSizeThisZoom) {
let x = 0;
let y = 0;
let maxLineLength = 0;
let maxLineHeight = 0;
const justify = textJustify === "right" ? 1 : textJustify === "left" ? 0 : .5;
const layoutTextSizeFactor = 24 / layoutTextSizeThisZoom;
let lineIndex = 0;
for (const line of lines) {
line.trim();
const lineMaxScale = line.getMaxScale();
const positionedLine = {
positionedGlyphs: [],
lineOffset: 0
};
shaping.positionedLines[lineIndex] = positionedLine;
const positionedGlyphs = positionedLine.positionedGlyphs;
let imageOffset = 0;
if (!line.length()) {
y += lineHeight;
++lineIndex;
continue;
}
const lineShapingSize = calculateLineContentSize(imagePositions, line, layoutTextSizeFactor);
let i = 0;
for (const char of line.text) {
const section = line.getSection(i);
const codePoint = char.codePointAt(0);
const vertical = isLineVertical(writingMode, allowVerticalPlacement, codePoint);
const positionedGlyph = {
glyph: codePoint,
imageName: null,
x,
y: y + -17,
vertical,
scale: 1,
fontStack: "",
sectionIndex: line.getSectionIndex(i),
metrics: null,
rect: null
};
let sectionAttributes;
if ("fontStack" in section) {
sectionAttributes = shapeTextSection(section, codePoint, vertical, lineShapingSize, glyphMap, glyphPositions);
if (!sectionAttributes) continue;
positionedGlyph.fontStack = section.fontStack;
} else {
shaping.iconsInText = true;
section.scale *= layoutTextSizeFactor;
sectionAttributes = shapeImageSection(section, vertical, lineMaxScale, lineShapingSize, imagePositions);
if (!sectionAttributes) continue;
imageOffset = Math.max(imageOffset, sectionAttributes.imageOffset);
positionedGlyph.imageName = section.imageName;
}
const { rect, metrics, baselineOffset } = sectionAttributes;
positionedGlyph.y += baselineOffset;
positionedGlyph.scale = section.scale;
positionedGlyph.metrics = metrics;
positionedGlyph.rect = rect;
positionedGlyphs.push(positionedGlyph);
if (!vertical) x += metrics.advance * section.scale + spacing;
else {
shaping.verticalizable = true;
const verticalAdvance = "imageName" in section ? metrics.advance : 24;
x += verticalAdvance * section.scale + spacing;
}
i++;
}
if (positionedGlyphs.length !== 0) {
const lineLength = x - spacing;
maxLineLength = Math.max(lineLength, maxLineLength);
justifyLine(positionedGlyphs, 0, positionedGlyphs.length - 1, justify);
}
x = 0;
const maxLineOffset = (lineMaxScale - 1) * 24;
positionedLine.lineOffset = Math.max(imageOffset, maxLineOffset);
const currentLineHeight = lineHeight * lineMaxScale + imageOffset;
y += currentLineHeight;
maxLineHeight = Math.max(currentLineHeight, maxLineHeight);
++lineIndex;
}
const { horizontalAlign, verticalAlign } = getAnchorAlignment(textAnchor);
align(shaping.positionedLines, justify, horizontalAlign, verticalAlign, maxLineLength, maxLineHeight, lineHeight, y, lines.length);
shaping.top += -verticalAlign * y;
shaping.bottom = shaping.top + y;
shaping.left += -horizontalAlign * maxLineLength;
shaping.right = shaping.left + maxLineLength;
}
function shapeTextSection(section, codePoint, vertical, lineShapingSize, glyphMap, glyphPositions) {
const glyphPosition = glyphPositions[section.fontStack]?.[codePoint];
const rectAndMetrics = getRectAndMetrics(glyphPosition, glyphMap, section, codePoint);
if (rectAndMetrics === null) return null;
let baselineOffset;
if (vertical) baselineOffset = lineShapingSize.verticalLineContentWidth - section.scale * 24;
else {
const verticalAlignFactor = getVerticalAlignFactor(section.verticalAlign);
baselineOffset = (lineShapingSize.horizontalLineContentHeight - section.scale * 24) * verticalAlignFactor;
}
return {
rect: rectAndMetrics.rect,
metrics: rectAndMetrics.metrics,
baselineOffset
};
}
function shapeImageSection(section, vertical, lineMaxScale, lineShapingSize, imagePositions) {
const imagePosition = imagePositions[section.imageName];
if (!imagePosition) return null;
const rect = imagePosition.paddedRect;
const size = imagePosition.displaySize;
const metrics = {
width: size[0],
height: size[1],
left: 1,
top: -3,
advance: vertical ? size[1] : size[0]
};
let baselineOffset;
if (vertical) baselineOffset = lineShapingSize.verticalLineContentWidth - size[1] * section.scale;
else {
const verticalAlignFactor = getVerticalAlignFactor(section.verticalAlign);
baselineOffset = (lineShapingSize.horizontalLineContentHeight - size[1] * section.scale) * verticalAlignFactor;
}
const imageOffset = (vertical ? size[0] : size[1]) * section.scale - 24 * lineMaxScale;
return {
rect,
metrics,
baselineOffset,
imageOffset
};
}
function justifyLine(positionedGlyphs, start, end, justify) {
if (justify === 0) return;
const lastPositionedGlyph = positionedGlyphs[end];
const lastAdvance = lastPositionedGlyph.metrics.advance * lastPositionedGlyph.scale;
const lineIndent = (positionedGlyphs[end].x + lastAdvance) * justify;
for (let j = start; j <= end; j++) positionedGlyphs[j].x -= lineIndent;
}
/**
* Aligns the lines based on horizontal and vertical alignment.
*/
function align(positionedLines, justify, horizontalAlign, verticalAlign, maxLineLength, maxLineHeight, lineHeight, blockHeight, lineCount) {
const shiftX = (justify - horizontalAlign) * maxLineLength;
let shiftY = 0;
if (maxLineHeight !== lineHeight) shiftY = -blockHeight * verticalAlign - -17;
else shiftY = -verticalAlign * lineCount * lineHeight + .5 * lineHeight;
for (const line of positionedLines) for (const positionedGlyph of line.positionedGlyphs) {
positionedGlyph.x += shiftX;
positionedGlyph.y += shiftY;
}
}
function shapeIcon(image, iconOffset, iconAnchor) {
const { horizontalAlign, verticalAlign } = getAnchorAlignment(iconAnchor);
const dx = iconOffset[0];
const dy = iconOffset[1];
const x1 = dx - image.displaySize[0] * horizontalAlign;
const x2 = x1 + image.displaySize[0];
const y1 = dy - image.displaySize[1] * verticalAlign;
return {
image,
top: y1,
bottom: y1 + image.displaySize[1],
left: x1,
right: x2
};
}
/**
* Called after a PositionedIcon has already been run through fitIconToText,
* but needs further adjustment to apply textFitWidth and textFitHeight.
* @param shapedIcon - The icon that will be adjusted.
* @returns Extents of the shapedIcon with text fit adjustments if necessary.
*/
function applyTextFit(shapedIcon) {
let iconLeft = shapedIcon.left;
let iconTop = shapedIcon.top;
let iconWidth = shapedIcon.right - iconLeft;
let iconHeight = shapedIcon.bottom - iconTop;
const contentWidth = shapedIcon.image.content[2] - shapedIcon.image.content[0];
const contentHeight = shapedIcon.image.content[3] - shapedIcon.image.content[1];
const textFitWidth = shapedIcon.image.textFitWidth ?? "stretchOrShrink";
const textFitHeight = shapedIcon.image.textFitHeight ?? "stretchOrShrink";
const contentAspectRatio = contentWidth / contentHeight;
if (textFitHeight === "proportional") {
if (textFitWidth === "stretchOnly" && iconWidth / iconHeight < contentAspectRatio || textFitWidth === "proportional") {
const newIconWidth = Math.ceil(iconHeight * contentAspectRatio);
iconLeft *= newIconWidth / iconWidth;
iconWidth = newIconWidth;
}
} else if (textFitWidth === "proportional") {
if (textFitHeight === "stretchOnly" && contentAspectRatio !== 0 && iconWidth / iconHeight > contentAspectRatio) {
const newIconHeight = Math.ceil(iconWidth / contentAspectRatio);
iconTop *= newIconHeight / iconHeight;
iconHeight = newIconHeight;
}
}
return {
x1: iconLeft,
y1: iconTop,
x2: iconLeft + iconWidth,
y2: iconTop + iconHeight
};
}
function fitIconToText(shapedIcon, shapedText, textFit, padding, iconOffset, fontScale) {
const image = shapedIcon.image;
let collisionPadding;
if (image.content) {
const content = image.content;
const pixelRatio = image.pixelRatio || 1;
collisionPadding = [
content[0] / pixelRatio,
content[1] / pixelRatio,
image.displaySize[0] - content[2] / pixelRatio,
image.displaySize[1] - content[3] / pixelRatio
];
}
const textLeft = shapedText.left * fontScale;
const textRight = shapedText.right * fontScale;
let top, right, bottom, left;
if (textFit === "width" || textFit === "both") {
left = iconOffset[0] + textLeft - padding[3];
right = iconOffset[0] + textRight + padding[1];
} else {
left = iconOffset[0] + (textLeft + textRight - image.displaySize[0]) / 2;
right = left + image.displaySize[0];
}
const textTop = shapedText.top * fontScale;
const textBottom = shapedText.bottom * fontScale;
if (textFit === "height" || textFit === "both") {
top = iconOffset[1] + textTop - padding[0];
bottom = iconOffset[1] + textBottom + padding[2];
} else {
top = iconOffset[1] + (textTop + textBottom - image.displaySize[1]) / 2;
bottom = top + image.displaySize[1];
}
return {
image,
top,
right,
bottom,
left,
collisionPadding
};
}
const MAX_PACKED_SIZE = 32640;
function getSizeData(tileZoom, value) {
const { expression } = value;
if (expression.kind === "constant") return {
kind: "constant",
layoutSize: expression.evaluate(new EvaluationParameters(tileZoom + 1))
};
else if (expression.kind === "source") return { kind: "source" };
else {
const { zoomStops, interpolationType } = expression;
let lower = 0;
while (lower < zoomStops.length && zoomStops[lower] <= tileZoom) lower++;
lower = Math.max(0, lower - 1);
let upper = lower;
while (upper < zoomStops.length && zoomStops[upper] < tileZoom + 1) upper++;
upper = Math.min(zoomStops.length - 1, upper);
const minZoom = zoomStops[lower];
const maxZoom = zoomStops[upper];
if (expression.kind === "composite") return {
kind: "composite",
minZoom,
maxZoom,
interpolationType
};
return {
kind: "camera",
minZoom,
maxZoom,
minSize: expression.evaluate(new EvaluationParameters(minZoom)),
maxSize: expression.evaluate(new EvaluationParameters(maxZoom)),
interpolationType
};
}
}
function evaluateSizeForFeature(sizeData, { uSize, uSizeT }, { lowerSize, upperSize }) {
if (sizeData.kind === "source") return lowerSize / 128;
else if (sizeData.kind === "composite") return interpolateFactory.number(lowerSize / 128, upperSize / 128, uSizeT);
return uSize;
}
function evaluateSizeForZoom(sizeData, zoom) {
let uSizeT = 0;
let uSize = 0;
if (sizeData.kind === "constant") uSize = sizeData.layoutSize;
else if (sizeData.kind !== "source") {
const { interpolationType, minZoom, maxZoom } = sizeData;
const t = !interpolationType ? 0 : clamp$2(Interpolate.interpolationFactor(interpolationType, zoom, minZoom, maxZoom), 0, 1);
if (sizeData.kind === "camera") uSize = interpolateFactory.number(sizeData.minSize, sizeData.maxSize, t);
else uSizeT = t;
}
return {
uSizeT,
uSize
};
}
//#endregion
//#region src/style/style_layer/overlap_mode.ts
function getOverlapMode(layout, overlapProp, allowOverlapProp) {
let result = "never";
const overlap = layout.get(overlapProp);
if (overlap) result = overlap;
else if (layout.get(allowOverlapProp)) result = "always";
return result;
}
//#endregion
//#region src/data/bucket/symbol_bucket.ts
const shaderOpacityAttributes = [{
name: "a_fade_opacity",
components: 1,
type: "Uint8",
offset: 0
}];
function addVertex(array, anchorX, anchorY, ox, oy, tx, ty, sizeVertex, isSDF, pixelOffsetX, pixelOffsetY, minFontScaleX, minFontScaleY) {
const aSizeX = sizeVertex ? Math.min(MAX_PACKED_SIZE, Math.round(sizeVertex[0])) : 0;
const aSizeY = sizeVertex ? Math.min(MAX_PACKED_SIZE, Math.round(sizeVertex[1])) : 0;
array.emplaceBack(anchorX, anchorY, Math.round(ox * 32), Math.round(oy * 32), tx, ty, (aSizeX << 1) + (isSDF ? 1 : 0), aSizeY, pixelOffsetX * 16, pixelOffsetY * 16, minFontScaleX * 256, minFontScaleY * 256);
}
function addDynamicAttributes(dynamicLayoutVertexArray, p, angle) {
dynamicLayoutVertexArray.emplaceBack(p.x, p.y, angle);
dynamicLayoutVertexArray.emplaceBack(p.x, p.y, angle);
dynamicLayoutVertexArray.emplaceBack(p.x, p.y, angle);
dynamicLayoutVertexArray.emplaceBack(p.x, p.y, angle);
}
function containsRTLText(formattedText) {
for (const section of formattedText.sections) if (stringContainsRTLText(section.text)) return true;
return false;
}
var SymbolBuffers = class {
constructor(programConfigurations) {
this.layoutVertexArray = new SymbolLayoutArray();
this.indexArray = new TriangleIndexArray();
this.programConfigurations = programConfigurations;
this.segments = new SegmentVector();
this.dynamicLayoutVertexArray = new SymbolDynamicLayoutArray();
this.opacityVertexArray = new SymbolOpacityArray();
this.hasVisibleVertices = false;
this.placedSymbolArray = new PlacedSymbolArray();
}
isEmpty() {
return this.layoutVertexArray.length === 0 && this.indexArray.length === 0 && this.dynamicLayoutVertexArray.length === 0 && this.opacityVertexArray.length === 0;
}
upload(context, dynamicIndexBuffer, upload, update) {
if (this.isEmpty()) return;
if (upload) {
this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, symbolLayoutAttributes.members);
this.indexBuffer = context.createIndexBuffer(this.indexArray, dynamicIndexBuffer);
this.dynamicLayoutVertexBuffer = context.createVertexBuffer(this.dynamicLayoutVertexArray, dynamicLayoutAttributes.members, true);
this.opacityVertexBuffer = context.createVertexBuffer(this.opacityVertexArray, shaderOpacityAttributes, true);
this.opacityVertexBuffer.itemSize = 1;
}
if (upload || update) this.programConfigurations.upload(context);
}
destroy() {
if (!this.layoutVertexBuffer) return;
this.layoutVertexBuffer.destroy();
this.indexBuffer.destroy();
this.programConfigurations.destroy();
this.segments.destroy();
this.dynamicLayoutVertexBuffer.destroy();
this.opacityVertexBuffer.destroy();
}
};
register("SymbolBuffers", SymbolBuffers);
var CollisionBuffers = class {
constructor(LayoutArray, layoutAttributes, IndexArray) {
this.layoutVertexArray = new LayoutArray();
this.layoutAttributes = layoutAttributes;
this.indexArray = new IndexArray();
this.segments = new SegmentVector();
this.collisionVertexArray = new CollisionVertexArray();
}
upload(context) {
this.layoutVertexBuffer = context.createVertexBuffer(this.layoutVertexArray, this.layoutAttributes);
this.indexBuffer = context.createIndexBuffer(this.indexArray);
this.collisionVertexBuffer = context.createVertexBuffer(this.collisionVertexArray, collisionVertexAttributes.members, true);
}
destroy() {
if (!this.layoutVertexBuffer) return;
this.layoutVertexBuffer.destroy();
this.indexBuffer.destroy();
this.segments.destroy();
this.collisionVertexBuffer.destroy();
}
};
register("CollisionBuffers", CollisionBuffers);
/**
* @internal
* Unlike other buckets, which simply implement `addFeature` with type-specific
* logic for (essentially) triangulating feature geometries, SymbolBucket
* requires specialized behavior:
*
* 1. WorkerTile.parse(), the logical owner of the bucket creation process,
* calls SymbolBucket.populate(), which resolves text and icon tokens on
* each feature, adds each glyphs and symbols needed to the passed-in
* collections options.glyphDependencies and options.iconDependencies, and
* stores the feature data for use in subsequent step (this.features).
*
* 2. WorkerTile asynchronously requests from the main thread all of the glyphs
* and icons needed (by this bucket and any others). When glyphs and icons
* have been received, the WorkerTile creates a CollisionIndex and invokes:
*
* 3. performSymbolLayout(bucket, stacks, icons) perform texts shaping and
* layout on a Symbol Bucket. This step populates:
* `this.symbolInstances`: metadata on generated symbols
* `this.collisionBoxArray`: collision data for use by foreground
* `this.text`: SymbolBuffers for text symbols
* `this.icons`: SymbolBuffers for icons
* `this.iconCollisionBox`: Debug SymbolBuffers for icon collision boxes
* `this.textCollisionBox`: Debug SymbolBuffers for text collision boxes
* The results are sent to the foreground for rendering
*
* 4. placement.ts is run on the foreground,
* and uses the CollisionIndex along with current camera settings to determine
* which symbols can actually show on the map. Collided symbols are hidden
* using a dynamic "OpacityVertexArray".
*/
var SymbolBucket = class {
constructor(options) {
this.collisionBoxArray = options.collisionBoxArray;
this.zoom = options.zoom;
this.overscaling = options.overscaling;
this.layers = options.layers;
this.layerIds = this.layers.map((layer) => layer.id);
this.index = options.index;
this.pixelRatio = options.pixelRatio;
this.sourceLayerIndex = options.sourceLayerIndex;
this.hasDependencies = false;
this.hasRTLText = false;
this.sortKeyRanges = [];
this.collisionCircleArray = [];
const unevaluatedLayoutValues = this.layers[0]._unevaluatedLayout._values;
this.textSizeData = getSizeData(this.zoom, unevaluatedLayoutValues["text-size"]);
this.iconSizeData = getSizeData(this.zoom, unevaluatedLayoutValues["icon-size"]);
const layout = this.layers[0].layout;
const sortKey = layout.get("symbol-sort-key");
const zOrder = layout.get("symbol-z-order");
this.canOverlap = getOverlapMode(layout, "text-overlap", "text-allow-overlap") !== "never" || getOverlapMode(layout, "icon-overlap", "icon-allow-overlap") !== "never" || layout.get("text-ignore-placement") || layout.get("icon-ignore-placement");
this.sortFeaturesByKey = zOrder !== "viewport-y" && !sortKey.isConstant();
const zOrderByViewportY = zOrder === "viewport-y" || zOrder === "auto" && !this.sortFeaturesByKey;
this.sortFeaturesByY = zOrderByViewportY && this.canOverlap;
if (layout.get("symbol-placement") === "point") this.writingModes = layout.get("text-writing-mode").map((wm) => WritingMode[wm]);
this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id);
this.sourceID = options.sourceID;
}
createArrays() {
this.text = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, (property) => property.startsWith("text")));
this.icon = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, (property) => property.startsWith("icon")));
this.glyphOffsetArray = new GlyphOffsetArray();
this.lineVertexArray = new SymbolLineVertexArray();
this.symbolInstances = new SymbolInstanceArray();
this.textAnchorOffsets = new TextAnchorOffsetArray();
}
calculateGlyphDependencies(text, stack, textAlongLine, allowVerticalPlacement, doesAllowVerticalWritingMode) {
for (const char of text) {
stack[char.codePointAt(0)] = true;
if ((textAlongLine || allowVerticalPlacement) && doesAllowVerticalWritingMode) {
const verticalChar = verticalizedCharacterMap[char];
if (verticalChar) stack[verticalChar.codePointAt(0)] = true;
}
}
}
populate(features, options, canonical) {
const layer = this.layers[0];
const layout = layer.layout;
const textFont = layout.get("text-font");
const textField = layout.get("text-field");
const iconImage = layout.get("icon-image");
const hasText = (textField.value.kind !== "constant" || textField.value.value instanceof Formatted && !textField.value.value.isEmpty() || textField.value.value.toString().length > 0) && (textFont.value.kind !== "constant" || textFont.value.value.length > 0);
const hasIcon = iconImage.value.kind !== "constant" || !!iconImage.value.value || Object.keys(iconImage.parameters).length > 0;
const symbolSortKey = layout.get("symbol-sort-key");
this.features = [];
if (!hasText && !hasIcon) return;
const icons = options.iconDependencies;
const stacks = options.glyphDependencies;
const availableImages = options.availableImages;
const globalProperties = new EvaluationParameters(this.zoom);
for (const { feature, id, index, sourceLayerIndex } of features) {
const needGeometry = layer._featureFilter.needGeometry;
const evaluationFeature = toEvaluationFeature(feature, needGeometry);
if (!layer._featureFilter.filter(globalProperties, evaluationFeature, canonical)) continue;
if (!needGeometry) evaluationFeature.geometry = loadGeometry(feature);
let text;
if (hasText) {
const resolvedTokens = layer.getValueAndResolveTokens("text-field", evaluationFeature, canonical, availableImages);
const formattedText = Formatted.factory(resolvedTokens);
this.hasRTLText ||= containsRTLText(formattedText);
if (!this.hasRTLText || rtlWorkerPlugin.getRTLTextPluginStatus() === "unavailable" || this.hasRTLText && rtlWorkerPlugin.isParsed()) text = transformText(formattedText, layer, evaluationFeature);
}
let icon;
if (hasIcon) {
const resolvedTokens = layer.getValueAndResolveTokens("icon-image", evaluationFeature, canonical, availableImages);
if (resolvedTokens instanceof ResolvedImage) icon = resolvedTokens;
else icon = ResolvedImage.fromString(resolvedTokens);
}
if (!text && !icon) continue;
const sortKey = this.sortFeaturesByKey ? symbolSortKey.evaluate(evaluationFeature, {}, canonical) : void 0;
const symbolFeature = {
id,
text,
icon,
index,
sourceLayerIndex,
geometry: evaluationFeature.geometry,
properties: feature.properties,
type: VectorTileFeature.types[feature.type],
sortKey
};
this.features.push(symbolFeature);
if (icon) icons[icon.name] = true;
if (text) {
const fontStack = textFont.evaluate(evaluationFeature, {}, canonical).join(",");
const textAlongLine = layout.get("text-rotation-alignment") !== "viewport" && layout.get("symbol-placement") !== "point";
this.allowVerticalPlacement = this.writingModes?.includes(2);
for (const section of text.sections) if (!section.image) {
const doesAllowVerticalWritingMode = allowsVerticalWritingMode(text.toString());
const sectionFont = section.fontStack || fontStack;
stacks[sectionFont] ||= {};
this.calculateGlyphDependencies(section.text, stacks[sectionFont], textAlongLine, this.allowVerticalPlacement, doesAllowVerticalWritingMode);
} else icons[section.image.name] = true;
}
}
if (layout.get("symbol-placement") === "line") this.features = mergeLines(this.features);
if (this.sortFeaturesByKey) this.features.sort((a, b) => {
return a.sortKey - b.sortKey;
});
}
update(states, vtLayer, imagePositions) {
if (!this.stateDependentLayers.length) return;
this.text.programConfigurations.updatePaintArrays(states, vtLayer, this.layers, { imagePositions });
this.icon.programConfigurations.updatePaintArrays(states, vtLayer, this.layers, { imagePositions });
}
isEmpty() {
return this.symbolInstances.length === 0 && !this.hasRTLText;
}
uploadPending() {
return !this.uploaded || this.text.programConfigurations.needsUpload || this.icon.programConfigurations.needsUpload;
}
upload(context) {
if (!this.uploaded && this.hasDebugData()) {
this.textCollisionBox.upload(context);
this.iconCollisionBox.upload(context);
}
this.text.upload(context, this.sortFeaturesByY, !this.uploaded, this.text.programConfigurations.needsUpload);
this.icon.upload(context, this.sortFeaturesByY, !this.uploaded, this.icon.programConfigurations.needsUpload);
this.uploaded = true;
}
destroyDebugData() {
this.textCollisionBox.destroy();
this.iconCollisionBox.destroy();
}
destroy() {
this.text.destroy();
this.icon.destroy();
if (this.hasDebugData()) this.destroyDebugData();
}
addToLineVertexArray(anchor, line) {
const lineStartIndex = this.lineVertexArray.length;
if (anchor.segment !== void 0) {
let sumForwardLength = anchor.dist(line[anchor.segment + 1]);
let sumBackwardLength = anchor.dist(line[anchor.segment]);
const vertices = {};
for (let i = anchor.segment + 1; i < line.length; i++) {
vertices[i] = {
x: line[i].x,
y: line[i].y,
tileUnitDistanceFromAnchor: sumForwardLength
};
if (i < line.length - 1) sumForwardLength += line[i + 1].dist(line[i]);
}
for (let i = anchor.segment || 0; i >= 0; i--) {
vertices[i] = {
x: line[i].x,
y: line[i].y,
tileUnitDistanceFromAnchor: sumBackwardLength
};
if (i > 0) sumBackwardLength += line[i - 1].dist(line[i]);
}
for (let i = 0; i < line.length; i++) {
const vertex = vertices[i];
this.lineVertexArray.emplaceBack(vertex.x, vertex.y, vertex.tileUnitDistanceFromAnchor);
}
}
return {
lineStartIndex,
lineLength: this.lineVertexArray.length - lineStartIndex
};
}
addSymbols(arrays, quads, sizeVertex, lineOffset, alongLine, feature, writingMode, labelAnchor, lineStartIndex, lineLength, associatedIconIndex, canonical) {
const indexArray = arrays.indexArray;
const layoutVertexArray = arrays.layoutVertexArray;
const segment = arrays.segments.prepareSegment(4 * quads.length, layoutVertexArray, indexArray, this.canOverlap ? feature.sortKey : void 0);
const glyphOffsetArrayStart = this.glyphOffsetArray.length;
const vertexStartIndex = segment.vertexLength;
const angle = this.allowVerticalPlacement && writingMode === 2 ? Math.PI / 2 : 0;
const sections = feature.text && feature.text.sections;
for (let i = 0; i < quads.length; i++) {
const { tl, tr, bl, br, tex, pixelOffsetTL, pixelOffsetBR, minFontScaleX, minFontScaleY, glyphOffset, isSDF, sectionIndex } = quads[i];
const index = segment.vertexLength;
const y = glyphOffset[1];
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, tl.x, y + tl.y, tex.x, tex.y, sizeVertex, isSDF, pixelOffsetTL.x, pixelOffsetTL.y, minFontScaleX, minFontScaleY);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, tr.x, y + tr.y, tex.x + tex.w, tex.y, sizeVertex, isSDF, pixelOffsetBR.x, pixelOffsetTL.y, minFontScaleX, minFontScaleY);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, bl.x, y + bl.y, tex.x, tex.y + tex.h, sizeVertex, isSDF, pixelOffsetTL.x, pixelOffsetBR.y, minFontScaleX, minFontScaleY);
addVertex(layoutVertexArray, labelAnchor.x, labelAnchor.y, br.x, y + br.y, tex.x + tex.w, tex.y + tex.h, sizeVertex, isSDF, pixelOffsetBR.x, pixelOffsetBR.y, minFontScaleX, minFontScaleY);
addDynamicAttributes(arrays.dynamicLayoutVertexArray, labelAnchor, angle);
indexArray.emplaceBack(index, index + 2, index + 1);
indexArray.emplaceBack(index + 1, index + 2, index + 3);
segment.vertexLength += 4;
segment.primitiveLength += 2;
this.glyphOffsetArray.emplaceBack(glyphOffset[0]);
if (i === quads.length - 1 || sectionIndex !== quads[i + 1].sectionIndex) arrays.programConfigurations.populatePaintArrays(layoutVertexArray.length, feature, feature.index, {
imagePositions: {},
canonical,
formattedSection: sections?.[sectionIndex]
});
}
arrays.placedSymbolArray.emplaceBack(labelAnchor.x, labelAnchor.y, glyphOffsetArrayStart, this.glyphOffsetArray.length - glyphOffsetArrayStart, vertexStartIndex, lineStartIndex, lineLength, labelAnchor.segment, sizeVertex ? sizeVertex[0] : 0, sizeVertex ? sizeVertex[1] : 0, lineOffset[0], lineOffset[1], writingMode, 0, false, 0, associatedIconIndex);
}
_addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, point, anchorX, anchorY, extrude) {
collisionVertexArray.emplaceBack(0, 0);
return layoutVertexArray.emplaceBack(point.x, point.y, anchorX, anchorY, Math.round(extrude.x), Math.round(extrude.y));
}
addCollisionDebugVertices(x1, y1, x2, y2, arrays, boxAnchorPoint, symbolInstance) {
const segment = arrays.segments.prepareSegment(4, arrays.layoutVertexArray, arrays.indexArray);
const index = segment.vertexLength;
const layoutVertexArray = arrays.layoutVertexArray;
const collisionVertexArray = arrays.collisionVertexArray;
const anchorX = symbolInstance.anchorX;
const anchorY = symbolInstance.anchorY;
this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x1, y1));
this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x2, y1));
this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x2, y2));
this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x1, y2));
segment.vertexLength += 4;
const indexArray = arrays.indexArray;
indexArray.emplaceBack(index, index + 1);
indexArray.emplaceBack(index + 1, index + 2);
indexArray.emplaceBack(index + 2, index + 3);
indexArray.emplaceBack(index + 3, index);
segment.primitiveLength += 4;
}
addDebugCollisionBoxes(startIndex, endIndex, symbolInstance, isText) {
for (let b = startIndex; b < endIndex; b++) {
const box = this.collisionBoxArray.get(b);
const x1 = box.x1;
const y1 = box.y1;
const x2 = box.x2;
const y2 = box.y2;
this.addCollisionDebugVertices(x1, y1, x2, y2, isText ? this.textCollisionBox : this.iconCollisionBox, box.anchorPoint, symbolInstance);
}
}
generateCollisionDebugBuffers() {
if (this.hasDebugData()) this.destroyDebugData();
this.textCollisionBox = new CollisionBuffers(CollisionBoxLayoutArray, collisionBoxLayout.members, LineIndexArray);
this.iconCollisionBox = new CollisionBuffers(CollisionBoxLayoutArray, collisionBoxLayout.members, LineIndexArray);
for (let i = 0; i < this.symbolInstances.length; i++) {
const symbolInstance = this.symbolInstances.get(i);
this.addDebugCollisionBoxes(symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance, true);
this.addDebugCollisionBoxes(symbolInstance.verticalTextBoxStartIndex, symbolInstance.verticalTextBoxEndIndex, symbolInstance, true);
this.addDebugCollisionBoxes(symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance, false);
this.addDebugCollisionBoxes(symbolInstance.verticalIconBoxStartIndex, symbolInstance.verticalIconBoxEndIndex, symbolInstance, false);
}
}
_deserializeCollisionBoxesForSymbol(collisionBoxArray, textStartIndex, textEndIndex, verticalTextStartIndex, verticalTextEndIndex, iconStartIndex, iconEndIndex, verticalIconStartIndex, verticalIconEndIndex) {
const collisionArrays = {};
for (let k = textStartIndex; k < textEndIndex; k++) {
const box = collisionBoxArray.get(k);
collisionArrays.textBox = {
x1: box.x1,
y1: box.y1,
x2: box.x2,
y2: box.y2,
anchorPointX: box.anchorPointX,
anchorPointY: box.anchorPointY
};
collisionArrays.textFeatureIndex = box.featureIndex;
break;
}
for (let k = verticalTextStartIndex; k < verticalTextEndIndex; k++) {
const box = collisionBoxArray.get(k);
collisionArrays.verticalTextBox = {
x1: box.x1,
y1: box.y1,
x2: box.x2,
y2: box.y2,
anchorPointX: box.anchorPointX,
anchorPointY: box.anchorPointY
};
collisionArrays.verticalTextFeatureIndex = box.featureIndex;
break;
}
for (let k = iconStartIndex; k < iconEndIndex; k++) {
const box = collisionBoxArray.get(k);
collisionArrays.iconBox = {
x1: box.x1,
y1: box.y1,
x2: box.x2,
y2: box.y2,
anchorPointX: box.anchorPointX,
anchorPointY: box.anchorPointY
};
collisionArrays.iconFeatureIndex = box.featureIndex;
break;
}
for (let k = verticalIconStartIndex; k < verticalIconEndIndex; k++) {
const box = collisionBoxArray.get(k);
collisionArrays.verticalIconBox = {
x1: box.x1,
y1: box.y1,
x2: box.x2,
y2: box.y2,
anchorPointX: box.anchorPointX,
anchorPointY: box.anchorPointY
};
collisionArrays.verticalIconFeatureIndex = box.featureIndex;
break;
}
return collisionArrays;
}
deserializeCollisionBoxes(collisionBoxArray) {
this.collisionArrays = [];
for (let i = 0; i < this.symbolInstances.length; i++) {
const symbolInstance = this.symbolInstances.get(i);
this.collisionArrays.push(this._deserializeCollisionBoxesForSymbol(collisionBoxArray, symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance.verticalTextBoxStartIndex, symbolInstance.verticalTextBoxEndIndex, symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance.verticalIconBoxStartIndex, symbolInstance.verticalIconBoxEndIndex));
}
}
hasTextData() {
return this.text.segments.get().length > 0;
}
hasIconData() {
return this.icon.segments.get().length > 0;
}
hasDebugData() {
return this.textCollisionBox && this.iconCollisionBox;
}
hasTextCollisionBoxData() {
return this.hasDebugData() && this.textCollisionBox.segments.get().length > 0;
}
hasIconCollisionBoxData() {
return this.hasDebugData() && this.iconCollisionBox.segments.get().length > 0;
}
addIndicesForPlacedSymbol(iconOrText, placedSymbolIndex) {
const placedSymbol = iconOrText.placedSymbolArray.get(placedSymbolIndex);
const endIndex = placedSymbol.vertexStartIndex + placedSymbol.numGlyphs * 4;
for (let vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
iconOrText.indexArray.emplaceBack(vertexIndex, vertexIndex + 2, vertexIndex + 1);
iconOrText.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
}
}
getSortedSymbolIndexes(angle) {
if (this.sortedAngle === angle && this.symbolInstanceIndexes !== void 0) return this.symbolInstanceIndexes;
const sin = Math.sin(angle);
const cos = Math.cos(angle);
const rotatedYs = [];
const featureIndexes = [];
const result = [];
for (let i = 0; i < this.symbolInstances.length; ++i) {
result.push(i);
const symbolInstance = this.symbolInstances.get(i);
rotatedYs.push(Math.round(sin * symbolInstance.anchorX + cos * symbolInstance.anchorY) | 0);
featureIndexes.push(symbolInstance.featureIndex);
}
result.sort((aIndex, bIndex) => {
return rotatedYs[aIndex] - rotatedYs[bIndex] || featureIndexes[bIndex] - featureIndexes[aIndex];
});
return result;
}
addToSortKeyRanges(symbolInstanceIndex, sortKey) {
const last = this.sortKeyRanges[this.sortKeyRanges.length - 1];
if (last?.sortKey === sortKey) last.symbolInstanceEnd = symbolInstanceIndex + 1;
else this.sortKeyRanges.push({
sortKey,
symbolInstanceStart: symbolInstanceIndex,
symbolInstanceEnd: symbolInstanceIndex + 1
});
}
sortFeatures(angle) {
if (!this.sortFeaturesByY) return;
if (this.sortedAngle === angle) return;
if (this.text.segments.get().length > 1 || this.icon.segments.get().length > 1) return;
this.symbolInstanceIndexes = this.getSortedSymbolIndexes(angle);
this.sortedAngle = angle;
this.text.indexArray.clear();
this.icon.indexArray.clear();
this.featureSortOrder = [];
for (const i of this.symbolInstanceIndexes) {
const symbolInstance = this.symbolInstances.get(i);
this.featureSortOrder.push(symbolInstance.featureIndex);
const textIndices = [
symbolInstance.rightJustifiedTextSymbolIndex,
symbolInstance.centerJustifiedTextSymbolIndex,
symbolInstance.leftJustifiedTextSymbolIndex
];
for (let i = 0; i < textIndices.length; i++) {
const index = textIndices[i];
if (index >= 0 && textIndices.indexOf(index) === i) this.addIndicesForPlacedSymbol(this.text, index);
}
if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) this.addIndicesForPlacedSymbol(this.text, symbolInstance.verticalPlacedTextSymbolIndex);
if (symbolInstance.placedIconSymbolIndex >= 0) this.addIndicesForPlacedSymbol(this.icon, symbolInstance.placedIconSymbolIndex);
if (symbolInstance.verticalPlacedIconSymbolIndex >= 0) this.addIndicesForPlacedSymbol(this.icon, symbolInstance.verticalPlacedIconSymbolIndex);
}
if (this.text.indexBuffer) this.text.indexBuffer.updateData(this.text.indexArray);
if (this.icon.indexBuffer) this.icon.indexBuffer.updateData(this.icon.indexArray);
}
};
register("SymbolBucket", SymbolBucket, { omit: [
"layers",
"collisionBoxArray",
"features",
"compareText"
] });
SymbolBucket.MAX_GLYPHS = 65535;
SymbolBucket.addDynamicAttributes = addDynamicAttributes;
//#endregion
//#region src/util/resolve_tokens.ts
/**
* Replace tokens in a string template with values in an object
*
* @param properties - a key/value relationship between tokens and replacements
* @param text - the template string
* @returns the template with tokens replaced
*/
function resolveTokens(properties, text) {
return text.replace(/{([^{}]+)}/g, (match, key) => {
return properties && key in properties ? String(properties[key]) : "";
});
}
//#endregion
//#region src/style/style_layer/symbol_style_layer_properties.g.ts
let layout;
const getLayout = () => layout = layout || new Properties({
"symbol-placement": new DataConstantProperty(latest["layout_symbol"]["symbol-placement"], "symbol-placement"),
"symbol-spacing": new DataConstantProperty(latest["layout_symbol"]["symbol-spacing"], "symbol-spacing"),
"symbol-avoid-edges": new DataConstantProperty(latest["layout_symbol"]["symbol-avoid-edges"], "symbol-avoid-edges"),
"symbol-sort-key": new DataDrivenProperty(latest["layout_symbol"]["symbol-sort-key"], "symbol-sort-key"),
"symbol-z-order": new DataConstantProperty(latest["layout_symbol"]["symbol-z-order"], "symbol-z-order"),
"icon-allow-overlap": new DataConstantProperty(latest["layout_symbol"]["icon-allow-overlap"], "icon-allow-overlap"),
"icon-overlap": new DataConstantProperty(latest["layout_symbol"]["icon-overlap"], "icon-overlap"),
"icon-ignore-placement": new DataConstantProperty(latest["layout_symbol"]["icon-ignore-placement"], "icon-ignore-placement"),
"icon-optional": new DataConstantProperty(latest["layout_symbol"]["icon-optional"], "icon-optional"),
"icon-rotation-alignment": new DataConstantProperty(latest["layout_symbol"]["icon-rotation-alignment"], "icon-rotation-alignment"),
"icon-size": new DataDrivenProperty(latest["layout_symbol"]["icon-size"], "icon-size"),
"icon-text-fit": new DataConstantProperty(latest["layout_symbol"]["icon-text-fit"], "icon-text-fit"),
"icon-text-fit-padding": new DataConstantProperty(latest["layout_symbol"]["icon-text-fit-padding"], "icon-text-fit-padding"),
"icon-image": new DataDrivenProperty(latest["layout_symbol"]["icon-image"], "icon-image"),
"icon-rotate": new DataDrivenProperty(latest["layout_symbol"]["icon-rotate"], "icon-rotate"),
"icon-padding": new DataDrivenProperty(latest["layout_symbol"]["icon-padding"], "icon-padding"),
"icon-keep-upright": new DataConstantProperty(latest["layout_symbol"]["icon-keep-upright"], "icon-keep-upright"),
"icon-offset": new DataDrivenProperty(latest["layout_symbol"]["icon-offset"], "icon-offset"),
"icon-anchor": new DataDrivenProperty(latest["layout_symbol"]["icon-anchor"], "icon-anchor"),
"icon-pitch-alignment": new DataConstantProperty(latest["layout_symbol"]["icon-pitch-alignment"], "icon-pitch-alignment"),
"text-pitch-alignment": new DataConstantProperty(latest["layout_symbol"]["text-pitch-alignment"], "text-pitch-alignment"),
"text-rotation-alignment": new DataConstantProperty(latest["layout_symbol"]["text-rotation-alignment"], "text-rotation-alignment"),
"text-field": new DataDrivenProperty(latest["layout_symbol"]["text-field"], "text-field"),
"text-font": new DataDrivenProperty(latest["layout_symbol"]["text-font"], "text-font"),
"text-size": new DataDrivenProperty(latest["layout_symbol"]["text-size"], "text-size"),
"text-max-width": new DataDrivenProperty(latest["layout_symbol"]["text-max-width"], "text-max-width"),
"text-line-height": new DataConstantProperty(latest["layout_symbol"]["text-line-height"], "text-line-height"),
"text-letter-spacing": new DataDrivenProperty(latest["layout_symbol"]["text-letter-spacing"], "text-letter-spacing"),
"text-justify": new DataDrivenProperty(latest["layout_symbol"]["text-justify"], "text-justify"),
"text-radial-offset": new DataDrivenProperty(latest["layout_symbol"]["text-radial-offset"], "text-radial-offset"),
"text-variable-anchor": new DataConstantProperty(latest["layout_symbol"]["text-variable-anchor"], "text-variable-anchor"),
"text-variable-anchor-offset": new DataDrivenProperty(latest["layout_symbol"]["text-variable-anchor-offset"], "text-variable-anchor-offset"),
"text-anchor": new DataDrivenProperty(latest["layout_symbol"]["text-anchor"], "text-anchor"),
"text-max-angle": new DataConstantProperty(latest["layout_symbol"]["text-max-angle"], "text-max-angle"),
"text-writing-mode": new DataConstantProperty(latest["layout_symbol"]["text-writing-mode"], "text-writing-mode"),
"text-rotate": new DataDrivenProperty(latest["layout_symbol"]["text-rotate"], "text-rotate"),
"text-padding": new DataConstantProperty(latest["layout_symbol"]["text-padding"], "text-padding"),
"text-keep-upright": new DataConstantProperty(latest["layout_symbol"]["text-keep-upright"], "text-keep-upright"),
"text-transform": new DataDrivenProperty(latest["layout_symbol"]["text-transform"], "text-transform"),
"text-offset": new DataDrivenProperty(latest["layout_symbol"]["text-offset"], "text-offset"),
"text-allow-overlap": new DataConstantProperty(latest["layout_symbol"]["text-allow-overlap"], "text-allow-overlap"),
"text-overlap": new DataConstantProperty(latest["layout_symbol"]["text-overlap"], "text-overlap"),
"text-ignore-placement": new DataConstantProperty(latest["layout_symbol"]["text-ignore-placement"], "text-ignore-placement"),
"text-optional": new DataConstantProperty(latest["layout_symbol"]["text-optional"], "text-optional")
});
let paint$1;
const getPaint$1 = () => paint$1 = paint$1 || new Properties({
"icon-opacity": new DataDrivenProperty(latest["paint_symbol"]["icon-opacity"], "icon-opacity"),
"icon-color": new DataDrivenProperty(latest["paint_symbol"]["icon-color"], "icon-color"),
"icon-halo-color": new DataDrivenProperty(latest["paint_symbol"]["icon-halo-color"], "icon-halo-color"),
"icon-halo-width": new DataDrivenProperty(latest["paint_symbol"]["icon-halo-width"], "icon-halo-width"),
"icon-halo-blur": new DataDrivenProperty(latest["paint_symbol"]["icon-halo-blur"], "icon-halo-blur"),
"icon-translate": new DataConstantProperty(latest["paint_symbol"]["icon-translate"], "icon-translate"),
"icon-translate-anchor": new DataConstantProperty(latest["paint_symbol"]["icon-translate-anchor"], "icon-translate-anchor"),
"text-opacity": new DataDrivenProperty(latest["paint_symbol"]["text-opacity"], "text-opacity"),
"text-color": new DataDrivenProperty(latest["paint_symbol"]["text-color"], "text-color", {
runtimeType: ColorType,
getOverride: (o) => o.textColor,
hasOverride: (o) => !!o.textColor
}),
"text-halo-color": new DataDrivenProperty(latest["paint_symbol"]["text-halo-color"], "text-halo-color"),
"text-halo-width": new DataDrivenProperty(latest["paint_symbol"]["text-halo-width"], "text-halo-width"),
"text-halo-blur": new DataDrivenProperty(latest["paint_symbol"]["text-halo-blur"], "text-halo-blur"),
"text-translate": new DataConstantProperty(latest["paint_symbol"]["text-translate"], "text-translate"),
"text-translate-anchor": new DataConstantProperty(latest["paint_symbol"]["text-translate-anchor"], "text-translate-anchor")
});
var symbol_style_layer_properties_g_default = {
get paint() {
return getPaint$1();
},
get layout() {
return getLayout();
}
};
//#endregion
//#region src/style/format_section_override.ts
var FormatSectionOverride = class {
constructor(defaultValue) {
if (defaultValue.property.overrides === void 0) throw new Error("overrides must be provided to instantiate FormatSectionOverride class");
this.type = defaultValue.property.overrides ? defaultValue.property.overrides.runtimeType : NullType;
this.defaultValue = defaultValue;
}
evaluate(ctx) {
if (ctx.formattedSection) {
const overrides = this.defaultValue.property.overrides;
if (overrides?.hasOverride(ctx.formattedSection)) return overrides.getOverride(ctx.formattedSection);
}
if (ctx.feature && ctx.featureState) return this.defaultValue.evaluate(ctx.feature, ctx.featureState);
return this.defaultValue.property.specification.default;
}
eachChild(fn) {
if (!this.defaultValue.isConstant()) {
const expr = this.defaultValue.value;
fn(expr._styleExpression.expression);
}
}
outputDefined() {
return false;
}
serialize() {
return null;
}
};
register("FormatSectionOverride", FormatSectionOverride, { omit: ["defaultValue"] });
//#endregion
//#region src/style/style_layer/symbol_style_layer.ts
const isSymbolStyleLayer = (layer) => layer.type === "symbol";
var SymbolStyleLayer = class SymbolStyleLayer extends StyleLayer {
constructor(layer, globalState) {
super(layer, symbol_style_layer_properties_g_default, globalState);
}
recalculate(parameters, availableImages) {
super.recalculate(parameters, availableImages);
if (this.layout.get("icon-rotation-alignment") === "auto") {
if (this.layout.get("symbol-placement") !== "point") this.layout._values["icon-rotation-alignment"] = "map";
else this.layout._values["icon-rotation-alignment"] = "viewport";
}
if (this.layout.get("text-rotation-alignment") === "auto") {
if (this.layout.get("symbol-placement") !== "point") this.layout._values["text-rotation-alignment"] = "map";
else this.layout._values["text-rotation-alignment"] = "viewport";
}
if (this.layout.get("text-pitch-alignment") === "auto") this.layout._values["text-pitch-alignment"] = this.layout.get("text-rotation-alignment") === "map" ? "map" : "viewport";
if (this.layout.get("icon-pitch-alignment") === "auto") this.layout._values["icon-pitch-alignment"] = this.layout.get("icon-rotation-alignment");
if (this.layout.get("symbol-placement") === "point") {
const writingModes = this.layout.get("text-writing-mode");
if (writingModes) {
const deduped = [];
for (const m of writingModes) if (!deduped.includes(m)) deduped.push(m);
this.layout._values["text-writing-mode"] = deduped;
} else this.layout._values["text-writing-mode"] = ["horizontal"];
}
this._setPaintOverrides();
}
getValueAndResolveTokens(name, feature, canonical, availableImages) {
const value = this.layout.get(name).evaluate(feature, {}, canonical, availableImages);
const unevaluated = this._unevaluatedLayout._values[name];
if (!unevaluated.isDataDriven() && !isExpression(unevaluated.value) && value) return resolveTokens(feature.properties, value);
return value;
}
createBucket(parameters) {
return new SymbolBucket(parameters);
}
queryRadius() {
return 0;
}
queryIntersectsFeature() {
throw new Error("Should take a different path in FeatureIndex");
}
_setPaintOverrides() {
for (const overridable of symbol_style_layer_properties_g_default.paint.overridableProperties) {
if (!SymbolStyleLayer.hasPaintOverride(this.layout, overridable)) continue;
const overridden = this.paint.get(overridable);
const styleExpression = new StyleExpression(new FormatSectionOverride(overridden), `layers[${this.id}].paint.${overridden.property.name}`, overridden.property.specification);
let expression = null;
if (overridden.value.kind === "constant" || overridden.value.kind === "source") expression = new ZoomConstantExpression("source", styleExpression);
else expression = new ZoomDependentExpression("composite", styleExpression, overridden.value.zoomStops);
this.paint._values[overridable] = new PossiblyEvaluatedPropertyValue(overridden.property, expression, overridden.parameters);
}
}
_handleOverridablePaintPropertyUpdate(name, oldValue, newValue) {
if (!this.layout || oldValue.isDataDriven() || newValue.isDataDriven()) return false;
return SymbolStyleLayer.hasPaintOverride(this.layout, name);
}
static hasPaintOverride(layout, propertyName) {
const textField = layout.get("text-field");
const property = symbol_style_layer_properties_g_default.paint.properties[propertyName];
let hasOverrides = false;
const checkSections = (sections) => {
for (const section of sections) if (property.overrides?.hasOverride(section)) {
hasOverrides = true;
return;
}
};
if (textField.value.kind === "constant" && textField.value.value instanceof Formatted) checkSections(textField.value.value.sections);
else if (textField.value.kind === "source" || textField.value.kind === "composite") {
const checkExpression = (expression) => {
if (hasOverrides) return;
if (expression instanceof Literal && typeOf(expression.value) === FormattedType) {
const formatted = expression.value;
checkSections(formatted.sections);
} else if (expression instanceof FormatExpression) checkSections(expression.sections);
else expression.eachChild(checkExpression);
};
const expr = textField.value;
if (expr._styleExpression) checkExpression(expr._styleExpression.expression);
}
return hasOverrides;
}
};
function getIconPadding(layout, feature, canonical, pixelRatio = 1) {
const values = layout.get("icon-padding").evaluate(feature, {}, canonical)?.values;
return [
values[0] * pixelRatio,
values[1] * pixelRatio,
values[2] * pixelRatio,
values[3] * pixelRatio
];
}
//#endregion
//#region src/style/style_layer/background_style_layer_properties.g.ts
let paint;
const getPaint = () => paint = paint || new Properties({
"background-color": new DataConstantProperty(latest["paint_background"]["background-color"], "background-color"),
"background-pattern": new CrossFadedProperty(latest["paint_background"]["background-pattern"], "background-pattern"),
"background-opacity": new DataConstantProperty(latest["paint_background"]["background-opacity"], "background-opacity")
});
var background_style_layer_properties_g_default = { get paint() {
return getPaint();
} };
//#endregion
//#region src/style/style_layer/background_style_layer.ts
const isBackgroundStyleLayer = (layer) => layer.type === "background";
var BackgroundStyleLayer = class extends StyleLayer {
constructor(layer, globalState) {
super(layer, background_style_layer_properties_g_default, globalState);
}
};
//#endregion
//#region src/style/style_layer/custom_style_layer.ts
function validateCustomStyleLayer(layerObject) {
const errors = [];
const id = layerObject.id;
if (id === void 0) errors.push(new ValidationError(`layers.${id}`, null, "missing required property \"id\""));
if (layerObject.render === void 0) errors.push(new ValidationError(`layers.${id}`, null, "missing required method \"render\""));
if (layerObject.renderingMode && layerObject.renderingMode !== "2d" && layerObject.renderingMode !== "3d") errors.push(new ValidationError(`layers.${id}`, null, "property \"renderingMode\" must be either \"2d\" or \"3d\""));
return errors;
}
const isCustomStyleLayer = (layer) => layer.type === "custom";
var CustomStyleLayer = class extends StyleLayer {
constructor(implementation, globalState) {
super(implementation, {}, globalState);
this.onAdd = (map) => {
if (this.implementation.onAdd) this.implementation.onAdd(map, map.painter.context.gl);
};
this.onRemove = (map) => {
if (this.implementation.onRemove) this.implementation.onRemove(map, map.painter.context.gl);
};
this.implementation = implementation;
}
is3D() {
return this.implementation.renderingMode === "3d";
}
hasOffscreenPass() {
return this.implementation.prerender !== void 0;
}
recalculate() {}
updateTransitions() {}
hasTransition() {
return false;
}
serialize() {
throw new Error("Custom layers cannot be serialized");
}
};
//#endregion
//#region src/style/create_style_layer.ts
function createStyleLayer(layer, globalState) {
if (layer.type === "custom") return new CustomStyleLayer(layer, globalState);
switch (layer.type) {
case "background": return new BackgroundStyleLayer(layer, globalState);
case "circle": return new CircleStyleLayer(layer, globalState);
case "color-relief": return new ColorReliefStyleLayer(layer, globalState);
case "fill": return new FillStyleLayer(layer, globalState);
case "fill-extrusion": return new FillExtrusionStyleLayer(layer, globalState);
case "heatmap": return new HeatmapStyleLayer(layer, globalState);
case "hillshade": return new HillshadeStyleLayer(layer, globalState);
case "line": return new LineStyleLayer(layer, globalState);
case "raster": return new RasterStyleLayer(layer, globalState);
case "symbol": return new SymbolStyleLayer(layer, globalState);
}
}
//#endregion
//#region src/util/throttled_invoker.ts
/**
* Invokes the wrapped function in a non-blocking way when trigger() is called.
* Invocation requests are ignored until the function was actually invoked.
*/
var ThrottledInvoker = class {
constructor(methodToThrottle) {
this._methodToThrottle = methodToThrottle;
this._triggered = false;
this._channel = new MessageChannel();
this._channel.port2.onmessage = () => {
this._triggered = false;
this._methodToThrottle();
};
}
trigger() {
if (this._triggered) return;
this._triggered = true;
this._channel?.port1.postMessage(true);
}
remove() {
delete this._channel;
this._methodToThrottle = () => {};
}
};
//#endregion
//#region src/util/actor.ts
const addEventDefaultOptions = { once: true };
/**
* An implementation of the [Actor design pattern](https://en.wikipedia.org/wiki/Actor_model)
* that maintains the relationship between asynchronous tasks and the objects
* that spin them off - in this case, tasks like parsing parts of styles,
* owned by the styles
*/
var Actor = class {
/**
* @param target - The target
* @param mapId - A unique identifier for the Map instance using this Actor.
*/
constructor(target, mapId) {
this.target = target;
this.mapId = mapId;
this.resolveRejects = {};
this.tasks = {};
this.taskQueue = [];
this.abortControllers = {};
this.messageHandlers = {};
this.invoker = new ThrottledInvoker(() => this.process());
this.subscription = subscribe(this.target, "message", (message) => this.receive(message), false);
this.globalScope = isWorker(self) ? target : window;
}
registerMessageHandler(type, handler) {
this.messageHandlers[type] = handler;
}
unregisterMessageHandler(type) {
delete this.messageHandlers[type];
}
/**
* Sends a message from a main-thread map to a Worker or from a Worker back to
* a main-thread map instance.
* @param message - the message to send
* @param abortController - an optional AbortController to abort the request
* @returns a promise that will be resolved with the response data
*/
sendAsync(message, abortController) {
return new Promise((resolve, reject) => {
const id = Math.round(Math.random() * 0xde0b6b3a7640000).toString(36).substring(0, 10);
const subscription = abortController ? subscribe(abortController.signal, "abort", () => {
subscription?.unsubscribe();
delete this.resolveRejects[id];
const cancelMessage = {
id,
type: "<cancel>",
origin: location.origin,
targetMapId: message.targetMapId,
sourceMapId: this.mapId
};
this.target.postMessage(cancelMessage);
reject(new AbortError(abortController.signal.reason));
}, addEventDefaultOptions) : null;
this.resolveRejects[id] = {
resolve: (value) => {
subscription?.unsubscribe();
resolve(value);
},
reject: (reason) => {
subscription?.unsubscribe();
reject(reason);
}
};
const buffers = [];
const messageToPost = {
...message,
id,
sourceMapId: this.mapId,
origin: location.origin,
data: serialize(message.data, buffers)
};
this.target.postMessage(messageToPost, { transfer: buffers });
});
}
receive(message) {
const data = message.data;
const id = data.id;
const SPECIAL_ORIGINS = [
"file://",
"resource://android",
"null"
];
const origins = [data.origin, location.origin];
const isSameOrigin = data.origin === location.origin;
const hasSpecialOrigin = origins.some((origin) => SPECIAL_ORIGINS.includes(origin));
if (!isSameOrigin && !hasSpecialOrigin) return;
if (data.targetMapId && this.mapId !== data.targetMapId) return;
if (data.type === "<cancel>") {
delete this.tasks[id];
const abortController = this.abortControllers[id];
delete this.abortControllers[id];
if (abortController) abortController.abort();
return;
}
if (isWorker(self) || data.mustQueue) {
this.tasks[id] = data;
this.taskQueue.push(id);
this.invoker.trigger();
return;
}
this.processTask(id, data);
}
process() {
if (this.taskQueue.length === 0) return;
const id = this.taskQueue.shift();
const task = this.tasks[id];
delete this.tasks[id];
if (this.taskQueue.length > 0) this.invoker.trigger();
if (!task) return;
this.processTask(id, task);
}
async processTask(id, task) {
if (task.type === "<response>") {
const resolveReject = this.resolveRejects[id];
delete this.resolveRejects[id];
if (!resolveReject) return;
if (task.error) resolveReject.reject(ensureError(deserialize(task.error)));
else resolveReject.resolve(deserialize(task.data));
return;
}
if (!this.messageHandlers[task.type]) {
this.completeTask(id, null, null);
return;
}
const params = deserialize(task.data);
const abortController = new AbortController();
this.abortControllers[id] = abortController;
try {
const data = await this.messageHandlers[task.type](task.sourceMapId, params, abortController);
this.completeTask(id, null, data);
} catch (err) {
this.completeTask(id, ensureError(err));
}
}
completeTask(id, err, data) {
const buffers = [];
delete this.abortControllers[id];
const responseMessage = {
id,
type: "<response>",
sourceMapId: this.mapId,
origin: location.origin,
error: err ? serialize(err) : null,
data: serialize(data, buffers)
};
this.target.postMessage(responseMessage, { transfer: buffers });
}
remove() {
this.invoker.remove();
this.subscription.unsubscribe();
}
};
//#endregion
//#region src/util/world_bounds.ts
/**
* Returns true if a given tile zoom (Z), X, and Y are in the bounds of the world.
* Zoom bounds are the minimum zoom (inclusive) through the maximum zoom (inclusive).
* X and Y bounds are 0 (inclusive) to their respective zoom-dependent maxima (exclusive).
*
* @param zoom - the tile zoom (Z)
* @param x - the tile X
* @param y - the tile Y
* @returns `true` if a given tile zoom, X, and Y are in the bounds of the world.
*/
function isInBoundsForTileZoomXY(zoom, x, y) {
return !(zoom < 0 || zoom > 25 || y < 0 || y >= Math.pow(2, zoom) || x < 0 || x >= Math.pow(2, zoom));
}
/**
* Returns true if a given zoom and `LngLat` are in the bounds of the world.
* Does not wrap `LngLat` when checking if in bounds.
* Zoom bounds are the minimum zoom (inclusive) through the maximum zoom (inclusive).
* `LngLat` bounds are the mercator world's north-west corner (inclusive) to its south-east corner (exclusive).
*
* @param zoom - the tile zoom (Z)
* @param LngLat - the `LngLat` object containing the longitude and latitude
* @returns `true` if a given zoom and `LngLat` are in the bounds of the world.
*/
function isInBoundsForZoomLngLat(zoom, lnglat) {
const { x, y } = MercatorCoordinate.fromLngLat(lnglat);
return !(zoom < 0 || zoom > 25 || y < 0 || y >= 1 || x < 0 || x >= 1);
}
//#endregion
//#region src/tile/tile_id.ts
/**
* A canonical way to define a tile ID
*/
var CanonicalTileID = class {
constructor(z, x, y) {
if (!isInBoundsForTileZoomXY(z, x, y)) throw new Error(`x=${x}, y=${y}, z=${z} outside of bounds. 0<=x<${Math.pow(2, z)}, 0<=y<${Math.pow(2, z)} 0<=z<=25 `);
this.z = z;
this.x = x;
this.y = y;
this.key = calculateTileKey(0, z, z, x, y);
}
equals(id) {
return this.z === id.z && this.x === id.x && this.y === id.y;
}
/**
* given a list of urls, choose a url template and return a tile URL
*/
url(urls, pixelRatio, scheme) {
const bbox = getTileBBox(this.x, this.y, this.z);
const quadkey = getQuadkey(this.z, this.x, this.y);
return urls[(this.x + this.y) % urls.length].replace(/{prefix}/g, (this.x % 16).toString(16) + (this.y % 16).toString(16)).replace(/{z}/g, String(this.z)).replace(/{x}/g, String(this.x)).replace(/{y}/g, String(scheme === "tms" ? Math.pow(2, this.z) - this.y - 1 : this.y)).replace(/{ratio}/g, pixelRatio > 1 ? "@2x" : "").replace(/{quadkey}/g, quadkey).replace(/{bbox-epsg-3857}/g, bbox);
}
isChildOf(parent) {
const dz = this.z - parent.z;
return dz > 0 && parent.x === this.x >> dz && parent.y === this.y >> dz;
}
getTilePoint(coord) {
const tilesAtZoom = Math.pow(2, this.z);
return new Point((coord.x * tilesAtZoom - this.x) * EXTENT$1, (coord.y * tilesAtZoom - this.y) * EXTENT$1);
}
toString() {
return `${this.z}/${this.x}/${this.y}`;
}
};
/**
* @internal
* An unwrapped tile identifier
*/
var UnwrappedTileID = class {
constructor(wrap, canonical) {
this.wrap = wrap;
this.canonical = canonical;
this.key = calculateTileKey(wrap, canonical.z, canonical.z, canonical.x, canonical.y);
}
};
/**
* An overscaled tile identifier
*/
var OverscaledTileID = class OverscaledTileID {
constructor(overscaledZ, wrap, z, x, y) {
this.terrainRttPosMatrix32f = null;
if (overscaledZ < z) throw new Error(`overscaledZ should be >= z; overscaledZ = ${overscaledZ}; z = ${z}`);
this.overscaledZ = overscaledZ;
this.wrap = wrap;
this.canonical = new CanonicalTileID(z, +x, +y);
this.key = calculateTileKey(wrap, overscaledZ, z, x, y);
}
clone() {
return new OverscaledTileID(this.overscaledZ, this.wrap, this.canonical.z, this.canonical.x, this.canonical.y);
}
equals(id) {
return this.overscaledZ === id.overscaledZ && this.wrap === id.wrap && this.canonical.equals(id.canonical);
}
/**
* Returns a new `OverscaledTileID` representing the tile at the target zoom level.
* When targetZ is greater than the current canonical z, the canonical coordinates are unchanged.
* When targetZ is less than the current canonical z, the canonical coordinates are updated.
* @param targetZ - the zoom level to scale to. Must be less than or equal to this.overscaledZ
* @returns a new OverscaledTileID representing the tile at the target zoom level
* @throws if targetZ is greater than this.overscaledZ
*/
scaledTo(targetZ) {
if (targetZ > this.overscaledZ) throw new Error(`targetZ > this.overscaledZ; targetZ = ${targetZ}; overscaledZ = ${this.overscaledZ}`);
const zDifference = this.canonical.z - targetZ;
if (targetZ > this.canonical.z) return new OverscaledTileID(targetZ, this.wrap, this.canonical.z, this.canonical.x, this.canonical.y);
else return new OverscaledTileID(targetZ, this.wrap, targetZ, this.canonical.x >> zDifference, this.canonical.y >> zDifference);
}
isOverscaled() {
return this.overscaledZ > this.canonical.z;
}
calculateScaledKey(targetZ, withWrap) {
if (targetZ > this.overscaledZ) throw new Error(`targetZ > this.overscaledZ; targetZ = ${targetZ}; overscaledZ = ${this.overscaledZ}`);
const zDifference = this.canonical.z - targetZ;
if (targetZ > this.canonical.z) return calculateTileKey(this.wrap * +withWrap, targetZ, this.canonical.z, this.canonical.x, this.canonical.y);
else return calculateTileKey(this.wrap * +withWrap, targetZ, targetZ, this.canonical.x >> zDifference, this.canonical.y >> zDifference);
}
isChildOf(parent) {
if (parent.wrap !== this.wrap) return false;
if (this.overscaledZ - parent.overscaledZ <= 0) return false;
if (parent.overscaledZ === 0) return this.overscaledZ > 0;
const dz = this.canonical.z - parent.canonical.z;
if (dz < 0) return false;
return parent.canonical.x === this.canonical.x >> dz && parent.canonical.y === this.canonical.y >> dz;
}
children(sourceMaxZoom) {
if (this.overscaledZ >= sourceMaxZoom) return [new OverscaledTileID(this.overscaledZ + 1, this.wrap, this.canonical.z, this.canonical.x, this.canonical.y)];
const z = this.canonical.z + 1;
const x = this.canonical.x * 2;
const y = this.canonical.y * 2;
return [
new OverscaledTileID(z, this.wrap, z, x, y),
new OverscaledTileID(z, this.wrap, z, x + 1, y),
new OverscaledTileID(z, this.wrap, z, x, y + 1),
new OverscaledTileID(z, this.wrap, z, x + 1, y + 1)
];
}
isLessThan(rhs) {
if (this.wrap < rhs.wrap) return true;
if (this.wrap > rhs.wrap) return false;
if (this.overscaledZ < rhs.overscaledZ) return true;
if (this.overscaledZ > rhs.overscaledZ) return false;
if (this.canonical.x < rhs.canonical.x) return true;
if (this.canonical.x > rhs.canonical.x) return false;
return this.canonical.y < rhs.canonical.y;
}
wrapped() {
return new OverscaledTileID(this.overscaledZ, 0, this.canonical.z, this.canonical.x, this.canonical.y);
}
unwrapTo(wrap) {
return new OverscaledTileID(this.overscaledZ, wrap, this.canonical.z, this.canonical.x, this.canonical.y);
}
overscaleFactor() {
return Math.pow(2, this.overscaledZ - this.canonical.z);
}
toUnwrapped() {
return new UnwrappedTileID(this.wrap, this.canonical);
}
toString() {
return `${this.overscaledZ}/${this.canonical.x}/${this.canonical.y}`;
}
getTilePoint(coord) {
return this.canonical.getTilePoint(new MercatorCoordinate(coord.x - this.wrap, coord.y));
}
/**
* Maps tile-local coordinates that may fall outside the `[0, extent)` range
* to the correct neighbor tile and the corresponding in-tile position.
*
* Coordinates can exceed tile bounds when geometry (e.g. symbol labels along
* lines) extends across tile edges. This method resolves such coordinates to
* the appropriate adjacent tile, wrapping horizontally across world boundaries
* and returning `null` when the target falls beyond the polar tile-grid limits.
*
* When the coordinates are already in bounds, the original tile ID is returned.
*
* @param x - x coordinate relative to this tile, may be outside `[0, extent)`
* @param y - y coordinate relative to this tile, may be outside `[0, extent)`
* @param extent - tile coordinate extent, default {@link EXTENT}
* @returns the resolved tile ID and in-tile coordinates, or `null` if the
* target is beyond the tile grid (e.g. past the poles)
*/
normalizeCoordinates(x, y, extent = EXTENT$1) {
if (x >= 0 && x < extent && y >= 0 && y < extent) return {
tileID: this,
x,
y
};
const tileOffsetX = Math.floor(x / extent);
const tileOffsetY = Math.floor(y / extent);
const newX = x - tileOffsetX * extent;
const newY = y - tileOffsetY * extent;
const z = this.canonical.z;
const dim = 1 << z;
const newCanonicalY = this.canonical.y + tileOffsetY;
if (newCanonicalY < 0 || newCanonicalY >= dim) return null;
let newCanonicalX = this.canonical.x + tileOffsetX;
let newWrap = this.wrap;
if (newCanonicalX < 0) {
newWrap -= Math.ceil(-newCanonicalX / dim);
newCanonicalX = (newCanonicalX % dim + dim) % dim;
} else if (newCanonicalX >= dim) {
newWrap += Math.floor(newCanonicalX / dim);
newCanonicalX = newCanonicalX % dim;
}
return {
tileID: new OverscaledTileID(this.overscaledZ, newWrap, z, newCanonicalX, newCanonicalY),
x: newX,
y: newY
};
}
};
function calculateTileKey(wrap, overscaledZ, z, x, y) {
wrap *= 2;
if (wrap < 0) wrap = wrap * -1 - 1;
const dim = 1 << z;
return (dim * dim * wrap + dim * y + x).toString(36) + z.toString(36) + overscaledZ.toString(36);
}
const EPSG3857_HALF_CIRCUMFERENCE = Math.PI * 6378137;
/**
* Builds the `{bbox-epsg-3857}` token used in WMS tile URLs: the tile's bounding
* box in EPSG:3857 meters as a `minX,minY,maxX,maxY` string.
*
* Inlined from the archived \@mapbox/whoots-js (ISC, Copyright (c) 2017 Mapbox).
*/
function getTileBBox(x, y, z) {
y = Math.pow(2, z) - y - 1;
const min = getEpsg3857Coords(x * 256, y * 256, z);
const max = getEpsg3857Coords((x + 1) * 256, (y + 1) * 256, z);
return `${min[0]},${min[1]},${max[0]},${max[1]}`;
}
/** Projects tile pixel coordinates to EPSG:3857 meters. */
function getEpsg3857Coords(x, y, z) {
const resolution = 2 * EPSG3857_HALF_CIRCUMFERENCE / 256 / Math.pow(2, z);
return [x * resolution - EPSG3857_HALF_CIRCUMFERENCE, y * resolution - EPSG3857_HALF_CIRCUMFERENCE];
}
function getQuadkey(z, x, y) {
let quadkey = "";
for (let i = z; i > 0; i--) {
const mask = 1 << i - 1;
quadkey += (x & mask ? 1 : 0) + (y & mask ? 2 : 0);
}
return quadkey;
}
function compareTileId(a, b) {
const aWrap = Math.abs(a.wrap * 2) - +(a.wrap < 0);
const bWrap = Math.abs(b.wrap * 2) - +(b.wrap < 0);
return a.overscaledZ - b.overscaledZ || bWrap - aWrap || b.canonical.y - a.canonical.y || b.canonical.x - a.canonical.x;
}
register("CanonicalTileID", CanonicalTileID);
register("OverscaledTileID", OverscaledTileID, { omit: ["terrainRttPosMatrix32f"] });
//#endregion
//#region node_modules/@maplibre/vt-pbf/dist/index.es.js
var FeatureWrapper = class {
constructor(feature, extent) {
this.feature = feature;
this.type = feature.type;
this.properties = feature.tags ? feature.tags : {};
this.extent = extent;
if ("id" in feature) {
if (typeof feature.id === "string") this.id = parseInt(feature.id, 10);
else if (typeof feature.id === "number" && !isNaN(feature.id)) this.id = feature.id;
}
}
loadGeometry() {
const geometry = [];
const rawGeo = this.feature.type === 1 ? [this.feature.geometry] : this.feature.geometry;
for (const ring of rawGeo) {
const newRing = [];
for (const point of ring) newRing.push(new Point(point[0], point[1]));
geometry.push(newRing);
}
return geometry;
}
};
const GEOJSON_TILE_LAYER_NAME = "_geojsonTileLayer";
var GeoJSONWrapper = class {
constructor(features, options) {
this.layers = { [GEOJSON_TILE_LAYER_NAME]: this };
this.name = GEOJSON_TILE_LAYER_NAME;
this.version = options ? options.version : 1;
this.extent = options ? options.extent : 4096;
this.length = features.length;
this.features = features;
}
feature(i) {
return new FeatureWrapper(this.features[i], this.extent);
}
};
/**
* Serialize a vector-tile-js-created tile to pbf
*
* @param tile - the tile to serialize
* @param jsonPrefix - a string prefix to prepend to JSON-stringified non-primitive property values, used to distinguish them from regular string values when parsing the tile later. Default is "".
* @return uncompressed, pbf-serialized tile data
*/
function fromVectorTileJs(tile, jsonPrefix = "") {
const out = new PbfWriter();
writeTile(tile, out, jsonPrefix);
return out.finish();
}
function writeTile(tile, pbf, jsonPrefix = "") {
for (const key in tile.layers) pbf.writeMessage(3, (layer, pbf) => writeLayer(layer, pbf, jsonPrefix), tile.layers[key]);
}
function writeLayer(layer, pbf, jsonPrefix = "") {
pbf.writeVarintField(15, layer.version || 1);
pbf.writeStringField(1, layer.name || "");
pbf.writeVarintField(5, layer.extent || 4096);
const context = {
jsonPrefix,
keys: [],
values: [],
keycache: {},
valuecache: {}
};
for (let i = 0; i < layer.length; i++) {
context.feature = layer.feature(i);
pbf.writeMessage(2, writeFeature, context);
}
const keys = context.keys;
for (const key of keys) pbf.writeStringField(3, key);
const values = context.values;
for (const value of values) pbf.writeMessage(4, writeValue, value);
}
function writeFeature(context, pbf) {
if (!context.feature) return;
const feature = context.feature;
if (feature.id !== void 0) pbf.writeVarintField(1, feature.id);
pbf.writeMessage(2, writeProperties, context);
pbf.writeVarintField(3, feature.type);
pbf.writeMessage(4, writeGeometry, feature);
}
function writeProperties(context, pbf) {
for (const key in context.feature?.properties) {
let value = context.feature.properties[key];
let keyIndex = context.keycache[key];
if (value == null) continue;
if (typeof keyIndex === "undefined") {
context.keys.push(key);
keyIndex = context.keys.length - 1;
context.keycache[key] = keyIndex;
}
pbf.writeVarint(keyIndex);
if (typeof value !== "string" && typeof value !== "boolean" && typeof value !== "number") value = context.jsonPrefix + JSON.stringify(value);
const valueKey = typeof value + ":" + value;
let valueIndex = context.valuecache[valueKey];
if (typeof valueIndex === "undefined") {
context.values.push(value);
valueIndex = context.values.length - 1;
context.valuecache[valueKey] = valueIndex;
}
pbf.writeVarint(valueIndex);
}
}
function command(cmd, length) {
return (length << 3) + (cmd & 7);
}
function zigzag(num) {
return num << 1 ^ num >> 31;
}
function writeGeometry(feature, pbf) {
const geometry = feature.loadGeometry();
const type = feature.type;
let x = 0;
let y = 0;
for (const ring of geometry) {
let count = 1;
if (type === 1) count = ring.length;
pbf.writeVarint(command(1, count));
const lineCount = type === 3 ? ring.length - 1 : ring.length;
for (let i = 0; i < lineCount; i++) {
if (i === 1 && type !== 1) pbf.writeVarint(command(2, lineCount - 1));
const dx = ring[i].x - x;
const dy = ring[i].y - y;
pbf.writeVarint(zigzag(dx));
pbf.writeVarint(zigzag(dy));
x += dx;
y += dy;
}
if (feature.type === 3) pbf.writeVarint(command(7, 1));
}
}
function writeValue(value, pbf) {
const type = typeof value;
if (type === "string") pbf.writeStringField(1, value);
else if (type === "boolean") pbf.writeBooleanField(7, value);
else if (type === "number") if (value % 1 !== 0) pbf.writeDoubleField(3, value);
else if (value < 0) pbf.writeSVarintField(6, value);
else pbf.writeVarintField(5, value);
}
//#endregion
//#region src/util/dictionary_coder.ts
var DictionaryCoder = class {
constructor(strings) {
this._stringToNumber = {};
this._numberToString = [];
for (let i = 0; i < strings.length; i++) {
const string = strings[i];
this._stringToNumber[string] = i;
this._numberToString[i] = string;
}
}
encode(string) {
return this._stringToNumber[string];
}
decode(n) {
if (n >= this._numberToString.length) throw new Error(`Out of bounds. Index requested n=${n} can't be >= this._numberToString.length ${this._numberToString.length}`);
return this._numberToString[n];
}
};
//#endregion
//#region src/util/vectortile_to_geojson.ts
/**
* A geojson feature
*/
var GeoJSONFeature = class {
constructor(vectorTileFeature, z, x, y, id) {
this.type = "Feature";
this._vectorTileFeature = vectorTileFeature;
this._x = x;
this._y = y;
this._z = z;
for (const key in vectorTileFeature.properties) {
if (typeof vectorTileFeature.properties[key] !== "string" || !vectorTileFeature.properties[key].startsWith("__$json__:")) continue;
vectorTileFeature.properties[key] = JSON.parse(vectorTileFeature.properties[key].slice(10));
}
this.properties = vectorTileFeature.properties;
this.id = id;
}
projectPoint(p, x0, y0, size) {
return [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp((1 - (p.y + y0) * 2 / size) * Math.PI)) - 90];
}
projectLine(line, x0, y0, size) {
return line.map((p) => this.projectPoint(p, x0, y0, size));
}
get geometry() {
if (this._geometry) return this._geometry;
const feature = this._vectorTileFeature;
const size = feature.extent * Math.pow(2, this._z);
const x0 = feature.extent * this._x;
const y0 = feature.extent * this._y;
const vtCoords = feature.loadGeometry();
switch (feature.type) {
case 1: {
const points = [];
for (const line of vtCoords) points.push(line[0]);
const coordinates = this.projectLine(points, x0, y0, size);
this._geometry = points.length === 1 ? {
type: "Point",
coordinates: coordinates[0]
} : {
type: "MultiPoint",
coordinates
};
break;
}
case 2: {
const coordinates = vtCoords.map((coord) => this.projectLine(coord, x0, y0, size));
this._geometry = coordinates.length === 1 ? {
type: "LineString",
coordinates: coordinates[0]
} : {
type: "MultiLineString",
coordinates
};
break;
}
case 3: {
const polygons = classifyRings(vtCoords);
const coordinates = [];
for (const polygon of polygons) coordinates.push(polygon.map((coord) => this.projectLine(coord, x0, y0, size)));
this._geometry = coordinates.length === 1 ? {
type: "Polygon",
coordinates: coordinates[0]
} : {
type: "MultiPolygon",
coordinates
};
break;
}
default: throw new Error(`unknown feature type: ${feature.type}`);
}
return this._geometry;
}
set geometry(g) {
this._geometry = g;
}
toJSON() {
const json = { geometry: this.geometry };
for (const i in this) {
if (i === "_geometry" || i === "_vectorTileFeature" || i === "_x" || i === "_y" || i === "_z") continue;
json[i] = this[i];
}
return json;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/vector.js
var Vector = class {
constructor(_name, dataBuffer, sizeOrNullabilityBuffer) {
this._name = _name;
this.dataBuffer = dataBuffer;
if (typeof sizeOrNullabilityBuffer === "number") this._size = sizeOrNullabilityBuffer;
else {
this.nullabilityBuffer = sizeOrNullabilityBuffer;
this._size = sizeOrNullabilityBuffer.size();
}
}
getValue(index) {
return this.nullabilityBuffer && !this.nullabilityBuffer.get(index) ? null : this.getValueFromBuffer(index);
}
has(index) {
return this.nullabilityBuffer?.get(index) || !this.nullabilityBuffer;
}
get name() {
return this._name;
}
get size() {
return this._size;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/fixedSizeVector.js
var FixedSizeVector = class extends Vector {};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/int32FlatVector.js
var Int32FlatVector = class extends FixedSizeVector {
getValueFromBuffer(index) {
return this.dataBuffer[index];
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/doubleFlatVector.js
var DoubleFlatVector = class extends FixedSizeVector {
getValueFromBuffer(index) {
return this.dataBuffer[index];
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/sequence/sequenceVector.js
var SequenceVector = class extends Vector {
constructor(name, baseValueBuffer, delta, size) {
super(name, baseValueBuffer, size);
this.delta = delta;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/sequence/int32SequenceVector.js
var Int32SequenceVector = class extends SequenceVector {
constructor(name, baseValue, delta, size, isSigned) {
super(name, isSigned ? Int32Array.of(baseValue) : Uint32Array.of(baseValue), delta, size);
}
getValueFromBuffer(index) {
return this.dataBuffer[0] + index * this.delta;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/constant/int32ConstVector.js
var Int32ConstVector = class extends Vector {
constructor(name, value, sizeOrNullabilityBuffer, isSigned) {
super(name, isSigned ? Int32Array.of(value) : Uint32Array.of(value), sizeOrNullabilityBuffer);
}
getValueFromBuffer(_index) {
return this.dataBuffer[0];
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/featureTable.js
var FeatureTable = class {
constructor(_name, _geometryVector, _idVector, _propertyVectors, _extent = 4096) {
this._name = _name;
this._geometryVector = _geometryVector;
this._idVector = _idVector;
this._propertyVectors = _propertyVectors;
this._extent = _extent;
if (_name.length === 0) throw new Error("Missing layer name");
}
get name() {
return this._name;
}
get idVector() {
return this._idVector;
}
get geometryVector() {
return this._geometryVector;
}
get propertyVectors() {
return this._propertyVectors ?? [];
}
getPropertyVector(name) {
if (!this.propertyVectorsMap) this.propertyVectorsMap = new Map(this.propertyVectors.map((vector) => [vector.name, vector]));
return this.propertyVectorsMap.get(name);
}
get numFeatures() {
return this.geometryVector.numGeometries;
}
get extent() {
return this._extent;
}
/**
* Returns all features as an array
*/
getFeatures() {
const features = [];
const geometries = this.geometryVector.getGeometries();
for (let i = 0; i < this.numFeatures; i++) {
let id;
if (this.idVector) {
const idValue = this.idVector.getValue(i);
if (idValue !== null) id = this.containsMaxSafeIntegerValues(this.idVector) ? Number(idValue) : idValue;
}
const geometry = {
coordinates: geometries[i],
type: this.geometryVector.geometryType(i)
};
const properties = {};
for (const propertyColumn of this.propertyVectors) {
if (!propertyColumn) continue;
const columnName = propertyColumn.name;
const propertyValue = propertyColumn.getValue(i);
if (propertyValue !== null) properties[columnName] = propertyValue;
}
features.push({
id,
geometry,
properties
});
}
return features;
}
containsMaxSafeIntegerValues(idVector) {
return idVector instanceof Int32FlatVector || idVector instanceof Int32ConstVector || idVector instanceof Int32SequenceVector || idVector instanceof DoubleFlatVector;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tileset/tilesetMetadata.js
const ColumnScope = {
FEATURE: 0,
VERTEX: 1
};
const ScalarType = {
BOOLEAN: 0,
INT_8: 1,
UINT_8: 2,
INT_32: 3,
UINT_32: 4,
INT_64: 5,
UINT_64: 6,
FLOAT: 7,
DOUBLE: 8,
STRING: 9
};
const ComplexType = {
GEOMETRY: 0,
STRUCT: 1,
MAP: 2
};
const LogicalScalarType = { ID: 0 };
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/intWrapper.js
var IntWrapper = class {
constructor(value) {
this.value = value;
}
get() {
return this.value;
}
set(v) {
this.value = v;
}
increment() {
return this.value++;
}
add(v) {
this.value += v;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/logicalLevelTechnique.js
var LogicalLevelTechnique;
(function(LogicalLevelTechnique) {
LogicalLevelTechnique["NONE"] = "NONE";
LogicalLevelTechnique["DELTA"] = "DELTA";
LogicalLevelTechnique["COMPONENTWISE_DELTA"] = "COMPONENTWISE_DELTA";
LogicalLevelTechnique["RLE"] = "RLE";
LogicalLevelTechnique["MORTON"] = "MORTON";
LogicalLevelTechnique["PDE"] = "PDE";
})(LogicalLevelTechnique || (LogicalLevelTechnique = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/physicalLevelTechnique.js
var PhysicalLevelTechnique;
(function(PhysicalLevelTechnique) {
PhysicalLevelTechnique["NONE"] = "NONE";
/**
* Preferred option, tends to produce the best compression ratio and decoding performance.
* But currently only limited to 32 bit integer.
*/
PhysicalLevelTechnique["FAST_PFOR"] = "FAST_PFOR";
/**
* Can produce better results in combination with a heavyweight compression scheme like Gzip.
* Simple compression scheme where the decoder are easier to implement compared to FastPfor.
*/
PhysicalLevelTechnique["VARINT"] = "VARINT";
})(PhysicalLevelTechnique || (PhysicalLevelTechnique = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/fastPforShared.js
/**
* Bit masks for each bitwidth 0-32.
* DO NOT MUTATE - this is a shared constant.
*/
const masks = /* @__PURE__ */ new Uint32Array(33);
masks[0] = 0;
for (let bitWidth = 1; bitWidth <= 32; bitWidth++) masks[bitWidth] = bitWidth === 32 ? 4294967295 : 4294967295 >>> 32 - bitWidth;
const MASKS = masks;
const DEFAULT_PAGE_SIZE = 65536;
function greatestMultiple(value, factor) {
return value - value % factor;
}
function roundUpToMultipleOf32(value) {
return greatestMultiple(value + 31, 32);
}
function normalizePageSize(pageSize) {
if (!Number.isFinite(pageSize) || pageSize <= 0) return DEFAULT_PAGE_SIZE;
const aligned = greatestMultiple(Math.floor(pageSize), 256);
return aligned === 0 ? 256 : aligned;
}
function bswap32(value) {
const x = value >>> 0;
return ((x & 255) << 24 | (x & 65280) << 8 | x >>> 8 & 65280 | x >>> 24 & 255) >>> 0;
}
function fastUnpack32_2(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
out[op++] = in0 >>> 0 & 3;
out[op++] = in0 >>> 2 & 3;
out[op++] = in0 >>> 4 & 3;
out[op++] = in0 >>> 6 & 3;
out[op++] = in0 >>> 8 & 3;
out[op++] = in0 >>> 10 & 3;
out[op++] = in0 >>> 12 & 3;
out[op++] = in0 >>> 14 & 3;
out[op++] = in0 >>> 16 & 3;
out[op++] = in0 >>> 18 & 3;
out[op++] = in0 >>> 20 & 3;
out[op++] = in0 >>> 22 & 3;
out[op++] = in0 >>> 24 & 3;
out[op++] = in0 >>> 26 & 3;
out[op++] = in0 >>> 28 & 3;
out[op++] = in0 >>> 30 & 3;
out[op++] = in1 >>> 0 & 3;
out[op++] = in1 >>> 2 & 3;
out[op++] = in1 >>> 4 & 3;
out[op++] = in1 >>> 6 & 3;
out[op++] = in1 >>> 8 & 3;
out[op++] = in1 >>> 10 & 3;
out[op++] = in1 >>> 12 & 3;
out[op++] = in1 >>> 14 & 3;
out[op++] = in1 >>> 16 & 3;
out[op++] = in1 >>> 18 & 3;
out[op++] = in1 >>> 20 & 3;
out[op++] = in1 >>> 22 & 3;
out[op++] = in1 >>> 24 & 3;
out[op++] = in1 >>> 26 & 3;
out[op++] = in1 >>> 28 & 3;
out[op] = in1 >>> 30 & 3;
}
function fastUnpack32_3(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
out[op++] = in0 >>> 0 & 7;
out[op++] = in0 >>> 3 & 7;
out[op++] = in0 >>> 6 & 7;
out[op++] = in0 >>> 9 & 7;
out[op++] = in0 >>> 12 & 7;
out[op++] = in0 >>> 15 & 7;
out[op++] = in0 >>> 18 & 7;
out[op++] = in0 >>> 21 & 7;
out[op++] = in0 >>> 24 & 7;
out[op++] = in0 >>> 27 & 7;
out[op++] = (in0 >>> 30 | (in1 & 1) << 2) & 7;
out[op++] = in1 >>> 1 & 7;
out[op++] = in1 >>> 4 & 7;
out[op++] = in1 >>> 7 & 7;
out[op++] = in1 >>> 10 & 7;
out[op++] = in1 >>> 13 & 7;
out[op++] = in1 >>> 16 & 7;
out[op++] = in1 >>> 19 & 7;
out[op++] = in1 >>> 22 & 7;
out[op++] = in1 >>> 25 & 7;
out[op++] = in1 >>> 28 & 7;
out[op++] = (in1 >>> 31 | (in2 & 3) << 1) & 7;
out[op++] = in2 >>> 2 & 7;
out[op++] = in2 >>> 5 & 7;
out[op++] = in2 >>> 8 & 7;
out[op++] = in2 >>> 11 & 7;
out[op++] = in2 >>> 14 & 7;
out[op++] = in2 >>> 17 & 7;
out[op++] = in2 >>> 20 & 7;
out[op++] = in2 >>> 23 & 7;
out[op++] = in2 >>> 26 & 7;
out[op] = in2 >>> 29 & 7;
}
function fastUnpack32_4(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
out[op++] = in0 >>> 0 & 15;
out[op++] = in0 >>> 4 & 15;
out[op++] = in0 >>> 8 & 15;
out[op++] = in0 >>> 12 & 15;
out[op++] = in0 >>> 16 & 15;
out[op++] = in0 >>> 20 & 15;
out[op++] = in0 >>> 24 & 15;
out[op++] = in0 >>> 28 & 15;
out[op++] = in1 >>> 0 & 15;
out[op++] = in1 >>> 4 & 15;
out[op++] = in1 >>> 8 & 15;
out[op++] = in1 >>> 12 & 15;
out[op++] = in1 >>> 16 & 15;
out[op++] = in1 >>> 20 & 15;
out[op++] = in1 >>> 24 & 15;
out[op++] = in1 >>> 28 & 15;
out[op++] = in2 >>> 0 & 15;
out[op++] = in2 >>> 4 & 15;
out[op++] = in2 >>> 8 & 15;
out[op++] = in2 >>> 12 & 15;
out[op++] = in2 >>> 16 & 15;
out[op++] = in2 >>> 20 & 15;
out[op++] = in2 >>> 24 & 15;
out[op++] = in2 >>> 28 & 15;
out[op++] = in3 >>> 0 & 15;
out[op++] = in3 >>> 4 & 15;
out[op++] = in3 >>> 8 & 15;
out[op++] = in3 >>> 12 & 15;
out[op++] = in3 >>> 16 & 15;
out[op++] = in3 >>> 20 & 15;
out[op++] = in3 >>> 24 & 15;
out[op] = in3 >>> 28 & 15;
}
function fastUnpack32_5(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
out[op++] = in0 >>> 0 & 31;
out[op++] = in0 >>> 5 & 31;
out[op++] = in0 >>> 10 & 31;
out[op++] = in0 >>> 15 & 31;
out[op++] = in0 >>> 20 & 31;
out[op++] = in0 >>> 25 & 31;
out[op++] = (in0 >>> 30 | (in1 & 7) << 2) & 31;
out[op++] = in1 >>> 3 & 31;
out[op++] = in1 >>> 8 & 31;
out[op++] = in1 >>> 13 & 31;
out[op++] = in1 >>> 18 & 31;
out[op++] = in1 >>> 23 & 31;
out[op++] = (in1 >>> 28 | (in2 & 1) << 4) & 31;
out[op++] = in2 >>> 1 & 31;
out[op++] = in2 >>> 6 & 31;
out[op++] = in2 >>> 11 & 31;
out[op++] = in2 >>> 16 & 31;
out[op++] = in2 >>> 21 & 31;
out[op++] = in2 >>> 26 & 31;
out[op++] = (in2 >>> 31 | (in3 & 15) << 1) & 31;
out[op++] = in3 >>> 4 & 31;
out[op++] = in3 >>> 9 & 31;
out[op++] = in3 >>> 14 & 31;
out[op++] = in3 >>> 19 & 31;
out[op++] = in3 >>> 24 & 31;
out[op++] = (in3 >>> 29 | (in4 & 3) << 3) & 31;
out[op++] = in4 >>> 2 & 31;
out[op++] = in4 >>> 7 & 31;
out[op++] = in4 >>> 12 & 31;
out[op++] = in4 >>> 17 & 31;
out[op++] = in4 >>> 22 & 31;
out[op] = in4 >>> 27 & 31;
}
function fastUnpack32_6(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
out[op++] = in0 >>> 0 & 63;
out[op++] = in0 >>> 6 & 63;
out[op++] = in0 >>> 12 & 63;
out[op++] = in0 >>> 18 & 63;
out[op++] = in0 >>> 24 & 63;
out[op++] = (in0 >>> 30 | (in1 & 15) << 2) & 63;
out[op++] = in1 >>> 4 & 63;
out[op++] = in1 >>> 10 & 63;
out[op++] = in1 >>> 16 & 63;
out[op++] = in1 >>> 22 & 63;
out[op++] = (in1 >>> 28 | (in2 & 3) << 4) & 63;
out[op++] = in2 >>> 2 & 63;
out[op++] = in2 >>> 8 & 63;
out[op++] = in2 >>> 14 & 63;
out[op++] = in2 >>> 20 & 63;
out[op++] = in2 >>> 26 & 63;
out[op++] = in3 >>> 0 & 63;
out[op++] = in3 >>> 6 & 63;
out[op++] = in3 >>> 12 & 63;
out[op++] = in3 >>> 18 & 63;
out[op++] = in3 >>> 24 & 63;
out[op++] = (in3 >>> 30 | (in4 & 15) << 2) & 63;
out[op++] = in4 >>> 4 & 63;
out[op++] = in4 >>> 10 & 63;
out[op++] = in4 >>> 16 & 63;
out[op++] = in4 >>> 22 & 63;
out[op++] = (in4 >>> 28 | (in5 & 3) << 4) & 63;
out[op++] = in5 >>> 2 & 63;
out[op++] = in5 >>> 8 & 63;
out[op++] = in5 >>> 14 & 63;
out[op++] = in5 >>> 20 & 63;
out[op] = in5 >>> 26 & 63;
}
function fastUnpack32_7(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
const in6 = inValues[inPos + 6] >>> 0;
out[op++] = in0 >>> 0 & 127;
out[op++] = in0 >>> 7 & 127;
out[op++] = in0 >>> 14 & 127;
out[op++] = in0 >>> 21 & 127;
out[op++] = (in0 >>> 28 | (in1 & 7) << 4) & 127;
out[op++] = in1 >>> 3 & 127;
out[op++] = in1 >>> 10 & 127;
out[op++] = in1 >>> 17 & 127;
out[op++] = in1 >>> 24 & 127;
out[op++] = (in1 >>> 31 | (in2 & 63) << 1) & 127;
out[op++] = in2 >>> 6 & 127;
out[op++] = in2 >>> 13 & 127;
out[op++] = in2 >>> 20 & 127;
out[op++] = (in2 >>> 27 | (in3 & 3) << 5) & 127;
out[op++] = in3 >>> 2 & 127;
out[op++] = in3 >>> 9 & 127;
out[op++] = in3 >>> 16 & 127;
out[op++] = in3 >>> 23 & 127;
out[op++] = (in3 >>> 30 | (in4 & 31) << 2) & 127;
out[op++] = in4 >>> 5 & 127;
out[op++] = in4 >>> 12 & 127;
out[op++] = in4 >>> 19 & 127;
out[op++] = (in4 >>> 26 | (in5 & 1) << 6) & 127;
out[op++] = in5 >>> 1 & 127;
out[op++] = in5 >>> 8 & 127;
out[op++] = in5 >>> 15 & 127;
out[op++] = in5 >>> 22 & 127;
out[op++] = (in5 >>> 29 | (in6 & 15) << 3) & 127;
out[op++] = in6 >>> 4 & 127;
out[op++] = in6 >>> 11 & 127;
out[op++] = in6 >>> 18 & 127;
out[op] = in6 >>> 25 & 127;
}
function fastUnpack32_8(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
const in6 = inValues[inPos + 6] >>> 0;
const in7 = inValues[inPos + 7] >>> 0;
out[op++] = in0 >>> 0 & 255;
out[op++] = in0 >>> 8 & 255;
out[op++] = in0 >>> 16 & 255;
out[op++] = in0 >>> 24 & 255;
out[op++] = in1 >>> 0 & 255;
out[op++] = in1 >>> 8 & 255;
out[op++] = in1 >>> 16 & 255;
out[op++] = in1 >>> 24 & 255;
out[op++] = in2 >>> 0 & 255;
out[op++] = in2 >>> 8 & 255;
out[op++] = in2 >>> 16 & 255;
out[op++] = in2 >>> 24 & 255;
out[op++] = in3 >>> 0 & 255;
out[op++] = in3 >>> 8 & 255;
out[op++] = in3 >>> 16 & 255;
out[op++] = in3 >>> 24 & 255;
out[op++] = in4 >>> 0 & 255;
out[op++] = in4 >>> 8 & 255;
out[op++] = in4 >>> 16 & 255;
out[op++] = in4 >>> 24 & 255;
out[op++] = in5 >>> 0 & 255;
out[op++] = in5 >>> 8 & 255;
out[op++] = in5 >>> 16 & 255;
out[op++] = in5 >>> 24 & 255;
out[op++] = in6 >>> 0 & 255;
out[op++] = in6 >>> 8 & 255;
out[op++] = in6 >>> 16 & 255;
out[op++] = in6 >>> 24 & 255;
out[op++] = in7 >>> 0 & 255;
out[op++] = in7 >>> 8 & 255;
out[op++] = in7 >>> 16 & 255;
out[op] = in7 >>> 24 & 255;
}
function fastUnpack32_9(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
const in6 = inValues[inPos + 6] >>> 0;
const in7 = inValues[inPos + 7] >>> 0;
const in8 = inValues[inPos + 8] >>> 0;
out[op++] = in0 >>> 0 & 511;
out[op++] = in0 >>> 9 & 511;
out[op++] = in0 >>> 18 & 511;
out[op++] = (in0 >>> 27 | (in1 & 15) << 5) & 511;
out[op++] = in1 >>> 4 & 511;
out[op++] = in1 >>> 13 & 511;
out[op++] = in1 >>> 22 & 511;
out[op++] = (in1 >>> 31 | (in2 & 255) << 1) & 511;
out[op++] = in2 >>> 8 & 511;
out[op++] = in2 >>> 17 & 511;
out[op++] = (in2 >>> 26 | (in3 & 7) << 6) & 511;
out[op++] = in3 >>> 3 & 511;
out[op++] = in3 >>> 12 & 511;
out[op++] = in3 >>> 21 & 511;
out[op++] = (in3 >>> 30 | (in4 & 127) << 2) & 511;
out[op++] = in4 >>> 7 & 511;
out[op++] = in4 >>> 16 & 511;
out[op++] = (in4 >>> 25 | (in5 & 3) << 7) & 511;
out[op++] = in5 >>> 2 & 511;
out[op++] = in5 >>> 11 & 511;
out[op++] = in5 >>> 20 & 511;
out[op++] = (in5 >>> 29 | (in6 & 63) << 3) & 511;
out[op++] = in6 >>> 6 & 511;
out[op++] = in6 >>> 15 & 511;
out[op++] = (in6 >>> 24 | (in7 & 1) << 8) & 511;
out[op++] = in7 >>> 1 & 511;
out[op++] = in7 >>> 10 & 511;
out[op++] = in7 >>> 19 & 511;
out[op++] = (in7 >>> 28 | (in8 & 31) << 4) & 511;
out[op++] = in8 >>> 5 & 511;
out[op++] = in8 >>> 14 & 511;
out[op] = in8 >>> 23 & 511;
}
function fastUnpack32_10(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
const in6 = inValues[inPos + 6] >>> 0;
const in7 = inValues[inPos + 7] >>> 0;
const in8 = inValues[inPos + 8] >>> 0;
const in9 = inValues[inPos + 9] >>> 0;
out[op++] = in0 >>> 0 & 1023;
out[op++] = in0 >>> 10 & 1023;
out[op++] = in0 >>> 20 & 1023;
out[op++] = (in0 >>> 30 | (in1 & 255) << 2) & 1023;
out[op++] = in1 >>> 8 & 1023;
out[op++] = in1 >>> 18 & 1023;
out[op++] = (in1 >>> 28 | (in2 & 63) << 4) & 1023;
out[op++] = in2 >>> 6 & 1023;
out[op++] = in2 >>> 16 & 1023;
out[op++] = (in2 >>> 26 | (in3 & 15) << 6) & 1023;
out[op++] = in3 >>> 4 & 1023;
out[op++] = in3 >>> 14 & 1023;
out[op++] = (in3 >>> 24 | (in4 & 3) << 8) & 1023;
out[op++] = in4 >>> 2 & 1023;
out[op++] = in4 >>> 12 & 1023;
out[op++] = in4 >>> 22 & 1023;
out[op++] = in5 >>> 0 & 1023;
out[op++] = in5 >>> 10 & 1023;
out[op++] = in5 >>> 20 & 1023;
out[op++] = (in5 >>> 30 | (in6 & 255) << 2) & 1023;
out[op++] = in6 >>> 8 & 1023;
out[op++] = in6 >>> 18 & 1023;
out[op++] = (in6 >>> 28 | (in7 & 63) << 4) & 1023;
out[op++] = in7 >>> 6 & 1023;
out[op++] = in7 >>> 16 & 1023;
out[op++] = (in7 >>> 26 | (in8 & 15) << 6) & 1023;
out[op++] = in8 >>> 4 & 1023;
out[op++] = in8 >>> 14 & 1023;
out[op++] = (in8 >>> 24 | (in9 & 3) << 8) & 1023;
out[op++] = in9 >>> 2 & 1023;
out[op++] = in9 >>> 12 & 1023;
out[op] = in9 >>> 22 & 1023;
}
function fastUnpack32_11(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
const in6 = inValues[inPos + 6] >>> 0;
const in7 = inValues[inPos + 7] >>> 0;
const in8 = inValues[inPos + 8] >>> 0;
const in9 = inValues[inPos + 9] >>> 0;
const in10 = inValues[inPos + 10] >>> 0;
out[op++] = in0 >>> 0 & 2047;
out[op++] = in0 >>> 11 & 2047;
out[op++] = (in0 >>> 22 | (in1 & 1) << 10) & 2047;
out[op++] = in1 >>> 1 & 2047;
out[op++] = in1 >>> 12 & 2047;
out[op++] = (in1 >>> 23 | (in2 & 3) << 9) & 2047;
out[op++] = in2 >>> 2 & 2047;
out[op++] = in2 >>> 13 & 2047;
out[op++] = (in2 >>> 24 | (in3 & 7) << 8) & 2047;
out[op++] = in3 >>> 3 & 2047;
out[op++] = in3 >>> 14 & 2047;
out[op++] = (in3 >>> 25 | (in4 & 15) << 7) & 2047;
out[op++] = in4 >>> 4 & 2047;
out[op++] = in4 >>> 15 & 2047;
out[op++] = (in4 >>> 26 | (in5 & 31) << 6) & 2047;
out[op++] = in5 >>> 5 & 2047;
out[op++] = in5 >>> 16 & 2047;
out[op++] = (in5 >>> 27 | (in6 & 63) << 5) & 2047;
out[op++] = in6 >>> 6 & 2047;
out[op++] = in6 >>> 17 & 2047;
out[op++] = (in6 >>> 28 | (in7 & 127) << 4) & 2047;
out[op++] = in7 >>> 7 & 2047;
out[op++] = in7 >>> 18 & 2047;
out[op++] = (in7 >>> 29 | (in8 & 255) << 3) & 2047;
out[op++] = in8 >>> 8 & 2047;
out[op++] = in8 >>> 19 & 2047;
out[op++] = (in8 >>> 30 | (in9 & 511) << 2) & 2047;
out[op++] = in9 >>> 9 & 2047;
out[op++] = in9 >>> 20 & 2047;
out[op++] = (in9 >>> 31 | (in10 & 1023) << 1) & 2047;
out[op++] = in10 >>> 10 & 2047;
out[op] = in10 >>> 21 & 2047;
}
function fastUnpack32_12(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
const in6 = inValues[inPos + 6] >>> 0;
const in7 = inValues[inPos + 7] >>> 0;
const in8 = inValues[inPos + 8] >>> 0;
const in9 = inValues[inPos + 9] >>> 0;
const in10 = inValues[inPos + 10] >>> 0;
const in11 = inValues[inPos + 11] >>> 0;
out[op++] = in0 >>> 0 & 4095;
out[op++] = in0 >>> 12 & 4095;
out[op++] = (in0 >>> 24 | (in1 & 15) << 8) & 4095;
out[op++] = in1 >>> 4 & 4095;
out[op++] = in1 >>> 16 & 4095;
out[op++] = (in1 >>> 28 | (in2 & 255) << 4) & 4095;
out[op++] = in2 >>> 8 & 4095;
out[op++] = in2 >>> 20 & 4095;
out[op++] = in3 >>> 0 & 4095;
out[op++] = in3 >>> 12 & 4095;
out[op++] = (in3 >>> 24 | (in4 & 15) << 8) & 4095;
out[op++] = in4 >>> 4 & 4095;
out[op++] = in4 >>> 16 & 4095;
out[op++] = (in4 >>> 28 | (in5 & 255) << 4) & 4095;
out[op++] = in5 >>> 8 & 4095;
out[op++] = in5 >>> 20 & 4095;
out[op++] = in6 >>> 0 & 4095;
out[op++] = in6 >>> 12 & 4095;
out[op++] = (in6 >>> 24 | (in7 & 15) << 8) & 4095;
out[op++] = in7 >>> 4 & 4095;
out[op++] = in7 >>> 16 & 4095;
out[op++] = (in7 >>> 28 | (in8 & 255) << 4) & 4095;
out[op++] = in8 >>> 8 & 4095;
out[op++] = in8 >>> 20 & 4095;
out[op++] = in9 >>> 0 & 4095;
out[op++] = in9 >>> 12 & 4095;
out[op++] = (in9 >>> 24 | (in10 & 15) << 8) & 4095;
out[op++] = in10 >>> 4 & 4095;
out[op++] = in10 >>> 16 & 4095;
out[op++] = (in10 >>> 28 | (in11 & 255) << 4) & 4095;
out[op++] = in11 >>> 8 & 4095;
out[op] = in11 >>> 20 & 4095;
}
function fastUnpack32_16(inValues, inPos, out, outPos) {
let op = outPos;
const in0 = inValues[inPos] >>> 0;
const in1 = inValues[inPos + 1] >>> 0;
const in2 = inValues[inPos + 2] >>> 0;
const in3 = inValues[inPos + 3] >>> 0;
const in4 = inValues[inPos + 4] >>> 0;
const in5 = inValues[inPos + 5] >>> 0;
const in6 = inValues[inPos + 6] >>> 0;
const in7 = inValues[inPos + 7] >>> 0;
const in8 = inValues[inPos + 8] >>> 0;
const in9 = inValues[inPos + 9] >>> 0;
const in10 = inValues[inPos + 10] >>> 0;
const in11 = inValues[inPos + 11] >>> 0;
const in12 = inValues[inPos + 12] >>> 0;
const in13 = inValues[inPos + 13] >>> 0;
const in14 = inValues[inPos + 14] >>> 0;
const in15 = inValues[inPos + 15] >>> 0;
out[op++] = in0 >>> 0 & 65535;
out[op++] = in0 >>> 16 & 65535;
out[op++] = in1 >>> 0 & 65535;
out[op++] = in1 >>> 16 & 65535;
out[op++] = in2 >>> 0 & 65535;
out[op++] = in2 >>> 16 & 65535;
out[op++] = in3 >>> 0 & 65535;
out[op++] = in3 >>> 16 & 65535;
out[op++] = in4 >>> 0 & 65535;
out[op++] = in4 >>> 16 & 65535;
out[op++] = in5 >>> 0 & 65535;
out[op++] = in5 >>> 16 & 65535;
out[op++] = in6 >>> 0 & 65535;
out[op++] = in6 >>> 16 & 65535;
out[op++] = in7 >>> 0 & 65535;
out[op++] = in7 >>> 16 & 65535;
out[op++] = in8 >>> 0 & 65535;
out[op++] = in8 >>> 16 & 65535;
out[op++] = in9 >>> 0 & 65535;
out[op++] = in9 >>> 16 & 65535;
out[op++] = in10 >>> 0 & 65535;
out[op++] = in10 >>> 16 & 65535;
out[op++] = in11 >>> 0 & 65535;
out[op++] = in11 >>> 16 & 65535;
out[op++] = in12 >>> 0 & 65535;
out[op++] = in12 >>> 16 & 65535;
out[op++] = in13 >>> 0 & 65535;
out[op++] = in13 >>> 16 & 65535;
out[op++] = in14 >>> 0 & 65535;
out[op++] = in14 >>> 16 & 65535;
out[op++] = in15 >>> 0 & 65535;
out[op] = in15 >>> 16 & 65535;
}
function fastUnpack256_1(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 1;
out[op++] = in0 >>> 1 & 1;
out[op++] = in0 >>> 2 & 1;
out[op++] = in0 >>> 3 & 1;
out[op++] = in0 >>> 4 & 1;
out[op++] = in0 >>> 5 & 1;
out[op++] = in0 >>> 6 & 1;
out[op++] = in0 >>> 7 & 1;
out[op++] = in0 >>> 8 & 1;
out[op++] = in0 >>> 9 & 1;
out[op++] = in0 >>> 10 & 1;
out[op++] = in0 >>> 11 & 1;
out[op++] = in0 >>> 12 & 1;
out[op++] = in0 >>> 13 & 1;
out[op++] = in0 >>> 14 & 1;
out[op++] = in0 >>> 15 & 1;
out[op++] = in0 >>> 16 & 1;
out[op++] = in0 >>> 17 & 1;
out[op++] = in0 >>> 18 & 1;
out[op++] = in0 >>> 19 & 1;
out[op++] = in0 >>> 20 & 1;
out[op++] = in0 >>> 21 & 1;
out[op++] = in0 >>> 22 & 1;
out[op++] = in0 >>> 23 & 1;
out[op++] = in0 >>> 24 & 1;
out[op++] = in0 >>> 25 & 1;
out[op++] = in0 >>> 26 & 1;
out[op++] = in0 >>> 27 & 1;
out[op++] = in0 >>> 28 & 1;
out[op++] = in0 >>> 29 & 1;
out[op++] = in0 >>> 30 & 1;
out[op++] = in0 >>> 31 & 1;
}
}
function fastUnpack256_2(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
const in1 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 3;
out[op++] = in0 >>> 2 & 3;
out[op++] = in0 >>> 4 & 3;
out[op++] = in0 >>> 6 & 3;
out[op++] = in0 >>> 8 & 3;
out[op++] = in0 >>> 10 & 3;
out[op++] = in0 >>> 12 & 3;
out[op++] = in0 >>> 14 & 3;
out[op++] = in0 >>> 16 & 3;
out[op++] = in0 >>> 18 & 3;
out[op++] = in0 >>> 20 & 3;
out[op++] = in0 >>> 22 & 3;
out[op++] = in0 >>> 24 & 3;
out[op++] = in0 >>> 26 & 3;
out[op++] = in0 >>> 28 & 3;
out[op++] = in0 >>> 30 & 3;
out[op++] = in1 >>> 0 & 3;
out[op++] = in1 >>> 2 & 3;
out[op++] = in1 >>> 4 & 3;
out[op++] = in1 >>> 6 & 3;
out[op++] = in1 >>> 8 & 3;
out[op++] = in1 >>> 10 & 3;
out[op++] = in1 >>> 12 & 3;
out[op++] = in1 >>> 14 & 3;
out[op++] = in1 >>> 16 & 3;
out[op++] = in1 >>> 18 & 3;
out[op++] = in1 >>> 20 & 3;
out[op++] = in1 >>> 22 & 3;
out[op++] = in1 >>> 24 & 3;
out[op++] = in1 >>> 26 & 3;
out[op++] = in1 >>> 28 & 3;
out[op++] = in1 >>> 30 & 3;
}
}
function fastUnpack256_3(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
const in1 = inValues[ip++] >>> 0;
const in2 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 7;
out[op++] = in0 >>> 3 & 7;
out[op++] = in0 >>> 6 & 7;
out[op++] = in0 >>> 9 & 7;
out[op++] = in0 >>> 12 & 7;
out[op++] = in0 >>> 15 & 7;
out[op++] = in0 >>> 18 & 7;
out[op++] = in0 >>> 21 & 7;
out[op++] = in0 >>> 24 & 7;
out[op++] = in0 >>> 27 & 7;
out[op++] = (in0 >>> 30 | (in1 & 1) << 2) & 7;
out[op++] = in1 >>> 1 & 7;
out[op++] = in1 >>> 4 & 7;
out[op++] = in1 >>> 7 & 7;
out[op++] = in1 >>> 10 & 7;
out[op++] = in1 >>> 13 & 7;
out[op++] = in1 >>> 16 & 7;
out[op++] = in1 >>> 19 & 7;
out[op++] = in1 >>> 22 & 7;
out[op++] = in1 >>> 25 & 7;
out[op++] = in1 >>> 28 & 7;
out[op++] = (in1 >>> 31 | (in2 & 3) << 1) & 7;
out[op++] = in2 >>> 2 & 7;
out[op++] = in2 >>> 5 & 7;
out[op++] = in2 >>> 8 & 7;
out[op++] = in2 >>> 11 & 7;
out[op++] = in2 >>> 14 & 7;
out[op++] = in2 >>> 17 & 7;
out[op++] = in2 >>> 20 & 7;
out[op++] = in2 >>> 23 & 7;
out[op++] = in2 >>> 26 & 7;
out[op++] = in2 >>> 29 & 7;
}
}
function fastUnpack256_4(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
const in1 = inValues[ip++] >>> 0;
const in2 = inValues[ip++] >>> 0;
const in3 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 15;
out[op++] = in0 >>> 4 & 15;
out[op++] = in0 >>> 8 & 15;
out[op++] = in0 >>> 12 & 15;
out[op++] = in0 >>> 16 & 15;
out[op++] = in0 >>> 20 & 15;
out[op++] = in0 >>> 24 & 15;
out[op++] = in0 >>> 28 & 15;
out[op++] = in1 >>> 0 & 15;
out[op++] = in1 >>> 4 & 15;
out[op++] = in1 >>> 8 & 15;
out[op++] = in1 >>> 12 & 15;
out[op++] = in1 >>> 16 & 15;
out[op++] = in1 >>> 20 & 15;
out[op++] = in1 >>> 24 & 15;
out[op++] = in1 >>> 28 & 15;
out[op++] = in2 >>> 0 & 15;
out[op++] = in2 >>> 4 & 15;
out[op++] = in2 >>> 8 & 15;
out[op++] = in2 >>> 12 & 15;
out[op++] = in2 >>> 16 & 15;
out[op++] = in2 >>> 20 & 15;
out[op++] = in2 >>> 24 & 15;
out[op++] = in2 >>> 28 & 15;
out[op++] = in3 >>> 0 & 15;
out[op++] = in3 >>> 4 & 15;
out[op++] = in3 >>> 8 & 15;
out[op++] = in3 >>> 12 & 15;
out[op++] = in3 >>> 16 & 15;
out[op++] = in3 >>> 20 & 15;
out[op++] = in3 >>> 24 & 15;
out[op++] = in3 >>> 28 & 15;
}
}
function fastUnpack256_5(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
const in1 = inValues[ip++] >>> 0;
const in2 = inValues[ip++] >>> 0;
const in3 = inValues[ip++] >>> 0;
const in4 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 31;
out[op++] = in0 >>> 5 & 31;
out[op++] = in0 >>> 10 & 31;
out[op++] = in0 >>> 15 & 31;
out[op++] = in0 >>> 20 & 31;
out[op++] = in0 >>> 25 & 31;
out[op++] = (in0 >>> 30 | (in1 & 7) << 2) & 31;
out[op++] = in1 >>> 3 & 31;
out[op++] = in1 >>> 8 & 31;
out[op++] = in1 >>> 13 & 31;
out[op++] = in1 >>> 18 & 31;
out[op++] = in1 >>> 23 & 31;
out[op++] = (in1 >>> 28 | (in2 & 1) << 4) & 31;
out[op++] = in2 >>> 1 & 31;
out[op++] = in2 >>> 6 & 31;
out[op++] = in2 >>> 11 & 31;
out[op++] = in2 >>> 16 & 31;
out[op++] = in2 >>> 21 & 31;
out[op++] = in2 >>> 26 & 31;
out[op++] = (in2 >>> 31 | (in3 & 15) << 1) & 31;
out[op++] = in3 >>> 4 & 31;
out[op++] = in3 >>> 9 & 31;
out[op++] = in3 >>> 14 & 31;
out[op++] = in3 >>> 19 & 31;
out[op++] = in3 >>> 24 & 31;
out[op++] = (in3 >>> 29 | (in4 & 3) << 3) & 31;
out[op++] = in4 >>> 2 & 31;
out[op++] = in4 >>> 7 & 31;
out[op++] = in4 >>> 12 & 31;
out[op++] = in4 >>> 17 & 31;
out[op++] = in4 >>> 22 & 31;
out[op++] = in4 >>> 27 & 31;
}
}
function fastUnpack256_6(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
const in1 = inValues[ip++] >>> 0;
const in2 = inValues[ip++] >>> 0;
const in3 = inValues[ip++] >>> 0;
const in4 = inValues[ip++] >>> 0;
const in5 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 63;
out[op++] = in0 >>> 6 & 63;
out[op++] = in0 >>> 12 & 63;
out[op++] = in0 >>> 18 & 63;
out[op++] = in0 >>> 24 & 63;
out[op++] = (in0 >>> 30 | (in1 & 15) << 2) & 63;
out[op++] = in1 >>> 4 & 63;
out[op++] = in1 >>> 10 & 63;
out[op++] = in1 >>> 16 & 63;
out[op++] = in1 >>> 22 & 63;
out[op++] = (in1 >>> 28 | (in2 & 3) << 4) & 63;
out[op++] = in2 >>> 2 & 63;
out[op++] = in2 >>> 8 & 63;
out[op++] = in2 >>> 14 & 63;
out[op++] = in2 >>> 20 & 63;
out[op++] = in2 >>> 26 & 63;
out[op++] = in3 >>> 0 & 63;
out[op++] = in3 >>> 6 & 63;
out[op++] = in3 >>> 12 & 63;
out[op++] = in3 >>> 18 & 63;
out[op++] = in3 >>> 24 & 63;
out[op++] = (in3 >>> 30 | (in4 & 15) << 2) & 63;
out[op++] = in4 >>> 4 & 63;
out[op++] = in4 >>> 10 & 63;
out[op++] = in4 >>> 16 & 63;
out[op++] = in4 >>> 22 & 63;
out[op++] = (in4 >>> 28 | (in5 & 3) << 4) & 63;
out[op++] = in5 >>> 2 & 63;
out[op++] = in5 >>> 8 & 63;
out[op++] = in5 >>> 14 & 63;
out[op++] = in5 >>> 20 & 63;
out[op++] = in5 >>> 26 & 63;
}
}
function fastUnpack256_7(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
const in1 = inValues[ip++] >>> 0;
const in2 = inValues[ip++] >>> 0;
const in3 = inValues[ip++] >>> 0;
const in4 = inValues[ip++] >>> 0;
const in5 = inValues[ip++] >>> 0;
const in6 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 127;
out[op++] = in0 >>> 7 & 127;
out[op++] = in0 >>> 14 & 127;
out[op++] = in0 >>> 21 & 127;
out[op++] = (in0 >>> 28 | (in1 & 7) << 4) & 127;
out[op++] = in1 >>> 3 & 127;
out[op++] = in1 >>> 10 & 127;
out[op++] = in1 >>> 17 & 127;
out[op++] = in1 >>> 24 & 127;
out[op++] = (in1 >>> 31 | (in2 & 63) << 1) & 127;
out[op++] = in2 >>> 6 & 127;
out[op++] = in2 >>> 13 & 127;
out[op++] = in2 >>> 20 & 127;
out[op++] = (in2 >>> 27 | (in3 & 3) << 5) & 127;
out[op++] = in3 >>> 2 & 127;
out[op++] = in3 >>> 9 & 127;
out[op++] = in3 >>> 16 & 127;
out[op++] = in3 >>> 23 & 127;
out[op++] = (in3 >>> 30 | (in4 & 31) << 2) & 127;
out[op++] = in4 >>> 5 & 127;
out[op++] = in4 >>> 12 & 127;
out[op++] = in4 >>> 19 & 127;
out[op++] = (in4 >>> 26 | (in5 & 1) << 6) & 127;
out[op++] = in5 >>> 1 & 127;
out[op++] = in5 >>> 8 & 127;
out[op++] = in5 >>> 15 & 127;
out[op++] = in5 >>> 22 & 127;
out[op++] = (in5 >>> 29 | (in6 & 15) << 3) & 127;
out[op++] = in6 >>> 4 & 127;
out[op++] = in6 >>> 11 & 127;
out[op++] = in6 >>> 18 & 127;
out[op++] = in6 >>> 25 & 127;
}
}
function fastUnpack256_8(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let c = 0; c < 8; c++) {
const in0 = inValues[ip++] >>> 0;
const in1 = inValues[ip++] >>> 0;
const in2 = inValues[ip++] >>> 0;
const in3 = inValues[ip++] >>> 0;
const in4 = inValues[ip++] >>> 0;
const in5 = inValues[ip++] >>> 0;
const in6 = inValues[ip++] >>> 0;
const in7 = inValues[ip++] >>> 0;
out[op++] = in0 >>> 0 & 255;
out[op++] = in0 >>> 8 & 255;
out[op++] = in0 >>> 16 & 255;
out[op++] = in0 >>> 24 & 255;
out[op++] = in1 >>> 0 & 255;
out[op++] = in1 >>> 8 & 255;
out[op++] = in1 >>> 16 & 255;
out[op++] = in1 >>> 24 & 255;
out[op++] = in2 >>> 0 & 255;
out[op++] = in2 >>> 8 & 255;
out[op++] = in2 >>> 16 & 255;
out[op++] = in2 >>> 24 & 255;
out[op++] = in3 >>> 0 & 255;
out[op++] = in3 >>> 8 & 255;
out[op++] = in3 >>> 16 & 255;
out[op++] = in3 >>> 24 & 255;
out[op++] = in4 >>> 0 & 255;
out[op++] = in4 >>> 8 & 255;
out[op++] = in4 >>> 16 & 255;
out[op++] = in4 >>> 24 & 255;
out[op++] = in5 >>> 0 & 255;
out[op++] = in5 >>> 8 & 255;
out[op++] = in5 >>> 16 & 255;
out[op++] = in5 >>> 24 & 255;
out[op++] = in6 >>> 0 & 255;
out[op++] = in6 >>> 8 & 255;
out[op++] = in6 >>> 16 & 255;
out[op++] = in6 >>> 24 & 255;
out[op++] = in7 >>> 0 & 255;
out[op++] = in7 >>> 8 & 255;
out[op++] = in7 >>> 16 & 255;
out[op++] = in7 >>> 24 & 255;
}
}
function fastUnpack256_16(inValues, inPos, out, outPos) {
let op = outPos;
let ip = inPos;
for (let i = 0; i < 128; i++) {
const in0 = inValues[ip++] >>> 0;
out[op++] = in0 & 65535;
out[op++] = in0 >>> 16 & 65535;
}
}
function fastUnpack256_Generic(inValues, inPos, out, outPos, bitWidth) {
const mask = MASKS[bitWidth] >>> 0;
let inputWordIndex = inPos;
let bitOffset = 0;
let currentWord = inValues[inputWordIndex] >>> 0;
let op = outPos;
for (let c = 0; c < 8; c++) {
for (let i = 0; i < 32; i++) if (bitOffset + bitWidth <= 32) {
const value = currentWord >>> bitOffset & mask;
out[op + i] = value | 0;
bitOffset += bitWidth;
if (bitOffset === 32) {
bitOffset = 0;
inputWordIndex++;
if (i !== 31) currentWord = inValues[inputWordIndex] >>> 0;
}
} else {
const lowBits = 32 - bitOffset;
const low = currentWord >>> bitOffset;
inputWordIndex++;
currentWord = inValues[inputWordIndex] >>> 0;
const highBits = bitWidth - lowBits;
const highMask = -1 >>> 32 - highBits >>> 0;
const value = (low | (currentWord & highMask) << lowBits) & mask;
out[op + i] = value | 0;
bitOffset = highBits;
}
op += 32;
bitOffset = 0;
if (c < 7) currentWord = inValues[inputWordIndex] >>> 0;
}
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/fastPforDecoder.js
const MAX_BIT_WIDTH = 32;
const BIT_WIDTH_SLOTS = 33;
const PAGE_SIZE = normalizePageSize(DEFAULT_PAGE_SIZE);
const BYTE_CONTAINER_SIZE = 3 * PAGE_SIZE / 256 + PAGE_SIZE | 0;
/**
* Creates an isolated workspace for decoding.
* Reusing a workspace across calls avoids repeated allocations.
*/
function createDecoderWorkspace() {
const byteContainer = new Uint8Array(BYTE_CONTAINER_SIZE);
return {
dataToBePacked: new Array(BIT_WIDTH_SLOTS),
dataPointers: new Int32Array(BIT_WIDTH_SLOTS),
byteContainer,
byteContainerI32: new Int32Array(byteContainer.buffer, byteContainer.byteOffset, byteContainer.byteLength >>> 2),
exceptionSizes: new Int32Array(BIT_WIDTH_SLOTS)
};
}
function createFastPforWireDecodeWorkspace(initialEncodedWordCapacity = 16) {
if (initialEncodedWordCapacity < 0) throw new RangeError(`initialEncodedWordCapacity must be >= 0, got ${initialEncodedWordCapacity}`);
const capacity = Math.max(16, initialEncodedWordCapacity | 0);
return {
encodedWords: new Uint32Array(capacity),
decoderWorkspace: createDecoderWorkspace()
};
}
function ensureFastPforWireEncodedWordsCapacity(workspace, requiredWordCount) {
if (requiredWordCount <= workspace.encodedWords.length) return workspace.encodedWords;
const next = new Uint32Array(Math.max(16, requiredWordCount * 2));
workspace.encodedWords = next;
return next;
}
function materializeByteContainer(inValues, byteContainerStart, byteSize, workspace) {
if (workspace.byteContainer.length < byteSize) {
workspace.byteContainer = new Uint8Array(byteSize * 2);
workspace.byteContainerI32 = void 0;
}
const byteContainer = workspace.byteContainer;
const numFullInts = byteSize >>> 2;
if ((byteContainer.byteOffset & 3) === 0) {
let intView = workspace.byteContainerI32;
if (!intView || intView.buffer !== byteContainer.buffer || intView.byteOffset !== byteContainer.byteOffset || intView.length < numFullInts) intView = workspace.byteContainerI32 = new Int32Array(byteContainer.buffer, byteContainer.byteOffset, byteContainer.byteLength >>> 2);
intView.set(inValues.subarray(byteContainerStart, byteContainerStart + numFullInts));
} else for (let i = 0; i < numFullInts; i = i + 1 | 0) {
const val = inValues[byteContainerStart + i | 0] | 0;
const base = i << 2;
byteContainer[base] = val & 255;
byteContainer[base + 1 | 0] = val >>> 8 & 255;
byteContainer[base + 2 | 0] = val >>> 16 & 255;
byteContainer[base + 3 | 0] = val >>> 24 & 255;
}
const remainder = byteSize & 3;
if (remainder > 0) {
const lastVal = inValues[byteContainerStart + numFullInts | 0] | 0;
const base = numFullInts << 2;
for (let r = 0; r < remainder; r = r + 1 | 0) byteContainer[base + r | 0] = lastVal >>> (r << 3) & 255;
}
return byteContainer;
}
/**
* Unpacks the per-bitWidth "exception streams" described by the page's bitmap.
*
* @remarks
* For each bit-width present in the bitmap, a stream header gives the count of outlier values for that
* bit-width, followed by packed bits representing those values.
*
* @param inValues - Packed input (32-bit words).
* @param inExcept - Offset (32-bit word index) where the exception bitmap starts.
* @param workspace - Decoder workspace used to store the unpacked exception streams.
* @returns The new input offset (32-bit word index) after consuming all exception streams.
*/
function unpackExceptionStreams(inValues, inExcept, workspace) {
const bitmap = inValues[inExcept++] | 0;
const dataToBePacked = workspace.dataToBePacked;
for (let bitWidth = 2; bitWidth <= MAX_BIT_WIDTH; bitWidth = bitWidth + 1 | 0) {
if ((bitmap >>> bitWidth - 1 & 1) === 0) continue;
if (inExcept >= inValues.length) throw new Error(`FastPFOR decode: truncated exception stream header (bitWidth=${bitWidth}, streamWordIndex=${inExcept}, needWords=1, availableWords=${inValues.length - inExcept}, encodedWords=${inValues.length})`);
const size = inValues[inExcept++] >>> 0;
const roundedUp = roundUpToMultipleOf32(size);
const wordsNeeded = size * bitWidth + 31 >>> 5;
if (inExcept + wordsNeeded > inValues.length) throw new Error(`FastPFOR decode: truncated exception stream (bitWidth=${bitWidth}, size=${size}, streamWordIndex=${inExcept}, needWords=${wordsNeeded}, availableWords=${inValues.length - inExcept}, encodedWords=${inValues.length})`);
let exceptionStream = dataToBePacked[bitWidth];
if (!exceptionStream || exceptionStream.length < roundedUp) exceptionStream = dataToBePacked[bitWidth] = new Uint32Array(roundedUp);
let j = 0;
for (; j < size; j = j + 32 | 0) {
fastUnpack32(inValues, inExcept, exceptionStream, j, bitWidth);
inExcept = inExcept + bitWidth | 0;
}
const overflow = j - size | 0;
inExcept = inExcept - (overflow * bitWidth >>> 5) | 0;
workspace.exceptionSizes[bitWidth] = size;
}
return inExcept;
}
/**
* Unpacks one 256-value block from the packed bitstream using a specialized implementation for common widths.
*
* @param inValues - Packed input (32-bit words).
* @param inPos - Input offset (32-bit word index) where the packed block starts.
* @param out - Output buffer.
* @param outPos - Output offset where the 256 values will be written.
* @param bitWidth - Base bit-width used for this block.
* @returns The new input offset (32-bit word index) right after the packed block data.
*/
function unpackBlock256(inValues, inPos, out, outPos, bitWidth) {
switch (bitWidth) {
case 1:
fastUnpack256_1(inValues, inPos, out, outPos);
break;
case 2:
fastUnpack256_2(inValues, inPos, out, outPos);
break;
case 3:
fastUnpack256_3(inValues, inPos, out, outPos);
break;
case 4:
fastUnpack256_4(inValues, inPos, out, outPos);
break;
case 5:
fastUnpack256_5(inValues, inPos, out, outPos);
break;
case 6:
fastUnpack256_6(inValues, inPos, out, outPos);
break;
case 7:
fastUnpack256_7(inValues, inPos, out, outPos);
break;
case 8:
fastUnpack256_8(inValues, inPos, out, outPos);
break;
case 16:
fastUnpack256_16(inValues, inPos, out, outPos);
break;
default: fastUnpack256_Generic(inValues, inPos, out, outPos, bitWidth);
}
return inPos + (bitWidth << 3) | 0;
}
/**
* Reads and validates the 2-byte block header from the byteContainer.
*
* @remarks
* The header is `[bitWidth, exceptionCount]`, both stored as single bytes.
*
* @param byteContainer - Byte metadata buffer for the page.
* @param byteContainerLen - The valid byte length in `byteContainer` for this page.
* @param bytePosIn - Current offset in `byteContainer`.
* @param block - Block index within the page (for error messages).
* @returns The parsed header and the updated `bytePosIn`.
*/
function readBlockHeader(byteContainer, byteContainerLen, bytePosIn, block) {
if (bytePosIn + 2 > byteContainerLen) throw new Error(`FastPFOR decode: byteContainer underflow at block=${block} (need 2 bytes for [bitWidth, exceptionCount], bytePos=${bytePosIn}, byteSize=${byteContainerLen})`);
const bitWidth = byteContainer[bytePosIn++];
const exceptionCount = byteContainer[bytePosIn++];
if (bitWidth > MAX_BIT_WIDTH) throw new Error(`FastPFOR decode: invalid bitWidth=${bitWidth} at block=${block} (expected 0..${MAX_BIT_WIDTH}). This likely indicates corrupted or truncated input.`);
return {
bitWidth,
exceptionCount,
bytePosIn
};
}
/**
* Reads and validates the exception header for a block.
*
* @remarks
* The header contains `maxBits` (1 byte), which defines the width of the outlier values as
* `exceptionBitWidth = maxBits - bitWidth`.
*
* @param byteContainer - Byte metadata buffer for the page.
* @param byteContainerLen - The valid byte length in `byteContainer` for this page.
* @param bytePosIn - Current offset in `byteContainer`.
* @param bitWidth - Base bit-width for the block.
* @param exceptionCount - Number of exceptions/outliers in this block.
* @param block - Block index within the page (for error messages).
* @returns Parsed `maxBits`, `exceptionBitWidth`, and the updated `bytePosIn`.
*/
function readBlockExceptionHeader(byteContainer, byteContainerLen, bytePosIn, bitWidth, exceptionCount, block) {
if (bytePosIn + 1 > byteContainerLen) throw new Error(`FastPFOR decode: exception header underflow at block=${block} (need 1 byte for maxBits, bytePos=${bytePosIn}, byteSize=${byteContainerLen})`);
const maxBits = byteContainer[bytePosIn++];
if (maxBits < bitWidth || maxBits > MAX_BIT_WIDTH) throw new Error(`FastPFOR decode: invalid maxBits=${maxBits} at block=${block} (bitWidth=${bitWidth}, expected ${bitWidth}..${MAX_BIT_WIDTH})`);
const exceptionBitWidth = maxBits - bitWidth | 0;
if (exceptionBitWidth < 1 || exceptionBitWidth > MAX_BIT_WIDTH) throw new Error(`FastPFOR decode: invalid exceptionBitWidth=${exceptionBitWidth} at block=${block} (bitWidth=${bitWidth}, maxBits=${maxBits})`);
if (bytePosIn + exceptionCount > byteContainerLen) throw new Error(`FastPFOR decode: exception positions underflow at block=${block} (need=${exceptionCount}, have=${byteContainerLen - bytePosIn})`);
return {
maxBits,
exceptionBitWidth,
bytePosIn
};
}
/**
* Applies (block-local) FastPFOR "exceptions" (outliers) to an already-unpacked base 256-value block.
*
* @param out - Output buffer containing the base unpacked values for the block.
* @param blockOutPos - Offset in `out` where the 256-value block starts.
* @param bitWidth - Base bit-width for the block.
* @param exceptionCount - Number of exceptions/outliers in this block.
* @param byteContainer - Byte metadata buffer for the page.
* @param byteContainerLen - The valid byte length in `byteContainer` for this page.
* @param bytePosIn - Current offset in `byteContainer` (right after `[bitWidth, exceptionCount]`).
* @param workspace - Decoder workspace holding the unpacked exception streams.
* @param block - Block index within the page (for error messages).
* @returns The updated `bytePosIn` after consuming the exception metadata bytes.
*
* The exception metadata is stored in `byteContainer`:
* - `maxBits` (1 byte): the maximum bit-width of any value in the block
* - `exceptionCount` exception positions (1 byte each, 0..255)
*
* The exception values themselves are read from the pre-unpacked exception streams stored in `workspace`.
* Returns the new position in the byteContainer after consuming the exception metadata bytes.
*/
function applyBlockExceptions(out, blockOutPos, bitWidth, exceptionCount, byteContainer, byteContainerLen, bytePosIn, workspace, block) {
const { maxBits, exceptionBitWidth, bytePosIn: afterHeaderPos } = readBlockExceptionHeader(byteContainer, byteContainerLen, bytePosIn, bitWidth, exceptionCount, block);
bytePosIn = afterHeaderPos;
if (exceptionBitWidth === 1) {
const shift = 1 << bitWidth;
for (let k = 0; k < exceptionCount; k = k + 1 | 0) {
const pos = byteContainer[bytePosIn++];
out[pos + blockOutPos | 0] |= shift;
}
return bytePosIn;
}
const exceptionValues = workspace.dataToBePacked[exceptionBitWidth];
if (!exceptionValues) throw new Error(`FastPFOR decode: missing exception stream for exceptionBitWidth=${exceptionBitWidth} (bitWidth=${bitWidth}, maxBits=${maxBits}) at block ${block}`);
const exceptionPointers = workspace.dataPointers;
let exPtr = exceptionPointers[exceptionBitWidth] | 0;
const exSize = workspace.exceptionSizes[exceptionBitWidth] | 0;
if (exPtr + exceptionCount > exSize) throw new Error(`FastPFOR decode: exception stream overflow for exceptionBitWidth=${exceptionBitWidth} (ptr=${exPtr}, need ${exceptionCount}, size=${exSize}) at block ${block}`);
for (let k = 0; k < exceptionCount; k = k + 1 | 0) {
const pos = byteContainer[bytePosIn++];
const val = exceptionValues[exPtr++] | 0;
out[pos + blockOutPos | 0] |= val << bitWidth;
}
exceptionPointers[exceptionBitWidth] = exPtr;
return bytePosIn;
}
function decodePageBlocks(inValues, pageStart, inPos, packedEnd, out, outPos, blocks, byteContainer, byteContainerLen, workspace) {
let tmpInPos = inPos | 0;
let bytePosIn = 0;
for (let run = 0; run < blocks; run = run + 1 | 0) {
const header = readBlockHeader(byteContainer, byteContainerLen, bytePosIn, run);
bytePosIn = header.bytePosIn;
const bitWidth = header.bitWidth;
const exceptionCount = header.exceptionCount;
const blockOutPos = outPos + run * 256 | 0;
switch (bitWidth) {
case 0:
out.fill(0, blockOutPos, blockOutPos + 256);
break;
case 32:
for (let i = 0; i < 256; i = i + 1 | 0) out[blockOutPos + i | 0] = inValues[tmpInPos + i | 0] | 0;
tmpInPos = tmpInPos + 256 | 0;
break;
default: tmpInPos = unpackBlock256(inValues, tmpInPos, out, blockOutPos, bitWidth);
}
if (exceptionCount > 0) bytePosIn = applyBlockExceptions(out, blockOutPos, bitWidth, exceptionCount, byteContainer, byteContainerLen, bytePosIn, workspace, run);
}
if (tmpInPos !== packedEnd) throw new Error(`FastPFOR decode: packed region mismatch (pageStart=${pageStart}, packedStart=${inPos}, consumedPackedEnd=${tmpInPos}, expectedPackedEnd=${packedEnd}, packedWords=${packedEnd - inPos}, encoded.length=${inValues.length})`);
}
/**
* Decodes one FastPFOR page (aligned to 256-value blocks).
*/
function decodePage(inValues, out, inPos, outPos, thisSize, workspace) {
const pageStart = inPos | 0;
const whereMeta = inValues[pageStart] | 0;
if (whereMeta <= 0 || pageStart + whereMeta > inValues.length - 1) throw new Error(`FastPFOR decode: invalid whereMeta=${whereMeta} at pageStart=${pageStart} (expected > 0 and pageStart+whereMeta < encoded.length=${inValues.length})`);
const packedStart = pageStart + 1 | 0;
const packedEnd = pageStart + whereMeta | 0;
const byteSize = inValues[packedEnd] >>> 0;
const metaInts = byteSize + 3 >>> 2;
const byteContainerStart = packedEnd + 1;
const bitmapPos = byteContainerStart + metaInts;
if (bitmapPos >= inValues.length) throw new Error(`FastPFOR decode: invalid byteSize=${byteSize} (metaInts=${metaInts}, pageStart=${pageStart}, packedEnd=${packedEnd}, byteContainerStart=${byteContainerStart}) causes bitmapPos=${bitmapPos} out of bounds (encoded.length=${inValues.length})`);
const byteContainer = materializeByteContainer(inValues, byteContainerStart, byteSize, workspace);
const byteContainerLen = byteSize;
const inExcept = unpackExceptionStreams(inValues, bitmapPos, workspace);
workspace.dataPointers.fill(0);
decodePageBlocks(inValues, pageStart, packedStart, packedEnd, out, outPos | 0, thisSize / 256 | 0, byteContainer, byteContainerLen, workspace);
return inExcept;
}
function decodeAlignedPages(inValues, out, inPos, outPos, outLength, workspace) {
const finalOut = outPos + greatestMultiple(outLength, 256);
let tmpOutPos = outPos;
let tmpInPos = inPos;
while (tmpOutPos !== finalOut) {
const thisSize = Math.min(PAGE_SIZE, finalOut - tmpOutPos);
tmpInPos = decodePage(inValues, out, tmpInPos, tmpOutPos, thisSize, workspace);
tmpOutPos = tmpOutPos + thisSize | 0;
}
return tmpInPos;
}
/**
* Decodes the VariableByte tail (MSB=1 terminator, opposite of Protobuf Varint).
*/
function decodeVByte(inValues, inPos, inLength, out, outPos, expectedCount) {
if (expectedCount === 0) return inPos;
let bitOffset = 0;
let wordIndex = inPos;
const finalWordIndex = inPos + inLength;
const outPos0 = outPos;
let tmpOutPos = outPos;
const targetOut = outPos + expectedCount;
let accumulator = 0;
let accumulatorShift = 0;
while (wordIndex < finalWordIndex && tmpOutPos < targetOut) {
const byte = inValues[wordIndex] >>> bitOffset & 255;
bitOffset += 8;
wordIndex += bitOffset >>> 5;
bitOffset &= 31;
accumulator |= (byte & 127) << accumulatorShift;
if ((byte & 128) !== 0) {
out[tmpOutPos++] = accumulator | 0;
accumulator = 0;
accumulatorShift = 0;
} else {
accumulatorShift += 7;
if (accumulatorShift > 28) throw new Error(`FastPFOR VByte: unterminated value (expected MSB=1 terminator within 5 bytes; shift=${accumulatorShift}, partial=${accumulator}, decoded=${tmpOutPos - outPos0}/${expectedCount}, inPos=${wordIndex}, inEnd=${finalWordIndex})`);
}
}
if (tmpOutPos !== targetOut) throw new Error(`FastPFOR VByte: truncated stream (decoded=${tmpOutPos - outPos0}, expected=${expectedCount}, consumedWords=${wordIndex - inPos}/${inLength}, vbyteStart=${inPos}, vbyteEnd=${finalWordIndex})`);
return wordIndex;
}
/**
* Decodes a sequence of FastPFOR-encoded integers.
*
* @param encoded The input buffer containing FastPFOR encoded data.
* @param numValues The number of integers expected to be decoded.
* @param workspace Optional workspace for reuse across calls. If omitted, a new workspace is created per call.
*/
function decodeFastPforInt32(encoded, numValues, workspace) {
let inPos = 0;
let outPos = 0;
const decoded = new Uint32Array(numValues);
const decoderWorkspace = workspace ?? createDecoderWorkspace();
if (encoded.length > 0) {
const alignedLength = encoded[inPos] | 0;
inPos = inPos + 1 | 0;
if ((alignedLength & 255) !== 0) throw new Error(`FastPFOR decode: invalid alignedLength=${alignedLength} (expected multiple of 256)`);
if (outPos + alignedLength > decoded.length) throw new Error(`FastPFOR decode: output buffer too small (outPos=${outPos}, alignedLength=${alignedLength}, out.length=${decoded.length})`);
inPos = decodeAlignedPages(encoded, decoded, inPos, outPos, alignedLength, decoderWorkspace);
outPos = outPos + alignedLength | 0;
}
const remainingLength = encoded.length - inPos | 0;
const expectedTail = numValues - outPos | 0;
decodeVByte(encoded, inPos, remainingLength, decoded, outPos, expectedTail);
return decoded;
}
function fastUnpack32(inValues, inPos, out, outPos, bitWidth) {
switch (bitWidth) {
case 2:
fastUnpack32_2(inValues, inPos, out, outPos);
return;
case 3:
fastUnpack32_3(inValues, inPos, out, outPos);
return;
case 4:
fastUnpack32_4(inValues, inPos, out, outPos);
return;
case 5:
fastUnpack32_5(inValues, inPos, out, outPos);
return;
case 6:
fastUnpack32_6(inValues, inPos, out, outPos);
return;
case 7:
fastUnpack32_7(inValues, inPos, out, outPos);
return;
case 8:
fastUnpack32_8(inValues, inPos, out, outPos);
return;
case 9:
fastUnpack32_9(inValues, inPos, out, outPos);
return;
case 10:
fastUnpack32_10(inValues, inPos, out, outPos);
return;
case 11:
fastUnpack32_11(inValues, inPos, out, outPos);
return;
case 12:
fastUnpack32_12(inValues, inPos, out, outPos);
return;
case 16:
fastUnpack32_16(inValues, inPos, out, outPos);
return;
case 32:
for (let i = 0; i < 32; i = i + 1 | 0) out[outPos + i | 0] = inValues[inPos + i | 0] | 0;
return;
}
const valueMask = MASKS[bitWidth] >>> 0;
let inputWordIndex = inPos;
let bitOffset = 0;
let currentWord = inValues[inputWordIndex] >>> 0;
for (let i = 0; i < 32; i++) if (bitOffset + bitWidth <= 32) {
const value = currentWord >>> bitOffset & valueMask;
out[outPos + i] = value | 0;
bitOffset += bitWidth;
if (bitOffset === 32) {
bitOffset = 0;
inputWordIndex++;
if (i !== 31) currentWord = inValues[inputWordIndex] >>> 0;
}
} else {
const lowBits = 32 - bitOffset;
const low = currentWord >>> bitOffset;
inputWordIndex++;
currentWord = inValues[inputWordIndex] >>> 0;
const highMask = MASKS[bitWidth - lowBits] >>> 0;
const value = (low | (currentWord & highMask) << lowBits) & valueMask;
out[outPos + i] = value | 0;
bitOffset = bitWidth - lowBits;
}
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/bigEndianDecode.js
/**
* Decodes big-endian bytes into `out` without allocating the output buffer.
*
* This function does not copy `bytes`; it writes decoded words into the provided `out` array.
* For aligned inputs it may create a temporary typed-array view (`Uint32Array`) over `bytes.buffer`
* to speed up decoding.
*
* If `byteLength` is not a multiple of 4, the final word is padded with zeros.
*
* @returns Number of int32 words written.
* @throws RangeError If `(offset, byteLength)` is out of bounds, or if `out` is too small.
*/
function decodeBigEndianInt32sInto(bytes, offset, byteLength, out) {
if (offset < 0 || byteLength < 0 || offset + byteLength > bytes.length) throw new RangeError(`decodeBigEndianInt32sInto: out of bounds (offset=${offset}, byteLength=${byteLength}, bytes.length=${bytes.length})`);
const numCompleteInts = Math.floor(byteLength / 4);
const hasTrailingBytes = byteLength % 4 !== 0;
const numInts = hasTrailingBytes ? numCompleteInts + 1 : numCompleteInts;
if (out.length < numInts) throw new RangeError(`decodeBigEndianInt32sInto: out.length=${out.length} < ${numInts}`);
if (numCompleteInts > 0) {
const absoluteOffset = bytes.byteOffset + offset;
if ((absoluteOffset & 3) === 0) {
const u32 = new Uint32Array(bytes.buffer, absoluteOffset, numCompleteInts);
for (let i = 0; i < numCompleteInts; i++) out[i] = bswap32(u32[i]) | 0;
} else for (let i = 0; i < numCompleteInts; i++) {
const base = offset + i * 4;
out[i] = bytes[base] << 24 | bytes[base + 1] << 16 | bytes[base + 2] << 8 | bytes[base + 3] | 0;
}
}
if (hasTrailingBytes) {
const base = offset + numCompleteInts * 4;
const remaining = byteLength - numCompleteInts * 4;
let v = 0;
for (let i = 0; i < remaining; i++) v |= bytes[base + i] << 24 - i * 8;
out[numCompleteInts] = v | 0;
}
return numInts;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/integerDecodingUtils.js
function decodeVarintInt32(buf, bufferOffset, numValues) {
const dst = new Uint32Array(numValues);
let dstOffset = 0;
let offset = bufferOffset.get();
for (let i = 0; i < dst.length; i++) {
let b = buf[offset++];
let val = b & 127;
if (b < 128) {
dst[dstOffset++] = val;
continue;
}
b = buf[offset++];
val |= (b & 127) << 7;
if (b < 128) {
dst[dstOffset++] = val;
continue;
}
b = buf[offset++];
val |= (b & 127) << 14;
if (b < 128) {
dst[dstOffset++] = val;
continue;
}
b = buf[offset++];
val |= (b & 127) << 21;
if (b < 128) {
dst[dstOffset++] = val;
continue;
}
b = buf[offset++];
val |= (b & 15) << 28;
dst[dstOffset++] = val;
}
bufferOffset.set(offset);
return dst;
}
function decodeVarintInt64(src, offset, numValues) {
const dst = new BigUint64Array(numValues);
for (let i = 0; i < dst.length; i++) dst[i] = decodeVarintInt64Value(src, offset);
return dst;
}
function decodeVarintInt64Value(bytes, pos) {
let value = 0n;
let shift = 0;
let index = pos.get();
while (index < bytes.length) {
const b = bytes[index++];
value |= BigInt(b & 127) << BigInt(shift);
if ((b & 128) === 0) break;
shift += 7;
if (shift >= 64) throw new Error("Varint too long");
}
pos.set(index);
return value;
}
function decodeVarintFloat64(src, offset, numValues) {
const dst = new Float64Array(numValues);
for (let i = 0; i < numValues; i++) dst[i] = decodeVarintFloat64Value(src, offset);
return dst;
}
function decodeVarintFloat64Value(buf, offset) {
let val;
let b;
b = buf[offset.get()];
offset.increment();
val = b & 127;
if (b < 128) return val;
b = buf[offset.get()];
offset.increment();
val |= (b & 127) << 7;
if (b < 128) return val;
b = buf[offset.get()];
offset.increment();
val |= (b & 127) << 14;
if (b < 128) return val;
b = buf[offset.get()];
offset.increment();
val |= (b & 127) << 21;
if (b < 128) return val;
b = buf[offset.get()];
val |= (b & 15) << 28;
return decodeVarintRemainder(val, buf, offset);
}
function decodeVarintRemainder(l, buf, offset) {
let h;
let b;
b = buf[offset.get()];
offset.increment();
h = (b & 112) >> 4;
if (b < 128) return h * 4294967296 + (l >>> 0);
b = buf[offset.get()];
offset.increment();
h |= (b & 127) << 3;
if (b < 128) return h * 4294967296 + (l >>> 0);
b = buf[offset.get()];
offset.increment();
h |= (b & 127) << 10;
if (b < 128) return h * 4294967296 + (l >>> 0);
b = buf[offset.get()];
offset.increment();
h |= (b & 127) << 17;
if (b < 128) return h * 4294967296 + (l >>> 0);
b = buf[offset.get()];
offset.increment();
h |= (b & 127) << 24;
if (b < 128) return h * 4294967296 + (l >>> 0);
b = buf[offset.get()];
offset.increment();
h |= (b & 1) << 31;
if (b < 128) return h * 4294967296 + (l >>> 0);
throw new Error("Expected varint not more than 10 bytes");
}
function decodeFastPfor(encodedBytes, expectedValueCount, encodedByteLength, offset) {
return decodeFastPforWithWorkspace(encodedBytes, expectedValueCount, encodedByteLength, offset, createFastPforWireDecodeWorkspace(encodedByteLength >>> 2));
}
function decodeFastPforWithWorkspace(encodedBytes, expectedValueCount, encodedByteLength, offset, workspace) {
const inputByteOffset = offset.get();
if ((encodedByteLength & 3) !== 0) throw new Error(`FastPFOR: invalid encodedByteLength=${encodedByteLength} at offset=${inputByteOffset} (encodedBytes.length=${encodedBytes.length}; expected a multiple of 4 bytes for an int32 big-endian word stream)`);
const encodedWordCount = encodedByteLength >>> 2;
const encodedWordBuffer = ensureFastPforWireEncodedWordsCapacity(workspace, encodedWordCount);
decodeBigEndianInt32sInto(encodedBytes, inputByteOffset, encodedByteLength, encodedWordBuffer);
const decodedValues = decodeFastPforInt32(encodedWordBuffer.subarray(0, encodedWordCount), expectedValueCount, workspace.decoderWorkspace);
offset.add(encodedByteLength);
return decodedValues;
}
function decodeZigZagInt32Value(encoded) {
return encoded >>> 1 ^ -(encoded & 1);
}
function decodeZigZagInt64Value(encoded) {
return encoded >> 1n ^ -(encoded & 1n);
}
function decodeZigZagFloat64Value(encoded) {
return encoded % 2 === 1 ? (encoded + 1) / -2 : encoded / 2;
}
function decodeZigZagInt32(encodedData) {
const decodedValues = new Int32Array(encodedData.length);
for (let i = 0; i < encodedData.length; i++) decodedValues[i] = decodeZigZagInt32Value(encodedData[i]);
return decodedValues;
}
function decodeZigZagInt64(encodedData) {
const decodedValues = new BigInt64Array(encodedData.length);
for (let i = 0; i < encodedData.length; i++) decodedValues[i] = decodeZigZagInt64Value(encodedData[i]);
return decodedValues;
}
function decodeZigZagFloat64(encodedData) {
for (let i = 0; i < encodedData.length; i++) encodedData[i] = decodeZigZagFloat64Value(encodedData[i]);
}
function decodeUnsignedRleInt32(encodedData, numRuns, numTotalValues) {
if (numTotalValues === void 0) {
numTotalValues = 0;
for (let i = 0; i < numRuns; i++) numTotalValues += encodedData[i];
}
const decodedValues = new Uint32Array(numTotalValues);
let offset = 0;
for (let i = 0; i < numRuns; i++) {
const runLength = encodedData[i];
const value = encodedData[i + numRuns];
decodedValues.fill(value, offset, offset + runLength);
offset += runLength;
}
return decodedValues;
}
function decodeUnsignedRleInt64(encodedData, numRuns, numTotalValues) {
if (numTotalValues === void 0) {
numTotalValues = 0;
for (let i = 0; i < numRuns; i++) numTotalValues += Number(encodedData[i]);
}
const decodedValues = new BigUint64Array(numTotalValues);
let offset = 0;
for (let i = 0; i < numRuns; i++) {
const runLength = Number(encodedData[i]);
const value = encodedData[i + numRuns];
decodedValues.fill(value, offset, offset + runLength);
offset += runLength;
}
return decodedValues;
}
function decodeUnsignedRleFloat64(encodedData, numRuns, numTotalValues) {
const decodedValues = new Float64Array(numTotalValues);
let offset = 0;
for (let i = 0; i < numRuns; i++) {
const runLength = encodedData[i];
const value = encodedData[i + numRuns];
decodedValues.fill(value, offset, offset + runLength);
offset += runLength;
}
return decodedValues;
}
function decodeZigZagDeltaInt32(data) {
const decodedValues = new Int32Array(data.length);
decodedValues[0] = decodeZigZagInt32Value(data[0]);
const sz0 = data.length / 4 * 4;
let i = 1;
if (sz0 >= 4) for (; i < sz0 - 4; i += 4) {
const data1 = data[i];
const data2 = data[i + 1];
const data3 = data[i + 2];
const data4 = data[i + 3];
decodedValues[i] = decodeZigZagInt32Value(data1) + decodedValues[i - 1];
decodedValues[i + 1] = decodeZigZagInt32Value(data2) + decodedValues[i];
decodedValues[i + 2] = decodeZigZagInt32Value(data3) + decodedValues[i + 1];
decodedValues[i + 3] = decodeZigZagInt32Value(data4) + decodedValues[i + 2];
}
for (; i !== data.length; ++i) decodedValues[i] = decodeZigZagInt32Value(data[i]) + decodedValues[i - 1];
return decodedValues;
}
function decodeZigZagDeltaInt64(data) {
const decodedValues = new BigInt64Array(data.length);
decodedValues[0] = decodeZigZagInt64Value(data[0]);
const sz0 = data.length / 4 * 4;
let i = 1;
if (sz0 >= 4) for (; i < sz0 - 4; i += 4) {
const data1 = data[i];
const data2 = data[i + 1];
const data3 = data[i + 2];
const data4 = data[i + 3];
decodedValues[i] = decodeZigZagInt64Value(data1) + decodedValues[i - 1];
decodedValues[i + 1] = decodeZigZagInt64Value(data2) + decodedValues[i];
decodedValues[i + 2] = decodeZigZagInt64Value(data3) + decodedValues[i + 1];
decodedValues[i + 3] = decodeZigZagInt64Value(data4) + decodedValues[i + 2];
}
for (; i !== decodedValues.length; ++i) decodedValues[i] = decodeZigZagInt64Value(data[i]) + decodedValues[i - 1];
return decodedValues;
}
function decodeZigZagDeltaFloat64(data) {
data[0] = decodeZigZagFloat64Value(data[0]);
const sz0 = data.length / 4 * 4;
let i = 1;
if (sz0 >= 4) for (; i < sz0 - 4; i += 4) {
const data1 = data[i];
const data2 = data[i + 1];
const data3 = data[i + 2];
const data4 = data[i + 3];
data[i] = decodeZigZagFloat64Value(data1) + data[i - 1];
data[i + 1] = decodeZigZagFloat64Value(data2) + data[i];
data[i + 2] = decodeZigZagFloat64Value(data3) + data[i + 1];
data[i + 3] = decodeZigZagFloat64Value(data4) + data[i + 2];
}
for (; i !== data.length; ++i) data[i] = decodeZigZagFloat64Value(data[i]) + data[i - 1];
}
function decodeZigZagRleInt32(data, numRuns, numTotalValues) {
if (numTotalValues === void 0) {
numTotalValues = 0;
for (let i = 0; i < numRuns; i++) numTotalValues += data[i];
}
const decodedValues = new Int32Array(numTotalValues);
let offset = 0;
for (let i = 0; i < numRuns; i++) {
const runLength = data[i];
let value = data[i + numRuns];
value = decodeZigZagInt32Value(value);
decodedValues.fill(value, offset, offset + runLength);
offset += runLength;
}
return decodedValues;
}
function decodeZigZagRleInt64(data, numRuns, numTotalValues) {
if (numTotalValues === void 0) {
numTotalValues = 0;
for (let i = 0; i < numRuns; i++) numTotalValues += Number(data[i]);
}
const decodedValues = new BigInt64Array(numTotalValues);
let offset = 0;
for (let i = 0; i < numRuns; i++) {
const runLength = Number(data[i]);
let value = data[i + numRuns];
value = decodeZigZagInt64Value(value);
decodedValues.fill(value, offset, offset + runLength);
offset += runLength;
}
return decodedValues;
}
function decodeZigZagRleFloat64(data, numRuns, numTotalValues) {
const decodedValues = new Float64Array(numTotalValues);
let offset = 0;
for (let i = 0; i < numRuns; i++) {
const runLength = data[i];
let value = data[i + numRuns];
value = decodeZigZagFloat64Value(value);
decodedValues.fill(value, offset, offset + runLength);
offset += runLength;
}
return decodedValues;
}
function fastInverseDelta(data) {
const sz0 = data.length / 4 * 4;
let i = 1;
if (sz0 >= 4) for (let a = data[0]; i < sz0 - 4; i += 4) {
a = data[i] += a;
a = data[i + 1] += a;
a = data[i + 2] += a;
a = data[i + 3] += a;
}
while (i !== data.length) {
data[i] += data[i - 1];
++i;
}
}
function inverseDelta(data) {
let prevValue = 0;
for (let i = 0; i < data.length; i++) {
data[i] += prevValue;
prevValue = data[i];
}
}
function decodeComponentwiseDeltaVec2(data) {
if (data.length < 2) return new Int32Array(data);
const decodedData = new Int32Array(data.length);
decodedData[0] = decodeZigZagInt32Value(data[0]);
decodedData[1] = decodeZigZagInt32Value(data[1]);
const sz0 = data.length / 4 * 4;
let i = 2;
if (sz0 >= 4) for (; i < sz0 - 4; i += 4) {
const x1 = data[i];
const y1 = data[i + 1];
const x2 = data[i + 2];
const y2 = data[i + 3];
decodedData[i] = decodeZigZagInt32Value(x1) + decodedData[i - 2];
decodedData[i + 1] = decodeZigZagInt32Value(y1) + decodedData[i - 1];
decodedData[i + 2] = decodeZigZagInt32Value(x2) + decodedData[i];
decodedData[i + 3] = decodeZigZagInt32Value(y2) + decodedData[i + 1];
}
for (; i !== data.length; i += 2) {
decodedData[i] = decodeZigZagInt32Value(data[i]) + decodedData[i - 2];
decodedData[i + 1] = decodeZigZagInt32Value(data[i + 1]) + decodedData[i - 1];
}
return decodedData;
}
function decodeComponentwiseDeltaVec2Scaled(data, scale, min, max) {
if (data.length < 2) return new Int32Array(data);
const decodedData = new Int32Array(data.length);
let previousVertexX = decodeZigZagInt32Value(data[0]);
let previousVertexY = decodeZigZagInt32Value(data[1]);
decodedData[0] = clamp(Math.round(previousVertexX * scale), min, max);
decodedData[1] = clamp(Math.round(previousVertexY * scale), min, max);
const sz0 = data.length / 16;
let i = 2;
if (sz0 >= 4) for (; i < sz0 - 4; i += 4) {
const x1 = data[i];
const y1 = data[i + 1];
const currentVertexX = decodeZigZagInt32Value(x1) + previousVertexX;
const currentVertexY = decodeZigZagInt32Value(y1) + previousVertexY;
decodedData[i] = clamp(Math.round(currentVertexX * scale), min, max);
decodedData[i + 1] = clamp(Math.round(currentVertexY * scale), min, max);
const x2 = data[i + 2];
const y2 = data[i + 3];
previousVertexX = decodeZigZagInt32Value(x2) + currentVertexX;
previousVertexY = decodeZigZagInt32Value(y2) + currentVertexY;
decodedData[i + 2] = clamp(Math.round(previousVertexX * scale), min, max);
decodedData[i + 3] = clamp(Math.round(previousVertexY * scale), min, max);
}
for (; i !== data.length; i += 2) {
previousVertexX += decodeZigZagInt32Value(data[i]);
previousVertexY += decodeZigZagInt32Value(data[i + 1]);
decodedData[i] = clamp(Math.round(previousVertexX * scale), min, max);
decodedData[i + 1] = clamp(Math.round(previousVertexY * scale), min, max);
}
return decodedData;
}
function clamp(n, min, max) {
return Math.min(max, Math.max(min, n));
}
function decodeZigZagDeltaOfDeltaInt32(data) {
const decodedData = new Int32Array(data.length + 1);
decodedData[0] = 0;
decodedData[1] = decodeZigZagInt32Value(data[0]);
let deltaSum = decodedData[1];
for (let i = 2; i !== decodedData.length; ++i) {
const zigZagValue = data[i - 1];
const delta = decodeZigZagInt32Value(zigZagValue);
deltaSum += delta;
decodedData[i] = decodedData[i - 1] + deltaSum;
}
return new Uint32Array(decodedData);
}
function decodeZigZagRleDeltaInt32(data, numRuns, numTotalValues) {
const decodedValues = new Int32Array(numTotalValues + 1);
decodedValues[0] = 0;
let offset = 1;
let previousValue = decodedValues[0];
for (let i = 0; i < numRuns; i++) {
const runLength = data[i];
let value = data[i + numRuns];
value = decodeZigZagInt32Value(value);
for (let j = offset; j < offset + runLength; j++) {
decodedValues[j] = value + previousValue;
previousValue = decodedValues[j];
}
offset += runLength;
}
return decodedValues;
}
function decodeRleDeltaInt32(data, numRuns, numTotalValues) {
const decodedValues = new Uint32Array(numTotalValues + 1);
decodedValues[0] = 0;
let offset = 1;
let previousValue = decodedValues[0];
for (let i = 0; i < numRuns; i++) {
const runLength = data[i];
const value = data[i + numRuns];
for (let j = offset; j < offset + runLength; j++) {
decodedValues[j] = value + previousValue;
previousValue = decodedValues[j];
}
offset += runLength;
}
return decodedValues;
}
/**
* Decode Delta-RLE with multiple runs by fully reconstructing values.
*
* @param data RLE encoded data: [run1, run2, ..., value1, value2, ...]
* @param numRuns Number of runs in the RLE encoding
* @param numValues Total number of values to reconstruct
* @returns Reconstructed values with deltas applied
*/
function decodeDeltaRleInt32(data, numRuns, numValues) {
const result = new Int32Array(numValues);
let outPos = 0;
let previousValue = 0;
for (let i = 0; i < numRuns; i++) {
const runLength = data[i];
const zigZagDelta = data[i + numRuns];
const delta = decodeZigZagInt32Value(zigZagDelta);
for (let j = 0; j < runLength; j++) {
previousValue += delta;
result[outPos++] = previousValue;
}
}
return result;
}
/**
* Decode Delta-RLE with multiple runs for 64-bit integers.
*/
function decodeDeltaRleInt64(data, numRuns, numValues) {
const result = new BigInt64Array(numValues);
let outPos = 0;
let previousValue = 0n;
for (let i = 0; i < numRuns; i++) {
const runLength = Number(data[i]);
const zigZagDelta = data[i + numRuns];
const delta = decodeZigZagInt64Value(zigZagDelta);
for (let j = 0; j < runLength; j++) {
previousValue += delta;
result[outPos++] = previousValue;
}
}
return result;
}
function decodeUnsignedZigZagDeltaInt32(data) {
const decodedValues = new Uint32Array(data.length);
decodedValues[0] = decodeZigZagInt32Value(data[0]) >>> 0;
for (let i = 1; i < data.length; i++) decodedValues[i] = decodedValues[i - 1] + decodeZigZagInt32Value(data[i]) >>> 0;
return decodedValues;
}
function decodeUnsignedZigZagDeltaInt64(data) {
const decodedValues = new BigUint64Array(data.length);
decodedValues[0] = BigInt.asUintN(64, decodeZigZagInt64Value(data[0]));
for (let i = 1; i < data.length; i++) decodedValues[i] = BigInt.asUintN(64, decodedValues[i - 1] + decodeZigZagInt64Value(data[i]));
return decodedValues;
}
function decodeUnsignedComponentwiseDeltaVec2(data) {
if (data.length < 2) return new Uint32Array(data);
const decodedData = new Uint32Array(data.length);
decodedData[0] = decodeZigZagInt32Value(data[0]) >>> 0;
decodedData[1] = decodeZigZagInt32Value(data[1]) >>> 0;
for (let i = 2; i < data.length; i += 2) {
decodedData[i] = decodedData[i - 2] + decodeZigZagInt32Value(data[i]) >>> 0;
decodedData[i + 1] = decodedData[i - 1] + decodeZigZagInt32Value(data[i + 1]) >>> 0;
}
return decodedData;
}
function decodeUnsignedComponentwiseDeltaVec2Scaled(data, scale, min, max) {
const scaledValues = decodeComponentwiseDeltaVec2Scaled(data, scale, min, max);
return new Uint32Array(scaledValues);
}
function decodeUnsignedConstRleInt32(data) {
return data[1];
}
function decodeZigZagConstRleInt32(data) {
return decodeZigZagInt32Value(data[1]);
}
function decodeZigZagSequenceRleInt32(data) {
if (data.length === 2) {
const value = decodeZigZagInt32Value(data[1]);
return [value, value];
}
return [decodeZigZagInt32Value(data[2]), decodeZigZagInt32Value(data[3])];
}
function decodeUnsignedConstRleInt64(data) {
return data[1];
}
function decodeZigZagConstRleInt64(data) {
return decodeZigZagInt64Value(data[1]);
}
function decodeZigZagSequenceRleInt64(data) {
if (data.length === 2) {
const value = decodeZigZagInt64Value(data[1]);
return [value, value];
}
return [decodeZigZagInt64Value(data[2]), decodeZigZagInt64Value(data[3])];
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/physicalStreamType.js
var PhysicalStreamType;
(function(PhysicalStreamType) {
PhysicalStreamType["PRESENT"] = "PRESENT";
PhysicalStreamType["DATA"] = "DATA";
PhysicalStreamType["OFFSET"] = "OFFSET";
PhysicalStreamType["LENGTH"] = "LENGTH";
})(PhysicalStreamType || (PhysicalStreamType = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/dictionaryType.js
var DictionaryType;
(function(DictionaryType) {
DictionaryType["NONE"] = "NONE";
DictionaryType["SINGLE"] = "SINGLE";
DictionaryType["SHARED"] = "SHARED";
DictionaryType["VERTEX"] = "VERTEX";
DictionaryType["MORTON"] = "MORTON";
DictionaryType["FSST"] = "FSST";
})(DictionaryType || (DictionaryType = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/offsetType.js
var OffsetType;
(function(OffsetType) {
OffsetType["VERTEX"] = "VERTEX";
OffsetType["INDEX"] = "INDEX";
OffsetType["STRING"] = "STRING";
OffsetType["KEY"] = "KEY";
})(OffsetType || (OffsetType = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/lengthType.js
var LengthType;
(function(LengthType) {
LengthType["VAR_BINARY"] = "VAR_BINARY";
LengthType["GEOMETRIES"] = "GEOMETRIES";
LengthType["PARTS"] = "PARTS";
LengthType["RINGS"] = "RINGS";
LengthType["TRIANGLES"] = "TRIANGLES";
LengthType["SYMBOL"] = "SYMBOL";
LengthType["DICTIONARY"] = "DICTIONARY";
})(LengthType || (LengthType = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/streamMetadataDecoder.js
const PHYSICAL_STREAM_TYPE_BY_ID = [
PhysicalStreamType.PRESENT,
PhysicalStreamType.DATA,
PhysicalStreamType.OFFSET,
PhysicalStreamType.LENGTH
];
const LOGICAL_LEVEL_TECHNIQUE_BY_ID = [
LogicalLevelTechnique.NONE,
LogicalLevelTechnique.DELTA,
LogicalLevelTechnique.COMPONENTWISE_DELTA,
LogicalLevelTechnique.RLE,
LogicalLevelTechnique.MORTON,
LogicalLevelTechnique.PDE
];
const PHYSICAL_LEVEL_TECHNIQUE_BY_ID = [
PhysicalLevelTechnique.NONE,
PhysicalLevelTechnique.FAST_PFOR,
PhysicalLevelTechnique.VARINT
];
const DICTIONARY_TYPE_BY_ID = [
DictionaryType.NONE,
DictionaryType.SINGLE,
DictionaryType.SHARED,
DictionaryType.VERTEX,
DictionaryType.MORTON,
DictionaryType.FSST
];
const OFFSET_TYPE_BY_ID = [
OffsetType.VERTEX,
OffsetType.INDEX,
OffsetType.STRING,
OffsetType.KEY
];
const LENGTH_TYPE_BY_ID = [
LengthType.VAR_BINARY,
LengthType.GEOMETRIES,
LengthType.PARTS,
LengthType.RINGS,
LengthType.TRIANGLES,
LengthType.SYMBOL,
LengthType.DICTIONARY
];
function decodeStreamMetadata(tile, offset) {
const streamMetadata = decodeStreamMetadataInternal(tile, offset);
if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.MORTON) return decodePartialMortonEncodedStreamMetadata(streamMetadata, tile, offset);
if ((LogicalLevelTechnique.RLE === streamMetadata.logicalLevelTechnique1 || LogicalLevelTechnique.RLE === streamMetadata.logicalLevelTechnique2) && PhysicalLevelTechnique.NONE !== streamMetadata.physicalLevelTechnique) return decodePartialRleEncodedStreamMetadata(streamMetadata, tile, offset);
return streamMetadata;
}
function decodePartialMortonEncodedStreamMetadata(streamMetadata, tile, offset) {
const mortonInfo = decodeVarintInt32(tile, offset, 2);
return {
physicalStreamType: streamMetadata.physicalStreamType,
logicalStreamType: streamMetadata.logicalStreamType,
logicalLevelTechnique1: streamMetadata.logicalLevelTechnique1,
logicalLevelTechnique2: streamMetadata.logicalLevelTechnique2,
physicalLevelTechnique: streamMetadata.physicalLevelTechnique,
numValues: streamMetadata.numValues,
byteLength: streamMetadata.byteLength,
decompressedCount: streamMetadata.decompressedCount,
numBits: mortonInfo[0],
coordinateShift: mortonInfo[1]
};
}
function decodePartialRleEncodedStreamMetadata(streamMetadata, tile, offset) {
const rleInfo = decodeVarintInt32(tile, offset, 2);
return {
physicalStreamType: streamMetadata.physicalStreamType,
logicalStreamType: streamMetadata.logicalStreamType,
logicalLevelTechnique1: streamMetadata.logicalLevelTechnique1,
logicalLevelTechnique2: streamMetadata.logicalLevelTechnique2,
physicalLevelTechnique: streamMetadata.physicalLevelTechnique,
numValues: streamMetadata.numValues,
byteLength: streamMetadata.byteLength,
decompressedCount: rleInfo[1],
runs: rleInfo[0],
numRleValues: rleInfo[1]
};
}
function decodeStreamMetadataInternal(tile, offset) {
const stream_type = tile[offset.get()];
const physicalStreamType = PHYSICAL_STREAM_TYPE_BY_ID[stream_type >> 4];
let logicalStreamType = {};
switch (physicalStreamType) {
case PhysicalStreamType.DATA:
logicalStreamType = { dictionaryType: DICTIONARY_TYPE_BY_ID[stream_type & 15] };
break;
case PhysicalStreamType.OFFSET:
logicalStreamType = { offsetType: OFFSET_TYPE_BY_ID[stream_type & 15] };
break;
case PhysicalStreamType.LENGTH: logicalStreamType = { lengthType: LENGTH_TYPE_BY_ID[stream_type & 15] };
}
offset.increment();
const encodings_header = tile[offset.get()];
const llt1 = LOGICAL_LEVEL_TECHNIQUE_BY_ID[encodings_header >> 5];
const llt2 = LOGICAL_LEVEL_TECHNIQUE_BY_ID[encodings_header >> 2 & 7];
const plt = PHYSICAL_LEVEL_TECHNIQUE_BY_ID[encodings_header & 3];
offset.increment();
const sizeInfo = decodeVarintInt32(tile, offset, 2);
const numValues = sizeInfo[0];
const byteLength = sizeInfo[1];
return {
physicalStreamType,
logicalStreamType,
logicalLevelTechnique1: llt1,
logicalLevelTechnique2: llt2,
physicalLevelTechnique: plt,
numValues,
byteLength,
decompressedCount: numValues
};
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/vectorType.js
var VectorType;
(function(VectorType) {
VectorType[VectorType["FLAT"] = 0] = "FLAT";
VectorType[VectorType["CONST"] = 1] = "CONST";
VectorType[VectorType["SEQUENCE"] = 2] = "SEQUENCE";
VectorType[VectorType["DICTIONARY"] = 3] = "DICTIONARY";
VectorType[VectorType["FSST_DICTIONARY"] = 4] = "FSST_DICTIONARY";
})(VectorType || (VectorType = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/bitVector.js
var BitVector = class {
/**
* @param values The byte buffer containing the bit values in least-significant bit (LSB)
* numbering
*/
constructor(values, size) {
this.values = values;
this._size = size;
}
get(index) {
const byteIndex = Math.floor(index / 8);
const bitIndex = index % 8;
return (this.values[byteIndex] >> bitIndex & 1) === 1;
}
set(index, value) {
const byteIndex = Math.floor(index / 8);
const bitIndex = index % 8;
this.values[byteIndex] = this.values[byteIndex] | (value ? 1 : 0) << bitIndex;
}
getInt(index) {
const byteIndex = Math.floor(index / 8);
const bitIndex = index % 8;
return this.values[byteIndex] >> bitIndex & 1;
}
size() {
return this._size;
}
getBuffer() {
return this.values;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/unpackNullableUtils.js
/**
* Generic unpacking function.
* Reconstructs the full array by inserting default values at null positions.
*
* @param dataStream The compact data stream containing only non-null values
* @param presentBits BitVector indicating which positions have values (null if non-nullable)
* @param defaultValue The default value to insert at null positions (0, 0n, etc.)
* @returns Full array with default values at null positions
*/
function unpackNullable(dataStream, presentBits, defaultValue) {
if (!presentBits) return dataStream;
const size = presentBits.size();
const constructor = dataStream.constructor;
const result = new constructor(size);
let counter = 0;
for (let i = 0; i < size; i++) result[i] = presentBits.get(i) ? dataStream[counter++] : defaultValue;
return result;
}
/**
* Special case for boolean columns because BitVector is not directly compatible with TypedArray.
*
* @param dataStream The compact BitVector data containing only non-null boolean values
* @param dataStreamSize The number of actual values in dataStream
* @param presentBits BitVector indicating which positions have values (null if non-nullable)
* @returns Uint8Array buffer for BitVector with false at null positions
*/
function unpackNullableBoolean(dataStream, dataStreamSize, presentBits) {
if (!presentBits) return dataStream;
const numFeatures = presentBits.size();
const bitVector = new BitVector(dataStream, dataStreamSize);
const result = new BitVector(new Uint8Array(Math.ceil(numFeatures / 8)), numFeatures);
let counter = 0;
for (let i = 0; i < numFeatures; i++) {
const value = presentBits.get(i) ? bitVector.get(counter++) : false;
result.set(i, value);
}
return result.getBuffer();
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/decodingUtils.js
function skipColumn(numStreams, tile, offset) {
for (let i = 0; i < numStreams; i++) {
const streamMetadata = decodeStreamMetadata(tile, offset);
offset.add(streamMetadata.byteLength);
}
}
function decodeBooleanRle(buffer, numBooleans, byteLength, pos, nullabilityBuffer) {
const values = decodeByteRle(buffer, Math.ceil(numBooleans / 8), byteLength, pos);
if (nullabilityBuffer) return unpackNullableBoolean(values, numBooleans, nullabilityBuffer);
return values;
}
function decodeByteRle(buffer, numBytes, byteLength, pos) {
const values = new Uint8Array(numBytes);
let valueOffset = 0;
const streamEndPos = pos.get() + byteLength;
while (valueOffset < numBytes) {
if (pos.get() >= streamEndPos) break;
const header = buffer[pos.increment()];
if (header <= 127) {
const numRuns = header + 3;
const value = buffer[pos.increment()];
const endValueOffset = Math.min(valueOffset + numRuns, numBytes);
values.fill(value, valueOffset, endValueOffset);
valueOffset = endValueOffset;
} else {
const numLiterals = 256 - header;
for (let i = 0; i < numLiterals && valueOffset < numBytes; i++) values[valueOffset++] = buffer[pos.increment()];
}
}
pos.set(streamEndPos);
return values;
}
function decodeFloatsLE(encodedValues, pos, numValues, nullabilityBuffer) {
const currentPos = pos.get();
const newOffset = currentPos + numValues * Float32Array.BYTES_PER_ELEMENT;
const newBuf = new Uint8Array(encodedValues.subarray(currentPos, newOffset)).buffer;
const fb = new Float32Array(newBuf);
pos.set(newOffset);
if (nullabilityBuffer) return unpackNullable(fb, nullabilityBuffer, 0);
return fb;
}
function decodeDoublesLE(encodedValues, pos, numValues, nullabilityBuffer) {
const currentPos = pos.get();
const newOffset = currentPos + numValues * Float64Array.BYTES_PER_ELEMENT;
const newBuf = new Uint8Array(encodedValues.subarray(currentPos, newOffset)).buffer;
const fb = new Float64Array(newBuf);
pos.set(newOffset);
if (nullabilityBuffer) return unpackNullable(fb, nullabilityBuffer, 0);
return fb;
}
function decodeUint32sLE(encodedValues, pos, numValues) {
const currentPos = pos.get();
const byteLength = numValues * Uint32Array.BYTES_PER_ELEMENT;
const view = new DataView(encodedValues.buffer, encodedValues.byteOffset, encodedValues.byteLength);
const values = new Uint32Array(numValues);
for (let i = 0; i < numValues; i++) values[i] = view.getUint32(currentPos + i * Uint32Array.BYTES_PER_ELEMENT, true);
pos.add(byteLength);
return values;
}
function decodeUint64sLE(encodedValues, pos, numValues) {
const currentPos = pos.get();
const byteLength = numValues * BigUint64Array.BYTES_PER_ELEMENT;
const view = new DataView(encodedValues.buffer, encodedValues.byteOffset, encodedValues.byteLength);
const values = new BigUint64Array(numValues);
for (let i = 0; i < numValues; i++) values[i] = view.getBigUint64(currentPos + i * BigUint64Array.BYTES_PER_ELEMENT, true);
pos.add(byteLength);
return values;
}
const TEXT_DECODER_MIN_LENGTH = 12;
const utf8TextDecoder = new TextDecoder();
function decodeString$2(buf, pos, end) {
if (end - pos >= TEXT_DECODER_MIN_LENGTH) return utf8TextDecoder.decode(buf.subarray(pos, end));
return readUtf8(buf, pos, end);
}
function readUtf8(buf, pos, end) {
let str = "";
let i = pos;
while (i < end) {
const b0 = buf[i];
let c = null;
let bytesPerSequence = b0 > 239 ? 4 : b0 > 223 ? 3 : b0 > 191 ? 2 : 1;
if (i + bytesPerSequence > end) break;
let b1;
let b2;
let b3;
if (bytesPerSequence === 1) {
if (b0 < 128) c = b0;
} else if (bytesPerSequence === 2) {
b1 = buf[i + 1];
if ((b1 & 192) === 128) {
c = (b0 & 31) << 6 | b1 & 63;
if (c <= 127) c = null;
}
} else if (bytesPerSequence === 3) {
b1 = buf[i + 1];
b2 = buf[i + 2];
if ((b1 & 192) === 128 && (b2 & 192) === 128) {
c = (b0 & 15) << 12 | (b1 & 63) << 6 | b2 & 63;
if (c <= 2047 || c >= 55296 && c <= 57343) c = null;
}
} else if (bytesPerSequence === 4) {
b1 = buf[i + 1];
b2 = buf[i + 2];
b3 = buf[i + 3];
if ((b1 & 192) === 128 && (b2 & 192) === 128 && (b3 & 192) === 128) {
c = (b0 & 15) << 18 | (b1 & 63) << 12 | (b2 & 63) << 6 | b3 & 63;
if (c <= 65535 || c >= 1114112) c = null;
}
}
if (c === null) {
c = 65533;
bytesPerSequence = 1;
} else if (c > 65535) {
c -= 65536;
str += String.fromCharCode(c >>> 10 & 1023 | 55296);
c = 56320 | c & 1023;
}
str += String.fromCharCode(c);
i += bytesPerSequence;
}
return str;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/integerStreamDecoder.js
function decodeSignedInt32Stream(data, offset, streamMetadata, scalingData, nullabilityBuffer) {
return decodeSignedInt32(decodePhysicalLevelTechnique(data, offset, streamMetadata), streamMetadata, scalingData, nullabilityBuffer);
}
function decodeUnsignedInt32Stream(data, offset, streamMetadata, scalingData, nullabilityBuffer) {
return decodeUnsignedInt32(decodePhysicalLevelTechnique(data, offset, streamMetadata), streamMetadata, scalingData, nullabilityBuffer);
}
function decodeLengthStreamToOffsetBuffer(data, offset, streamMetadata) {
return decodeLengthToOffsetBuffer(decodePhysicalLevelTechnique(data, offset, streamMetadata), streamMetadata);
}
function decodePhysicalLevelTechnique(data, offset, streamMetadata) {
const physicalLevelTechnique = streamMetadata.physicalLevelTechnique;
switch (physicalLevelTechnique) {
case PhysicalLevelTechnique.FAST_PFOR: return decodeFastPfor(data, streamMetadata.numValues, streamMetadata.byteLength, offset);
case PhysicalLevelTechnique.VARINT: return decodeVarintInt32(data, offset, streamMetadata.numValues);
case PhysicalLevelTechnique.NONE: return decodeUint32sLE(data, offset, streamMetadata.numValues);
default: throw new Error(`Specified physicalLevelTechnique ${physicalLevelTechnique} is not supported (yet).`);
}
}
function decodePhysicalLevelTechniqueInt64(data, offset, streamMetadata) {
const physicalLevelTechnique = streamMetadata.physicalLevelTechnique;
switch (physicalLevelTechnique) {
case PhysicalLevelTechnique.VARINT: return decodeVarintInt64(data, offset, streamMetadata.numValues);
case PhysicalLevelTechnique.NONE: return decodeUint64sLE(data, offset, streamMetadata.numValues);
default: throw new Error(`Specified physicalLevelTechnique ${physicalLevelTechnique} is not supported (yet).`);
}
}
function decodeSignedConstInt32Stream(data, offset, streamMetadata) {
const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);
if (values.length === 1) return decodeZigZagInt32Value(values[0]);
return decodeZigZagConstRleInt32(values);
}
function decodeUnsignedConstInt32Stream(data, offset, streamMetadata) {
const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);
if (values.length === 1) {
if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.DELTA) return decodeZigZagInt32Value(values[0]);
return values[0];
}
return decodeUnsignedConstRleInt32(values);
}
function decodeSequenceInt32Stream(data, offset, streamMetadata) {
return decodeZigZagSequenceRleInt32(decodePhysicalLevelTechnique(data, offset, streamMetadata));
}
function decodeSequenceInt64Stream(data, offset, streamMetadata) {
return decodeZigZagSequenceRleInt64(decodeVarintInt64(data, offset, streamMetadata.numValues));
}
function decodeSignedInt64Stream(data, offset, streamMetadata, nullabilityBuffer) {
return decodeSignedInt64(decodePhysicalLevelTechniqueInt64(data, offset, streamMetadata), streamMetadata, nullabilityBuffer);
}
function decodeUnsignedInt64Stream(data, offset, streamMetadata, nullabilityBuffer) {
return decodeUnsignedInt64(decodePhysicalLevelTechniqueInt64(data, offset, streamMetadata), streamMetadata, nullabilityBuffer);
}
function decodeUnsignedInt64AsFloat64Stream(data, offset, streamMetadata, nullabilityBuffer) {
const values = decodeInt64AsFloat64(data, offset, streamMetadata, false);
return nullabilityBuffer ? unpackNullable(values, nullabilityBuffer, 0) : values;
}
function decodeInt64AsFloat64(data, offset, streamMetadata, isSigned) {
if (streamMetadata.physicalLevelTechnique === PhysicalLevelTechnique.VARINT) return decodeFloat64Values(decodeVarintFloat64(data, offset, streamMetadata.numValues), streamMetadata, isSigned);
const values = decodePhysicalLevelTechniqueInt64(data, offset, streamMetadata);
const decodedValues = isSigned ? decodeSignedInt64(values, streamMetadata) : decodeUnsignedInt64(values, streamMetadata);
return Float64Array.from(decodedValues, Number);
}
function decodeSignedConstInt64Stream(data, offset, streamMetadata) {
const values = decodePhysicalLevelTechniqueInt64(data, offset, streamMetadata);
if (values.length === 1) return decodeZigZagInt64Value(values[0]);
return decodeZigZagConstRleInt64(values);
}
function decodeUnsignedConstInt64Stream(data, offset, streamMetadata) {
const values = decodePhysicalLevelTechniqueInt64(data, offset, streamMetadata);
if (values.length === 1) {
if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.DELTA) return decodeZigZagInt64Value(values[0]);
return values[0];
}
return decodeUnsignedConstRleInt64(values);
}
/**
* This method decodes integer streams.
* Currently the encoder uses only fixed combinations of encodings.
* For performance reasons it is also uses a fixed combination of the encodings on the decoding side.
* The following encodings and combinations are used:
* - Morton Delta -> always sorted so not ZigZag encoding needed
* - Delta -> currently always in combination with ZigZag encoding
* - Rle -> in combination with ZigZag encoding if data type is signed
* - Delta Rle
* - Componentwise Delta -> always ZigZag encoding is used
*/
function decodeSignedInt32(values, streamMetadata, scalingData, nullabilityBuffer) {
let decodedValues;
switch (streamMetadata.logicalLevelTechnique1) {
case LogicalLevelTechnique.DELTA:
if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {
const rleMetadata = streamMetadata;
if (!nullabilityBuffer) return decodeDeltaRleInt32(values, rleMetadata.runs, rleMetadata.numRleValues);
values = decodeUnsignedRleInt32(values, rleMetadata.runs, rleMetadata.numRleValues);
decodedValues = decodeZigZagDeltaInt32(values);
} else decodedValues = decodeZigZagDeltaInt32(values);
break;
case LogicalLevelTechnique.RLE:
decodedValues = decodeZigZagRleInt32(values, streamMetadata.runs, streamMetadata.numRleValues);
break;
case LogicalLevelTechnique.MORTON:
fastInverseDelta(values);
decodedValues = new Int32Array(values);
break;
case LogicalLevelTechnique.COMPONENTWISE_DELTA:
if (scalingData && !nullabilityBuffer) return decodeComponentwiseDeltaVec2Scaled(values, scalingData.scale, scalingData.min, scalingData.max);
decodedValues = decodeComponentwiseDeltaVec2(values);
break;
case LogicalLevelTechnique.NONE:
decodedValues = decodeZigZagInt32(values);
break;
default: throw new Error(`The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`);
}
if (nullabilityBuffer) return unpackNullable(decodedValues, nullabilityBuffer, 0);
return decodedValues;
}
function decodeUnsignedInt32(values, streamMetadata, scalingData, nullabilityBuffer) {
let decodedValues;
switch (streamMetadata.logicalLevelTechnique1) {
case LogicalLevelTechnique.DELTA:
if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {
const rleMetadata = streamMetadata;
decodedValues = decodeUnsignedZigZagDeltaInt32(decodeUnsignedRleInt32(values, rleMetadata.runs, rleMetadata.numRleValues));
} else decodedValues = decodeUnsignedZigZagDeltaInt32(values);
break;
case LogicalLevelTechnique.RLE:
decodedValues = decodeUnsignedRleInt32(values, streamMetadata.runs, streamMetadata.numRleValues);
break;
case LogicalLevelTechnique.MORTON:
fastInverseDelta(values);
decodedValues = values;
break;
case LogicalLevelTechnique.COMPONENTWISE_DELTA:
if (scalingData && !nullabilityBuffer) decodedValues = decodeUnsignedComponentwiseDeltaVec2Scaled(values, scalingData.scale, scalingData.min, scalingData.max);
else decodedValues = decodeUnsignedComponentwiseDeltaVec2(values);
break;
case LogicalLevelTechnique.NONE:
decodedValues = values;
break;
default: throw new Error(`The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`);
}
if (nullabilityBuffer) return unpackNullable(decodedValues, nullabilityBuffer, 0);
return decodedValues;
}
function decodeSignedInt64(values, streamMetadata, nullabilityBuffer) {
let decodedValues;
switch (streamMetadata.logicalLevelTechnique1) {
case LogicalLevelTechnique.DELTA:
if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {
const rleMetadata = streamMetadata;
if (!nullabilityBuffer) return decodeDeltaRleInt64(values, rleMetadata.runs, rleMetadata.numRleValues);
values = decodeUnsignedRleInt64(values, rleMetadata.runs, rleMetadata.numRleValues);
decodedValues = decodeZigZagDeltaInt64(values);
} else decodedValues = decodeZigZagDeltaInt64(values);
break;
case LogicalLevelTechnique.RLE:
decodedValues = decodeZigZagRleInt64(values, streamMetadata.runs, streamMetadata.numRleValues);
break;
case LogicalLevelTechnique.NONE:
decodedValues = decodeZigZagInt64(values);
break;
default: throw new Error(`The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`);
}
if (nullabilityBuffer) return unpackNullable(decodedValues, nullabilityBuffer, 0n);
return decodedValues;
}
function decodeUnsignedInt64(values, streamMetadata, nullabilityBuffer) {
let decodedValues;
switch (streamMetadata.logicalLevelTechnique1) {
case LogicalLevelTechnique.DELTA:
if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {
const rleMetadata = streamMetadata;
decodedValues = decodeUnsignedZigZagDeltaInt64(decodeUnsignedRleInt64(values, rleMetadata.runs, rleMetadata.numRleValues));
} else decodedValues = decodeUnsignedZigZagDeltaInt64(values);
break;
case LogicalLevelTechnique.RLE:
decodedValues = decodeUnsignedRleInt64(values, streamMetadata.runs, streamMetadata.numRleValues);
break;
case LogicalLevelTechnique.NONE:
decodedValues = values;
break;
default: throw new Error(`The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`);
}
if (nullabilityBuffer) return unpackNullable(decodedValues, nullabilityBuffer, 0n);
return decodedValues;
}
function decodeFloat64Values(values, streamMetadata, isSigned) {
switch (streamMetadata.logicalLevelTechnique1) {
case LogicalLevelTechnique.DELTA:
if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {
const rleMetadata = streamMetadata;
values = decodeUnsignedRleFloat64(values, rleMetadata.runs, rleMetadata.numRleValues);
}
decodeZigZagDeltaFloat64(values);
return values;
case LogicalLevelTechnique.RLE: return decodeRleFloat64(values, streamMetadata, isSigned);
case LogicalLevelTechnique.NONE:
if (isSigned) decodeZigZagFloat64(values);
return values;
default: throw new Error(`The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`);
}
}
function decodeLengthToOffsetBuffer(values, streamMetadata) {
if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.DELTA && streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.NONE) return decodeZigZagDeltaOfDeltaInt32(values);
if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.RLE && streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.NONE) {
const rleMetadata = streamMetadata;
return decodeRleDeltaInt32(values, rleMetadata.runs, rleMetadata.numRleValues);
}
if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.NONE && streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.NONE) {
inverseDelta(values);
const offsets = new Uint32Array(streamMetadata.numValues + 1);
offsets[0] = 0;
offsets.set(values, 1);
return offsets;
}
if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.DELTA && streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {
const rleMetadata = streamMetadata;
const decodedValues = decodeZigZagRleDeltaInt32(values, rleMetadata.runs, rleMetadata.numRleValues);
fastInverseDelta(decodedValues);
return new Uint32Array(decodedValues);
}
throw new Error("Only delta encoding is supported for transforming length to offset streams yet.");
}
function getVectorType(streamMetadata, sizeOrNullabilityBuffer, data, offset, varintWidth = "int32") {
const logicalLevelTechnique1 = streamMetadata.logicalLevelTechnique1;
if (logicalLevelTechnique1 === LogicalLevelTechnique.RLE) return streamMetadata.runs === 1 ? VectorType.CONST : VectorType.FLAT;
if (logicalLevelTechnique1 !== LogicalLevelTechnique.DELTA || streamMetadata.logicalLevelTechnique2 !== LogicalLevelTechnique.RLE) return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;
const numFeatures = sizeOrNullabilityBuffer instanceof BitVector ? sizeOrNullabilityBuffer.size() : sizeOrNullabilityBuffer;
const rleMetadata = streamMetadata;
if (rleMetadata.numRleValues !== numFeatures) return VectorType.FLAT;
if (rleMetadata.runs === 1) return VectorType.SEQUENCE;
if (rleMetadata.runs !== 2) return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;
const savedOffset = offset.get();
if (streamMetadata.physicalLevelTechnique === PhysicalLevelTechnique.VARINT) {
if (isDeltaRleSequenceVarintWidth(data, offset, varintWidth)) return VectorType.SEQUENCE;
return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;
}
const byteOffset = offset.get();
const values = new Int32Array(data.buffer, data.byteOffset + byteOffset, 4);
offset.set(savedOffset);
const zigZagOne = 2;
if (values[2] === zigZagOne && values[3] === zigZagOne) return VectorType.SEQUENCE;
return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;
}
function isDeltaRleSequenceVarintWidth(data, offset, varintWidth) {
const peekOffset = new IntWrapper(offset.get());
if (varintWidth === "int64") {
const values = decodeVarintInt64(data, peekOffset, 4);
return values[2] === 2n && values[3] === 2n;
}
const values = decodeVarintInt32(data, peekOffset, 4);
return values[2] === 2 && values[3] === 2;
}
function decodeRleFloat64(data, streamMetadata, isSigned) {
return isSigned ? decodeZigZagRleFloat64(data, streamMetadata.runs, streamMetadata.numRleValues) : decodeUnsignedRleFloat64(data, streamMetadata.runs, streamMetadata.numRleValues);
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/int64FlatVector.js
var Int64FlatVector = class extends FixedSizeVector {
getValueFromBuffer(index) {
return this.dataBuffer[index];
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/sequence/int64SequenceVector.js
var Int64SequenceVector = class extends SequenceVector {
constructor(name, baseValue, delta, size, isSigned) {
super(name, isSigned ? BigInt64Array.of(baseValue) : BigUint64Array.of(baseValue), delta, size);
}
getValueFromBuffer(index) {
return this.dataBuffer[0] + BigInt(index) * this.delta;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/zOrderCurve.js
function decodeZOrderCurve(mortonCode, numBits, coordinateShift) {
return {
x: decodeMorton(mortonCode, numBits) - coordinateShift,
y: decodeMorton(mortonCode >> 1, numBits) - coordinateShift
};
}
function decodeMorton(code, numBits) {
let coordinate = 0;
for (let i = 0; i < numBits; i++) coordinate |= (code & 1 << 2 * i) >> i;
return coordinate;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/geometryType.js
var GEOMETRY_TYPE;
(function(GEOMETRY_TYPE) {
GEOMETRY_TYPE[GEOMETRY_TYPE["POINT"] = 0] = "POINT";
GEOMETRY_TYPE[GEOMETRY_TYPE["LINESTRING"] = 1] = "LINESTRING";
GEOMETRY_TYPE[GEOMETRY_TYPE["POLYGON"] = 2] = "POLYGON";
GEOMETRY_TYPE[GEOMETRY_TYPE["MULTIPOINT"] = 3] = "MULTIPOINT";
GEOMETRY_TYPE[GEOMETRY_TYPE["MULTILINESTRING"] = 4] = "MULTILINESTRING";
GEOMETRY_TYPE[GEOMETRY_TYPE["MULTIPOLYGON"] = 5] = "MULTIPOLYGON";
})(GEOMETRY_TYPE || (GEOMETRY_TYPE = {}));
var SINGLE_PART_GEOMETRY_TYPE;
(function(SINGLE_PART_GEOMETRY_TYPE) {
SINGLE_PART_GEOMETRY_TYPE[SINGLE_PART_GEOMETRY_TYPE["POINT"] = 0] = "POINT";
SINGLE_PART_GEOMETRY_TYPE[SINGLE_PART_GEOMETRY_TYPE["LINESTRING"] = 1] = "LINESTRING";
SINGLE_PART_GEOMETRY_TYPE[SINGLE_PART_GEOMETRY_TYPE["POLYGON"] = 2] = "POLYGON";
})(SINGLE_PART_GEOMETRY_TYPE || (SINGLE_PART_GEOMETRY_TYPE = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/vertexBufferType.js
var VertexBufferType;
(function(VertexBufferType) {
VertexBufferType[VertexBufferType["MORTON"] = 0] = "MORTON";
VertexBufferType[VertexBufferType["VEC_2"] = 1] = "VEC_2";
VertexBufferType[VertexBufferType["VEC_3"] = 2] = "VEC_3";
})(VertexBufferType || (VertexBufferType = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/geometryVectorConverter.js
function convertGeometryVector(geometryVector) {
const geometries = new Array(geometryVector.numGeometries);
let partOffsetCounter = 1;
let ringOffsetsCounter = 1;
let geometryOffsetsCounter = 1;
let geometryCounter = 0;
let vertexBufferOffset = 0;
let vertexOffsetsOffset = 0;
const mortonSettings = geometryVector.mortonSettings;
const topologyVector = geometryVector.topologyVector;
const geometryOffsets = topologyVector.geometryOffsets;
const partOffsets = topologyVector.partOffsets;
const ringOffsets = topologyVector.ringOffsets;
const vertexOffsets = geometryVector.vertexOffsets;
const nonOffset = !vertexOffsets || vertexOffsets.length === 0;
const containsPolygon = geometryVector.containsPolygonGeometry();
const vertexBuffer = geometryVector.vertexBuffer;
for (let i = 0; i < geometryVector.numGeometries; i++) {
const geometryType = geometryVector.geometryType(i);
switch (geometryType) {
case GEOMETRY_TYPE.POINT:
{
let x;
let y;
if (nonOffset) {
x = vertexBuffer[vertexBufferOffset++];
y = vertexBuffer[vertexBufferOffset++];
} else if (geometryVector.vertexBufferType === VertexBufferType.MORTON) {
const mortonCode = vertexBuffer[vertexOffsets[vertexOffsetsOffset++]];
const vertex = decodeZOrderCurve(mortonCode, mortonSettings.numBits, mortonSettings.coordinateShift);
x = vertex.x;
y = vertex.y;
} else {
const offset = vertexOffsets[vertexOffsetsOffset++] * 2;
x = vertexBuffer[offset];
y = vertexBuffer[offset + 1];
}
geometries[geometryCounter++] = [[new Point(x, y)]];
if (geometryOffsets) geometryOffsetsCounter++;
if (partOffsets) partOffsetCounter++;
if (ringOffsets) ringOffsetsCounter++;
}
break;
case GEOMETRY_TYPE.MULTIPOINT:
{
const numPoints = geometryOffsets[geometryOffsetsCounter] - geometryOffsets[geometryOffsetsCounter - 1];
geometryOffsetsCounter++;
let points;
if (nonOffset) {
points = new Array(numPoints);
for (let j = 0; j < numPoints; j++) {
const x = vertexBuffer[vertexBufferOffset++];
const y = vertexBuffer[vertexBufferOffset++];
points[j] = new Point(x, y);
}
} else {
points = decodeDictionaryEncodedVertices(geometryVector.vertexBufferType, vertexBuffer, vertexOffsets, vertexOffsetsOffset, numPoints, false, mortonSettings);
vertexOffsetsOffset += numPoints;
}
geometries[geometryCounter++] = points.map((point) => [point]);
partOffsetCounter += numPoints;
ringOffsetsCounter += numPoints;
}
break;
case GEOMETRY_TYPE.LINESTRING:
{
let numVertices;
if (containsPolygon) {
numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];
ringOffsetsCounter++;
} else numVertices = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];
partOffsetCounter++;
let vertices;
if (nonOffset) {
vertices = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, false);
vertexBufferOffset += numVertices * 2;
} else {
vertices = decodeDictionaryEncodedVertices(geometryVector.vertexBufferType, vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, false, mortonSettings);
vertexOffsetsOffset += numVertices;
}
geometries[geometryCounter++] = [vertices];
if (geometryOffsets) geometryOffsetsCounter++;
}
break;
case GEOMETRY_TYPE.POLYGON:
{
const numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];
partOffsetCounter++;
const rings = new Array(numRings - 1);
let shell;
let numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];
ringOffsetsCounter++;
if (nonOffset) {
shell = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, true);
vertexBufferOffset += numVertices * 2;
for (let j = 0; j < rings.length; j++) {
numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];
ringOffsetsCounter++;
rings[j] = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, true);
vertexBufferOffset += numVertices * 2;
}
} else {
shell = decodeDictionaryEncodedVertices(geometryVector.vertexBufferType, vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, true, mortonSettings);
vertexOffsetsOffset += numVertices;
for (let j = 0; j < rings.length; j++) {
numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];
ringOffsetsCounter++;
rings[j] = decodeDictionaryEncodedVertices(geometryVector.vertexBufferType, vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, true, mortonSettings);
vertexOffsetsOffset += numVertices;
}
}
geometries[geometryCounter++] = [shell].concat(rings);
if (geometryOffsets) geometryOffsetsCounter++;
}
break;
case GEOMETRY_TYPE.MULTILINESTRING:
{
const numLineStrings = geometryOffsets[geometryOffsetsCounter] - geometryOffsets[geometryOffsetsCounter - 1];
geometryOffsetsCounter++;
const lineStrings = new Array(numLineStrings);
for (let j = 0; j < numLineStrings; j++) {
let numVertices;
if (containsPolygon) {
numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];
ringOffsetsCounter++;
} else numVertices = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];
partOffsetCounter++;
if (nonOffset) {
lineStrings[j] = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, false);
vertexBufferOffset += numVertices * 2;
} else {
const vertices = decodeDictionaryEncodedVertices(geometryVector.vertexBufferType, vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, false, mortonSettings);
lineStrings[j] = vertices;
vertexOffsetsOffset += numVertices;
}
}
geometries[geometryCounter++] = lineStrings;
}
break;
case GEOMETRY_TYPE.MULTIPOLYGON:
{
const numPolygons = geometryOffsets[geometryOffsetsCounter] - geometryOffsets[geometryOffsetsCounter - 1];
geometryOffsetsCounter++;
const polygons = new Array(numPolygons);
for (let j = 0; j < numPolygons; j++) {
const numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];
partOffsetCounter++;
let shell;
const rings = new Array(numRings - 1);
const numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];
ringOffsetsCounter++;
if (nonOffset) {
shell = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, true);
vertexBufferOffset += numVertices * 2;
} else {
shell = decodeDictionaryEncodedVertices(geometryVector.vertexBufferType, vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, true, mortonSettings);
vertexOffsetsOffset += numVertices;
}
for (let k = 0; k < rings.length; k++) {
const numRingVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];
ringOffsetsCounter++;
if (nonOffset) {
rings[k] = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numRingVertices, true);
vertexBufferOffset += numRingVertices * 2;
} else {
rings[k] = decodeDictionaryEncodedVertices(geometryVector.vertexBufferType, vertexBuffer, vertexOffsets, vertexOffsetsOffset, numRingVertices, true, mortonSettings);
vertexOffsetsOffset += numRingVertices;
}
}
polygons[j] = [shell].concat(rings);
}
geometries[geometryCounter++] = polygons.flat();
}
break;
default: throw new Error(`The specified geometry type (${geometryType}) is currently not supported.`);
}
}
return geometries;
}
function decodeDictionaryEncodedVertices(vertexBufferType, vertexBuffer, vertexOffsets, vertexOffset, numVertices, isRing, mortonSettings) {
if (vertexBufferType === VertexBufferType.MORTON) return decodeMortonDictionaryEncodedVertices(vertexBuffer, vertexOffsets, vertexOffset, numVertices, isRing, mortonSettings);
else return decodeVec2DictionaryEncodedVertices(vertexBuffer, vertexOffsets, vertexOffset, numVertices, isRing);
}
function getLineStringOrRing(vertexBuffer, startIndex, numVertices, isRing) {
const vertices = new Array(isRing ? numVertices + 1 : numVertices);
for (let i = 0; i < numVertices * 2; i += 2) {
const x = vertexBuffer[startIndex + i];
const y = vertexBuffer[startIndex + i + 1];
vertices[i / 2] = new Point(x, y);
}
if (isRing) vertices[vertices.length - 1] = vertices[0];
return vertices;
}
function decodeVec2DictionaryEncodedVertices(vertexBuffer, vertexOffsets, vertexOffset, numVertices, isRing) {
const vertices = new Array(isRing ? numVertices + 1 : numVertices);
for (let i = 0; i < numVertices * 2; i += 2) {
const offset = vertexOffsets[vertexOffset + i / 2] * 2;
const x = vertexBuffer[offset];
const y = vertexBuffer[offset + 1];
vertices[i / 2] = new Point(x, y);
}
if (isRing) vertices[vertices.length - 1] = vertices[0];
return vertices;
}
function decodeMortonDictionaryEncodedVertices(vertexBuffer, vertexOffsets, vertexOffset, numVertices, isRing, mortonSettings) {
const vertices = new Array(isRing ? numVertices + 1 : numVertices);
for (let i = 0; i < numVertices; i++) {
const mortonEncodedVertex = vertexBuffer[vertexOffsets[vertexOffset + i]];
const vertex = decodeZOrderCurve(mortonEncodedVertex, mortonSettings.numBits, mortonSettings.coordinateShift);
vertices[i] = new Point(vertex.x, vertex.y);
}
if (isRing) vertices[vertices.length - 1] = vertices[0];
return vertices;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/geometryVector.js
var GeometryVector = class {
constructor(_vertexBufferType, _topologyVector, _vertexOffsets, _vertexBuffer, _mortonSettings) {
this._vertexBufferType = _vertexBufferType;
this._topologyVector = _topologyVector;
this._vertexOffsets = _vertexOffsets;
this._vertexBuffer = _vertexBuffer;
this._mortonSettings = _mortonSettings;
}
get vertexBufferType() {
return this._vertexBufferType;
}
get topologyVector() {
return this._topologyVector;
}
get vertexOffsets() {
return this._vertexOffsets;
}
get vertexBuffer() {
return this._vertexBuffer;
}
getSimpleEncodedVertex(index) {
const offset = this.vertexOffsets ? this.vertexOffsets[index] * 2 : index * 2;
return [this.vertexBuffer[offset], this.vertexBuffer[offset + 1]];
}
getVertex(index) {
if (this.vertexOffsets && this.mortonSettings) {
const vertexOffset = this.vertexOffsets[index];
const mortonEncodedVertex = this.vertexBuffer[vertexOffset];
const vertex = decodeZOrderCurve(mortonEncodedVertex, this.mortonSettings.numBits, this.mortonSettings.coordinateShift);
return [vertex.x, vertex.y];
}
const offset = this.vertexOffsets ? this.vertexOffsets[index] * 2 : index * 2;
return [this.vertexBuffer[offset], this.vertexBuffer[offset + 1]];
}
getGeometries() {
return convertGeometryVector(this);
}
get mortonSettings() {
return this._mortonSettings;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/constGeometryVector.js
function createConstGeometryVector(numGeometries, geometryType, topologyVector, vertexOffsets, vertexBuffer) {
return new ConstGeometryVector(numGeometries, geometryType, VertexBufferType.VEC_2, topologyVector, vertexOffsets, vertexBuffer);
}
function createMortonEncodedConstGeometryVector(numGeometries, geometryType, topologyVector, vertexOffsets, vertexBuffer, mortonInfo) {
return new ConstGeometryVector(numGeometries, geometryType, VertexBufferType.MORTON, topologyVector, vertexOffsets, vertexBuffer, mortonInfo);
}
var ConstGeometryVector = class extends GeometryVector {
constructor(_numGeometries, _geometryType, vertexBufferType, topologyVector, vertexOffsets, vertexBuffer, mortonSettings) {
super(vertexBufferType, topologyVector, vertexOffsets, vertexBuffer, mortonSettings);
this._numGeometries = _numGeometries;
this._geometryType = _geometryType;
}
geometryType(_index) {
return this._geometryType;
}
get numGeometries() {
return this._numGeometries;
}
containsPolygonGeometry() {
return this._geometryType === GEOMETRY_TYPE.POLYGON || this._geometryType === GEOMETRY_TYPE.MULTIPOLYGON;
}
containsSingleGeometryType() {
return true;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/flatGeometryVector.js
function createFlatGeometryVector(geometryTypes, topologyVector, vertexOffsets, vertexBuffer) {
return new FlatGeometryVector(VertexBufferType.VEC_2, geometryTypes, topologyVector, vertexOffsets, vertexBuffer);
}
function createFlatGeometryVectorMortonEncoded(geometryTypes, topologyVector, vertexOffsets, vertexBuffer, mortonInfo) {
return new FlatGeometryVector(VertexBufferType.MORTON, geometryTypes, topologyVector, vertexOffsets, vertexBuffer, mortonInfo);
}
var FlatGeometryVector = class extends GeometryVector {
constructor(vertexBufferType, _geometryTypes, topologyVector, vertexOffsets, vertexBuffer, mortonSettings) {
super(vertexBufferType, topologyVector, vertexOffsets, vertexBuffer, mortonSettings);
this._geometryTypes = _geometryTypes;
}
geometryType(index) {
return this._geometryTypes[index];
}
get numGeometries() {
return this._geometryTypes.length;
}
containsPolygonGeometry() {
for (let i = 0; i < this.numGeometries; i++) if (this.geometryType(i) === GEOMETRY_TYPE.POLYGON || this.geometryType(i) === GEOMETRY_TYPE.MULTIPOLYGON) return true;
return false;
}
containsSingleGeometryType() {
return false;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/gpuVector.js
var GpuVector = class {
constructor(_triangleOffsets, _indexBuffer, _vertexBuffer, _topologyVector) {
this._triangleOffsets = _triangleOffsets;
this._indexBuffer = _indexBuffer;
this._vertexBuffer = _vertexBuffer;
this._topologyVector = _topologyVector;
}
get triangleOffsets() {
return this._triangleOffsets;
}
get indexBuffer() {
return this._indexBuffer;
}
get vertexBuffer() {
return this._vertexBuffer;
}
get topologyVector() {
return this._topologyVector;
}
getGeometries() {
if (!this._topologyVector) throw new Error("Cannot convert GpuVector to coordinates without topology information");
const types = new Uint32Array(this.numGeometries);
for (let i = 0; i < this.numGeometries; i++) types[i] = this.geometryType(i);
return createFlatGeometryVector(types, this._topologyVector, void 0, this._vertexBuffer).getGeometries();
}
[Symbol.iterator]() {
return null;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/constGpuVector.js
function createConstGpuVector(numGeometries, geometryType, triangleOffsets, indexBuffer, vertexBuffer, topologyVector) {
return new ConstGpuVector(numGeometries, geometryType, triangleOffsets, indexBuffer, vertexBuffer, topologyVector);
}
var ConstGpuVector = class extends GpuVector {
constructor(_numGeometries, _geometryType, triangleOffsets, indexBuffer, vertexBuffer, topologyVector) {
super(triangleOffsets, indexBuffer, vertexBuffer, topologyVector);
this._numGeometries = _numGeometries;
this._geometryType = _geometryType;
}
geometryType(_index) {
return this._geometryType;
}
get numGeometries() {
return this._numGeometries;
}
containsSingleGeometryType() {
return true;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/geometry/flatGpuVector.js
function createFlatGpuVector(geometryTypes, triangleOffsets, indexBuffer, vertexBuffer, topologyVector) {
return new FlatGpuVector(geometryTypes, triangleOffsets, indexBuffer, vertexBuffer, topologyVector);
}
var FlatGpuVector = class extends GpuVector {
constructor(_geometryTypes, triangleOffsets, indexBuffer, vertexBuffer, topologyVector) {
super(triangleOffsets, indexBuffer, vertexBuffer, topologyVector);
this._geometryTypes = _geometryTypes;
}
geometryType(index) {
return this._geometryTypes[index];
}
get numGeometries() {
return this._geometryTypes.length;
}
containsSingleGeometryType() {
return false;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/geometryDecoder.js
function decodeGeometryColumn(tile, numStreams, offset, numFeatures, scalingData) {
const geometryTypeMetadata = decodeStreamMetadata(tile, offset);
const geometryTypesVectorType = getVectorType(geometryTypeMetadata, numFeatures, tile, offset);
let vertexOffsets;
let vertexBuffer;
let mortonSettings;
let indexBuffer;
if (geometryTypesVectorType === VectorType.CONST) {
const geometryType = decodeUnsignedConstInt32Stream(tile, offset, geometryTypeMetadata);
let geometryOffsets;
let partOffsets;
let ringOffsets;
let triangleOffsets;
for (let i = 0; i < numStreams - 1; i++) {
const geometryStreamMetadata = decodeStreamMetadata(tile, offset);
switch (geometryStreamMetadata.physicalStreamType) {
case PhysicalStreamType.LENGTH:
switch (geometryStreamMetadata.logicalStreamType.lengthType) {
case LengthType.GEOMETRIES:
geometryOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
break;
case LengthType.PARTS:
partOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
break;
case LengthType.RINGS:
ringOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
break;
case LengthType.TRIANGLES: triangleOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
}
break;
case PhysicalStreamType.OFFSET:
switch (geometryStreamMetadata.logicalStreamType.offsetType) {
case OffsetType.VERTEX:
vertexOffsets = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case OffsetType.INDEX: indexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
}
break;
case PhysicalStreamType.DATA: if (DictionaryType.VERTEX === geometryStreamMetadata.logicalStreamType.dictionaryType) vertexBuffer = decodeSignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
else {
const mortonMetadata = geometryStreamMetadata;
mortonSettings = {
numBits: mortonMetadata.numBits,
coordinateShift: mortonMetadata.coordinateShift
};
vertexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
}
}
}
if (indexBuffer) {
if (geometryOffsets !== void 0 || partOffsets !== void 0) return createConstGpuVector(numFeatures, geometryType, triangleOffsets, indexBuffer, vertexBuffer, {
geometryOffsets,
partOffsets,
ringOffsets
});
return createConstGpuVector(numFeatures, geometryType, triangleOffsets, indexBuffer, vertexBuffer);
}
return mortonSettings === void 0 ? createConstGeometryVector(numFeatures, geometryType, {
geometryOffsets,
partOffsets,
ringOffsets
}, vertexOffsets, vertexBuffer) : createMortonEncodedConstGeometryVector(numFeatures, geometryType, {
geometryOffsets,
partOffsets,
ringOffsets
}, vertexOffsets, vertexBuffer, mortonSettings);
}
const geometryTypeVector = decodeUnsignedInt32Stream(tile, offset, geometryTypeMetadata);
let geometryLengths;
let partLengths;
let ringLengths;
let triangleOffsets;
for (let i = 0; i < numStreams - 1; i++) {
const geometryStreamMetadata = decodeStreamMetadata(tile, offset);
switch (geometryStreamMetadata.physicalStreamType) {
case PhysicalStreamType.LENGTH:
switch (geometryStreamMetadata.logicalStreamType.lengthType) {
case LengthType.GEOMETRIES:
geometryLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case LengthType.PARTS:
partLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case LengthType.RINGS:
ringLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case LengthType.TRIANGLES: triangleOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);
}
break;
case PhysicalStreamType.OFFSET:
switch (geometryStreamMetadata.logicalStreamType.offsetType) {
case OffsetType.VERTEX:
vertexOffsets = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
break;
case OffsetType.INDEX: indexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);
}
break;
case PhysicalStreamType.DATA: if (DictionaryType.VERTEX === geometryStreamMetadata.logicalStreamType.dictionaryType) vertexBuffer = decodeSignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
else {
const mortonMetadata = geometryStreamMetadata;
mortonSettings = {
numBits: mortonMetadata.numBits,
coordinateShift: mortonMetadata.coordinateShift
};
vertexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);
}
}
}
let geometryOffsets;
let partOffsets;
let ringOffsets;
if (geometryLengths) {
geometryOffsets = decodeRootLengthStream(geometryTypeVector, geometryLengths, 2);
if (partLengths && ringLengths) {
partOffsets = decodeLevel1LengthStream(geometryTypeVector, geometryOffsets, partLengths, false);
ringOffsets = decodeLevel2LengthStream(geometryTypeVector, geometryOffsets, partOffsets, ringLengths);
} else if (partLengths) partOffsets = decodeLevel1WithoutRingBufferLengthStream(geometryTypeVector, geometryOffsets, partLengths);
} else if (partLengths && ringLengths) {
partOffsets = decodeRootLengthStream(geometryTypeVector, partLengths, 1);
ringOffsets = decodeLevel1LengthStream(geometryTypeVector, partOffsets, ringLengths, true);
} else if (partLengths) partOffsets = decodeRootLengthStream(geometryTypeVector, partLengths, 0);
if (indexBuffer && !partOffsets) return createFlatGpuVector(geometryTypeVector, triangleOffsets, indexBuffer, vertexBuffer);
if (indexBuffer) return createFlatGpuVector(geometryTypeVector, triangleOffsets, indexBuffer, vertexBuffer, {
geometryOffsets,
partOffsets,
ringOffsets
});
return mortonSettings === void 0 ? createFlatGeometryVector(geometryTypeVector, {
geometryOffsets,
partOffsets,
ringOffsets
}, vertexOffsets, vertexBuffer) : createFlatGeometryVectorMortonEncoded(geometryTypeVector, {
geometryOffsets,
partOffsets,
ringOffsets
}, vertexOffsets, vertexBuffer, mortonSettings);
}
function decodeRootLengthStream(geometryTypes, rootLengthStream, bufferId) {
const rootBufferOffsets = new Uint32Array(geometryTypes.length + 1);
let previousOffset = 0;
rootBufferOffsets[0] = previousOffset;
let rootLengthCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) previousOffset = rootBufferOffsets[i + 1] = previousOffset + (geometryTypes[i] > bufferId ? rootLengthStream[rootLengthCounter++] : 1);
return rootBufferOffsets;
}
function decodeLevel1LengthStream(geometryTypes, rootOffsetBuffer, level1LengthBuffer, isLineStringPresent) {
const level1BufferOffsets = new Uint32Array(rootOffsetBuffer[rootOffsetBuffer.length - 1] + 1);
let previousOffset = 0;
level1BufferOffsets[0] = previousOffset;
let level1BufferCounter = 1;
let level1LengthBufferCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) {
const geometryType = geometryTypes[i];
const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];
if (geometryType === 5 || geometryType === 2 || isLineStringPresent && (geometryType === 4 || geometryType === 1)) for (let j = 0; j < numGeometries; j++) previousOffset = level1BufferOffsets[level1BufferCounter++] = previousOffset + level1LengthBuffer[level1LengthBufferCounter++];
else for (let j = 0; j < numGeometries; j++) level1BufferOffsets[level1BufferCounter++] = ++previousOffset;
}
return level1BufferOffsets;
}
function decodeLevel1WithoutRingBufferLengthStream(geometryTypes, rootOffsetBuffer, level1LengthBuffer) {
const level1BufferOffsets = new Uint32Array(rootOffsetBuffer[rootOffsetBuffer.length - 1] + 1);
let previousOffset = 0;
level1BufferOffsets[0] = previousOffset;
let level1OffsetBufferCounter = 1;
let level1LengthCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) {
const geometryType = geometryTypes[i];
const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];
if (geometryType === 4 || geometryType === 1) for (let j = 0; j < numGeometries; j++) previousOffset = level1BufferOffsets[level1OffsetBufferCounter++] = previousOffset + level1LengthBuffer[level1LengthCounter++];
else for (let j = 0; j < numGeometries; j++) level1BufferOffsets[level1OffsetBufferCounter++] = ++previousOffset;
}
return level1BufferOffsets;
}
function decodeLevel2LengthStream(geometryTypes, rootOffsetBuffer, level1OffsetBuffer, level2LengthBuffer) {
const level2BufferOffsets = new Uint32Array(level1OffsetBuffer[level1OffsetBuffer.length - 1] + 1);
let previousOffset = 0;
level2BufferOffsets[0] = previousOffset;
let level1OffsetBufferCounter = 1;
let level2OffsetBufferCounter = 1;
let level2LengthBufferCounter = 0;
for (let i = 0; i < geometryTypes.length; i++) {
const geometryType = geometryTypes[i];
const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];
if (geometryType !== 0 && geometryType !== 3) for (let j = 0; j < numGeometries; j++) {
const numParts = level1OffsetBuffer[level1OffsetBufferCounter] - level1OffsetBuffer[level1OffsetBufferCounter - 1];
level1OffsetBufferCounter++;
for (let k = 0; k < numParts; k++) previousOffset = level2BufferOffsets[level2OffsetBufferCounter++] = previousOffset + level2LengthBuffer[level2LengthBufferCounter++];
}
else for (let j = 0; j < numGeometries; j++) {
level2BufferOffsets[level2OffsetBufferCounter++] = ++previousOffset;
level1OffsetBufferCounter++;
}
}
return level2BufferOffsets;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/booleanFlatVector.js
var BooleanFlatVector = class extends Vector {
constructor(name, dataVector, sizeOrNullabilityBuffer) {
super(name, dataVector.getBuffer(), sizeOrNullabilityBuffer);
this.dataVector = dataVector;
}
getValueFromBuffer(index) {
return this.dataVector.get(index);
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/floatFlatVector.js
var FloatFlatVector = class extends FixedSizeVector {
getValueFromBuffer(index) {
return this.dataBuffer[index];
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/constant/int64ConstVector.js
var Int64ConstVector = class extends Vector {
constructor(name, value, sizeOrNullabilityBuffer, isSigned) {
super(name, isSigned ? BigInt64Array.of(value) : BigUint64Array.of(value), sizeOrNullabilityBuffer);
}
getValueFromBuffer(_index) {
return this.dataBuffer[0];
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/variableSizeVector.js
var VariableSizeVector = class extends Vector {
constructor(name, offsetBuffer, dataBuffer, sizeOrNullabilityBuffer) {
super(name, dataBuffer, sizeOrNullabilityBuffer);
this.offsetBuffer = offsetBuffer;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/stringFlatVector.js
var StringFlatVector = class extends VariableSizeVector {
constructor(name, offsetBuffer, dataBuffer, nullabilityBuffer) {
super(name, offsetBuffer, dataBuffer, nullabilityBuffer ?? offsetBuffer.length - 1);
}
getValueFromBuffer(index) {
const start = this.offsetBuffer[index];
const end = this.offsetBuffer[index + 1];
return decodeString$2(this.dataBuffer, start, end);
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/dictionary/stringDictionaryVector.js
var StringDictionaryVector = class extends VariableSizeVector {
constructor(name, indexBuffer, offsetBuffer, dictionaryBuffer, nullabilityBuffer) {
super(name, offsetBuffer, dictionaryBuffer, nullabilityBuffer ?? indexBuffer.length);
this.indexBuffer = indexBuffer;
this.indexBuffer = indexBuffer;
}
getValueFromBuffer(index) {
const offset = this.indexBuffer[index];
const start = this.offsetBuffer[offset];
const end = this.offsetBuffer[offset + 1];
return decodeString$2(this.dataBuffer, start, end);
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/fsstDecoder.js
/**
* Calculates the exact output size before decoding. This allows one final
* `Uint8Array` allocation and avoids growing a JavaScript number array and
* copying it into a typed array afterward. Traversing the compressed data
* twice is always worthwhile here because it avoids those larger temporary
* allocations.
*/
function getDecodedLength(symbolLengths, compressedData) {
let decodedLength = 0;
for (let i = 0; i < compressedData.length; i++) {
const symbolIndex = compressedData[i];
if (symbolIndex === 255) {
decodedLength++;
i++;
} else decodedLength += symbolLengths[symbolIndex];
}
return decodedLength;
}
/**
* Decode FSST compressed data
*
* @param symbols Array of symbols, where each symbol can be between 1 and 8 bytes
* @param symbolLengths Array of symbol lengths, length of each symbol in symbols array
* @param compressedData FSST Compressed data, where each entry is an index to the symbols array
* @returns Decoded data as Uint8Array
*/
function decodeFsst(symbols, symbolLengths, compressedData) {
const symbolOffsets = new Uint32Array(symbolLengths.length);
for (let i = 1; i < symbolLengths.length; i++) symbolOffsets[i] = symbolOffsets[i - 1] + symbolLengths[i - 1];
const decodedData = new Uint8Array(getDecodedLength(symbolLengths, compressedData));
let decodedOffset = 0;
for (let i = 0; i < compressedData.length; i++) {
const symbolIndex = compressedData[i];
if (symbolIndex === 255) {
i++;
decodedData[decodedOffset++] = compressedData[i];
} else {
let symbolLength = symbolLengths[symbolIndex];
let symbolOffset = symbolOffsets[symbolIndex];
while (symbolLength-- > 0) decodedData[decodedOffset++] = symbols[symbolOffset++];
}
}
return decodedData;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/fsst-dictionary/stringFsstDictionaryVector.js
var StringFsstDictionaryVector = class extends VariableSizeVector {
constructor(name, indexBuffer, offsetBuffer, dictionaryBuffer, symbolOffsetBuffer, symbolTableBuffer, nullabilityBuffer, sharedDictionaryCache) {
super(name, offsetBuffer, dictionaryBuffer, nullabilityBuffer ?? indexBuffer.length);
this.indexBuffer = indexBuffer;
this.symbolOffsetBuffer = symbolOffsetBuffer;
this.symbolTableBuffer = symbolTableBuffer;
this.sharedDictionaryCache = sharedDictionaryCache;
}
getValueFromBuffer(index) {
if (this.decodedDictionary == null) {
this.decodedDictionary = this.sharedDictionaryCache?.decodedDictionary;
if (this.decodedDictionary == null) {
this.decodedDictionary = this.decodeDictionary();
if (this.sharedDictionaryCache) this.sharedDictionaryCache.decodedDictionary = this.decodedDictionary;
}
}
const offset = this.indexBuffer[index];
const start = this.offsetBuffer[offset];
const end = this.offsetBuffer[offset + 1];
return decodeString$2(this.decodedDictionary, start, end);
}
decodeDictionary() {
if (this.symbolLengthBuffer == null) this.symbolLengthBuffer = this.offsetToLengthBuffer(this.symbolOffsetBuffer);
return decodeFsst(this.symbolTableBuffer, this.symbolLengthBuffer, this.dataBuffer);
}
offsetToLengthBuffer(offsetBuffer) {
const lengthBuffer = new Uint32Array(offsetBuffer.length - 1);
let previousOffset = offsetBuffer[0];
for (let i = 1; i < offsetBuffer.length; i++) {
const offset = offsetBuffer[i];
lengthBuffer[i - 1] = offset - previousOffset;
previousOffset = offset;
}
return lengthBuffer;
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/stringDecoder.js
function decodeString$1(name, data, offset, numStreams, bitVector) {
let dictionaryLengthStream;
let offsetStream;
let dictionaryStream;
let symbolLengthStream;
let symbolTableStream;
let nullabilityBuffer = bitVector;
let plainLengthStream;
let plainDataStream;
for (let i = 0; i < numStreams; i++) {
const streamMetadata = decodeStreamMetadata(data, offset);
switch (streamMetadata.physicalStreamType) {
case PhysicalStreamType.PRESENT: {
const presentStream = new BitVector(decodeBooleanRle(data, streamMetadata.numValues, streamMetadata.byteLength, offset), streamMetadata.numValues);
nullabilityBuffer = bitVector ?? presentStream;
break;
}
case PhysicalStreamType.OFFSET:
offsetStream = decodeUnsignedInt32Stream(data, offset, streamMetadata, void 0, nullabilityBuffer);
break;
case PhysicalStreamType.LENGTH: {
const lengthStream = decodeLengthStreamToOffsetBuffer(data, offset, streamMetadata);
if (LengthType.DICTIONARY === streamMetadata.logicalStreamType.lengthType) dictionaryLengthStream = lengthStream;
else if (LengthType.SYMBOL === streamMetadata.logicalStreamType.lengthType) symbolLengthStream = lengthStream;
else plainLengthStream = lengthStream;
break;
}
case PhysicalStreamType.DATA: {
const dataStream = data.subarray(offset.get(), offset.get() + streamMetadata.byteLength);
offset.add(streamMetadata.byteLength);
const dictType = streamMetadata.logicalStreamType.dictionaryType;
if (DictionaryType.FSST === dictType) symbolTableStream = dataStream;
else if (DictionaryType.SINGLE === dictType || DictionaryType.SHARED === dictType) dictionaryStream = dataStream;
else if (DictionaryType.NONE === dictType) plainDataStream = dataStream;
break;
}
}
}
return decodeFsstDictionaryVector(name, symbolTableStream, offsetStream, dictionaryLengthStream, dictionaryStream, symbolLengthStream, nullabilityBuffer) ?? decodeDictionaryVector(name, dictionaryStream, offsetStream, dictionaryLengthStream, nullabilityBuffer) ?? decodePlainStringVector(name, plainLengthStream, plainDataStream, offsetStream, nullabilityBuffer);
}
function decodeFsstDictionaryVector(name, symbolTableStream, offsetStream, dictionaryLengthStream, dictionaryStream, symbolLengthStream, nullabilityBuffer) {
if (!symbolTableStream) return;
if (!offsetStream || !dictionaryLengthStream || !dictionaryStream || !symbolLengthStream) throw new Error(`Incomplete FSST dictionary string column "${name}"`);
return new StringFsstDictionaryVector(name, offsetStream, dictionaryLengthStream, dictionaryStream, symbolLengthStream, symbolTableStream, nullabilityBuffer);
}
function decodeDictionaryVector(name, dictionaryStream, offsetStream, dictionaryLengthStream, nullabilityBuffer) {
if (!dictionaryStream) return;
if (!offsetStream || !dictionaryLengthStream) throw new Error(`Incomplete dictionary string column "${name}"`);
return nullabilityBuffer ? new StringDictionaryVector(name, offsetStream, dictionaryLengthStream, dictionaryStream, nullabilityBuffer) : new StringDictionaryVector(name, offsetStream, dictionaryLengthStream, dictionaryStream);
}
function decodePlainStringVector(name, plainLengthStream, plainDataStream, offsetStream, nullabilityBuffer) {
if (!plainLengthStream || !plainDataStream) return;
if (offsetStream) return nullabilityBuffer ? new StringDictionaryVector(name, offsetStream, plainLengthStream, plainDataStream, nullabilityBuffer) : new StringDictionaryVector(name, offsetStream, plainLengthStream, plainDataStream);
if (nullabilityBuffer && nullabilityBuffer.size() !== plainLengthStream.length - 1) {
const sparseOffsetStream = new Uint32Array(nullabilityBuffer.size());
let valueIndex = 0;
for (let i = 0; i < nullabilityBuffer.size(); i++) if (nullabilityBuffer.get(i)) sparseOffsetStream[i] = valueIndex++;
else sparseOffsetStream[i] = 0;
return new StringDictionaryVector(name, sparseOffsetStream, plainLengthStream, plainDataStream, nullabilityBuffer);
}
return nullabilityBuffer ? new StringFlatVector(name, plainLengthStream, plainDataStream, nullabilityBuffer) : new StringFlatVector(name, plainLengthStream, plainDataStream);
}
function decodeSharedDictionary(data, offset, column, propertyColumnNames) {
let dictionaryOffsetBuffer;
let dictionaryBuffer;
let symbolOffsetBuffer;
let symbolTableBuffer;
let dictionaryStreamDecoded = false;
while (!dictionaryStreamDecoded) {
const streamMetadata = decodeStreamMetadata(data, offset);
switch (streamMetadata.physicalStreamType) {
case PhysicalStreamType.LENGTH:
if (LengthType.DICTIONARY === streamMetadata.logicalStreamType.lengthType) dictionaryOffsetBuffer = decodeLengthStreamToOffsetBuffer(data, offset, streamMetadata);
else symbolOffsetBuffer = decodeLengthStreamToOffsetBuffer(data, offset, streamMetadata);
break;
case PhysicalStreamType.DATA:
if (DictionaryType.SINGLE === streamMetadata.logicalStreamType.dictionaryType || DictionaryType.SHARED === streamMetadata.logicalStreamType.dictionaryType) {
dictionaryBuffer = data.subarray(offset.get(), offset.get() + streamMetadata.byteLength);
dictionaryStreamDecoded = true;
} else symbolTableBuffer = data.subarray(offset.get(), offset.get() + streamMetadata.byteLength);
offset.add(streamMetadata.byteLength);
}
}
if (column.type !== "complexType") throw new Error(`Shared dictionary column ${column.name} must be a complex (struct) column.`);
if (!dictionaryOffsetBuffer || !dictionaryBuffer) throw new Error(`Incomplete shared dictionary for column "${column.name}"`);
const childFields = column.complexType.children;
const stringDictionaryVectors = [];
/** Shared by every FSST child-column vector in this SharedDict and populated on first access. */
const sharedDictionaryCache = symbolTableBuffer ? {} : void 0;
let i = 0;
for (const childField of childFields) {
const numStreams = decodeVarintInt32(data, offset, 1)[0];
if (numStreams === 0) continue;
const columnName = childField.name ? `${column.name}${childField.name}` : column.name;
if (propertyColumnNames) {
if (!propertyColumnNames.has(columnName)) {
skipColumn(numStreams, data, offset);
continue;
}
}
if (childField.type !== "scalarField" || childField.scalarField.physicalType !== ScalarType.STRING) throw new Error("Currently only scalar string fields are implemented for a struct.");
if (numStreams > 1 && !childField.nullable || numStreams === 1 && childField.nullable) throw new Error(`The number of streams for the child field ${childField.name} does not match its nullability. nullibilty: ${childField.nullable}, numStreams: ${numStreams}`);
let presentStreamBitVector;
if (childField.nullable) {
const presentStreamMetadata = decodeStreamMetadata(data, offset);
presentStreamBitVector = new BitVector(decodeBooleanRle(data, presentStreamMetadata.numValues, presentStreamMetadata.byteLength, offset), presentStreamMetadata.numValues);
}
const offsetStream = decodeUnsignedInt32Stream(data, offset, decodeStreamMetadata(data, offset), void 0, presentStreamBitVector);
if (symbolTableBuffer) {
if (!symbolOffsetBuffer) throw new Error(`Incomplete shared FSST dictionary for column "${columnName}"`);
stringDictionaryVectors[i++] = new StringFsstDictionaryVector(columnName, offsetStream, dictionaryOffsetBuffer, dictionaryBuffer, symbolOffsetBuffer, symbolTableBuffer, presentStreamBitVector, sharedDictionaryCache);
} else stringDictionaryVectors[i++] = new StringDictionaryVector(columnName, offsetStream, dictionaryOffsetBuffer, dictionaryBuffer, presentStreamBitVector);
}
return stringDictionaryVectors;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/vector/flat/objectFlatVector.js
/**
* Holds already-decoded values of arbitrary shape, one per feature.
*
* Unlike the other vectors there is no packed buffer to index into: nested property (MAP) columns
* decode to plain JavaScript maps, arrays and scalars, so the values are kept as-is. Features
* without a value are marked absent in the nullability buffer, so `has` reports them as missing and
* `getValue` returns `null`.
*/
var ObjectFlatVector = class extends Vector {
constructor(name, values, nullabilityBuffer) {
super(name, /* @__PURE__ */ new Uint8Array(0), nullabilityBuffer ?? values.length);
this.values = values;
}
getValueFromBuffer(index) {
return this.values[index];
}
};
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/mapMask.js
/**
* Bitmask written ahead of a nested property (MAP) column, marking which optional streams follow
* the mandatory length stream. Only one of INT32/INT64 and one of UINT32/UINT64 is ever set: the
* encoder picks the narrower width that fits every value.
*/
var MapMask;
(function(MapMask) {
MapMask[MapMask["STRING"] = 1] = "STRING";
MapMask[MapMask["INT32"] = 2] = "INT32";
MapMask[MapMask["UINT32"] = 4] = "UINT32";
MapMask[MapMask["INT64"] = 8] = "INT64";
MapMask[MapMask["UINT64"] = 16] = "UINT64";
MapMask[MapMask["FLOAT"] = 32] = "FLOAT";
MapMask[MapMask["DOUBLE"] = 64] = "DOUBLE";
MapMask[MapMask["PRESENCE"] = 128] = "PRESENCE";
})(MapMask || (MapMask = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tile/mapControlValue.js
/**
* Tokens in the data stream of a nested property (MAP) column. Values below `COUNT` describe the
* structure; anything else is an index into the combined dictionary, offset by `COUNT`. Booleans are
* encoded directly as tokens rather than being added to a dictionary.
*/
var MapControlValue;
(function(MapControlValue) {
MapControlValue[MapControlValue["FALSE"] = 0] = "FALSE";
MapControlValue[MapControlValue["TRUE"] = 1] = "TRUE";
/** A nested map follows: this token, the payload length including these two tokens, the payload. */
MapControlValue[MapControlValue["START_MAP"] = 2] = "START_MAP";
/** A list follows, laid out the same way as START_MAP. */
MapControlValue[MapControlValue["START_LIST"] = 3] = "START_LIST";
/** Number of reserved tokens, i.e. the first dictionary index. */
MapControlValue[MapControlValue["COUNT"] = 4] = "COUNT";
})(MapControlValue || (MapControlValue = {}));
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/mapPropertyDecoder.js
/**
* Decodes a nested property (MAP) column into one vector per child column.
*
* The column is stored as a length stream (values per feature), a dictionary stream per value type
* present, an optional presence stream, and a data stream of dictionary indices interleaved with
* the control tokens that describe the map/list structure.
*
* Ported from the Java reference implementation (`MapPropertyDecoder`).
*/
function decodeMapPropertyColumn(data, offset, columnMetadata, numStreams) {
const columnNames = getMapColumnNames(columnMetadata);
if (numStreams === 0) return columnNames.map((name) => new ObjectFlatVector(name, []));
const streams = decodeMapStreams(data, offset, numStreams);
const featureCount = (streams.presentStream ? streams.presentCount : streams.lengthStream.length) / columnNames.length;
const vectors = [];
let countsCursor = 0;
let valuesCursor = 0;
for (let childIndex = 0; childIndex < columnNames.length; childIndex++) {
const child = decodeChildColumn(streams, childIndex, featureCount, countsCursor, valuesCursor);
vectors.push(new ObjectFlatVector(columnNames[childIndex], child.value, child.nullabilityBuffer));
countsCursor = child.countsEnd;
valuesCursor = child.valuesEnd;
}
return vectors;
}
/**
* A single map column carries its own name. A shared column carries one child per sibling, whose
* full name is the parent name followed by the child name.
*/
function getMapColumnNames(columnMetadata) {
const children = columnMetadata.type === "complexType" ? columnMetadata.complexType.children : void 0;
if (!children || children.length === 0) return [columnMetadata.name];
return children.map((child) => columnMetadata.name + (child.name ?? ""));
}
/** Reads the stream mask and every stream it announces, in the order the encoder wrote them. */
function decodeMapStreams(data, offset, numStreams) {
const dictionaryMask = data[offset.get()];
offset.add(1);
const lengthStream = decodeUnsignedInt32Stream(data, offset, decodeStreamMetadata(data, offset));
let remainingStreams = numStreams - 1;
const dictionary = [];
if (dictionaryMask & MapMask.STRING) remainingStreams -= decodeStringDictionary(data, offset, dictionary);
remainingStreams -= decodeIntegerDictionaries(data, offset, dictionaryMask, dictionary);
remainingStreams -= decodeFloatingPointDictionaries(data, offset, dictionaryMask, dictionary);
let presentStream;
let presentCount = 0;
if (dictionaryMask & MapMask.PRESENCE) {
const presence = decodePresenceStream(data, offset);
presentStream = presence.value;
presentCount = presence.count;
remainingStreams--;
}
let flattenedValues = /* @__PURE__ */ new Uint32Array(0);
if (remainingStreams > 0) {
flattenedValues = decodeUnsignedInt32Stream(data, offset, decodeStreamMetadata(data, offset));
remainingStreams--;
}
if (remainingStreams !== 0) throw new Error(`Unexpected number of remaining streams while decoding map column: ${remainingStreams}`);
return {
lengthStream,
dictionary,
presentStream,
presentCount,
flattenedValues
};
}
/** @returns the number of streams consumed, which the string encoding decides for itself. */
function decodeStringDictionary(data, offset, dictionary) {
const stringStreamCount = data[offset.get()];
offset.add(1);
const strings = decodeString$1("", data, offset, stringStreamCount);
if (strings) for (let i = 0; i < strings.size; i++) dictionary.push(strings.getValue(i));
return stringStreamCount;
}
/**
* Signed and unsigned integers each get at most one stream, whose width the encoder chose to fit
* the widest value.
*
* @returns the number of streams consumed.
*/
function decodeIntegerDictionaries(data, offset, dictionaryMask, dictionary) {
let consumed = 0;
if (dictionaryMask & MapMask.INT32) {
pushAll(dictionary, decodeSignedInt32Stream(data, offset, decodeStreamMetadata(data, offset)));
consumed++;
} else if (dictionaryMask & MapMask.INT64) {
pushAll(dictionary, decodeSignedInt64Stream(data, offset, decodeStreamMetadata(data, offset)));
consumed++;
}
if (dictionaryMask & MapMask.UINT32) {
pushAll(dictionary, decodeUnsignedInt32Stream(data, offset, decodeStreamMetadata(data, offset)));
consumed++;
} else if (dictionaryMask & MapMask.UINT64) {
pushAll(dictionary, decodeUnsignedInt64Stream(data, offset, decodeStreamMetadata(data, offset)));
consumed++;
}
return consumed;
}
/** @returns the number of streams consumed. */
function decodeFloatingPointDictionaries(data, offset, dictionaryMask, dictionary) {
let consumed = 0;
if (dictionaryMask & MapMask.FLOAT) {
pushAll(dictionary, decodeFloatsLE(data, offset, decodeStreamMetadata(data, offset).numValues));
consumed++;
}
if (dictionaryMask & MapMask.DOUBLE) {
pushAll(dictionary, decodeDoublesLE(data, offset, decodeStreamMetadata(data, offset).numValues));
consumed++;
}
return consumed;
}
function decodePresenceStream(data, offset) {
const streamMetadata = decodeStreamMetadata(data, offset);
if (streamMetadata.physicalStreamType !== PhysicalStreamType.PRESENT) throw new Error(`Expected PRESENT stream for map column but found: ${streamMetadata.physicalStreamType}`);
const count = streamMetadata.numValues;
const streamDataStart = offset.get();
const value = new BitVector(decodeBooleanRle(data, count, streamMetadata.byteLength, offset), count);
offset.set(streamDataStart + streamMetadata.byteLength);
return {
value,
count
};
}
/**
* Decodes one child column's per-feature values.
*
* Lengths, presence bits and tokens are all laid out child-major, so each child picks up where the
* previous one left off.
*/
function decodeChildColumn(streams, childIndex, featureCount, countsCursor, valuesCursor) {
const { lengthStream, flattenedValues, presentStream, dictionary } = streams;
const presentOffset = childIndex * featureCount;
let presentInChild = featureCount;
let nullabilityBuffer;
if (presentStream) {
nullabilityBuffer = new BitVector(new Uint8Array(Math.ceil(featureCount / 8)), featureCount);
presentInChild = 0;
for (let i = 0; i < featureCount; i++) if (presentStream.get(presentOffset + i)) {
nullabilityBuffer.set(i, true);
presentInChild++;
}
}
const countsEnd = countsCursor + presentInChild;
if (countsEnd > lengthStream.length) throw new Error("Merged map counts underflow while decoding child streams");
const value = new Array(featureCount);
let countCursor = countsCursor;
let flattenedIndex = valuesCursor;
for (let featureIndex = 0; featureIndex < featureCount; featureIndex++) {
if (presentStream && !presentStream.get(presentOffset + featureIndex)) {
value[featureIndex] = null;
continue;
}
const endIndex = flattenedIndex + lengthStream[countCursor++];
if (endIndex > flattenedValues.length) throw new Error("Map value stream underflow while decoding feature payload");
const decoded = decodeFeatureValue(flattenedValues, flattenedIndex, endIndex, dictionary);
value[featureIndex] = decoded.value;
flattenedIndex = decoded.nextIndex;
}
let childValueCount = 0;
for (let i = countsCursor; i < countsEnd; i++) childValueCount += lengthStream[i];
const valuesEnd = valuesCursor + childValueCount;
if (flattenedIndex !== valuesEnd) throw new Error("Unused flattened map values remain after decode");
return {
value,
nullabilityBuffer,
countsEnd,
valuesEnd
};
}
/**
* A feature's payload is a bare sequence of map entries, unless it is a single token — a root-level
* scalar — or opens with a list token. Those two shapes are what distinguish it from map entries.
*/
function decodeFeatureValue(flattenedValues, startIndex, endIndex, dictionary) {
if (endIndex - startIndex === 1 || flattenedValues[startIndex] === MapControlValue.START_LIST) return decodeValue(flattenedValues, startIndex, endIndex, dictionary);
return decodeMapEntries(flattenedValues, startIndex, endIndex, dictionary);
}
function decodeMapEntries(flattenedValues, startIndex, endIndex, dictionary) {
const value = Object.create(null);
let index = startIndex;
while (index < endIndex) {
const key = decodeScalarByIndex(flattenedValues[index++], dictionary);
if (typeof key !== "string") throw new Error(`Map key dictionary index does not resolve to a string: ${key}`);
const decoded = decodeValue(flattenedValues, index, endIndex, dictionary);
value[key] = decoded.value;
index = decoded.nextIndex;
}
return {
value,
nextIndex: index
};
}
function decodeValue(flattenedValues, startIndex, endIndex, dictionary) {
if (startIndex >= endIndex) throw new Error("Unexpected end of map value stream");
const token = flattenedValues[startIndex];
if (token === MapControlValue.FALSE) return {
value: false,
nextIndex: startIndex + 1
};
if (token === MapControlValue.TRUE) return {
value: true,
nextIndex: startIndex + 1
};
if (token === MapControlValue.START_MAP) {
const valueEndIndex = decodeNestedPayloadEnd(flattenedValues, startIndex, endIndex);
return {
value: decodeMapEntries(flattenedValues, startIndex + 2, valueEndIndex, dictionary).value,
nextIndex: valueEndIndex
};
}
if (token === MapControlValue.START_LIST) {
const valueEndIndex = decodeNestedPayloadEnd(flattenedValues, startIndex, endIndex);
const value = [];
let index = startIndex + 2;
while (index < valueEndIndex) {
const nested = decodeValue(flattenedValues, index, valueEndIndex, dictionary);
value.push(nested.value);
index = nested.nextIndex;
}
return {
value,
nextIndex: valueEndIndex
};
}
return {
value: decodeScalarByIndex(token, dictionary),
nextIndex: startIndex + 1
};
}
/**
* Reads the length prefix of a nested payload and returns where it ends, the counterpart of
* `encodeNestedPayloadLength`. The length covers the two header tokens as well.
*/
function decodeNestedPayloadEnd(flattenedValues, startIndex, endIndex) {
if (startIndex + 1 >= endIndex) throw new Error("Missing length for nested map/list payload");
const encodedLength = flattenedValues[startIndex + 1];
if (encodedLength < 2) throw new Error(`Invalid nested payload length: ${encodedLength}`);
const valueEndIndex = startIndex + encodedLength;
if (valueEndIndex > endIndex) throw new Error("Nested payload exceeds containing payload bounds");
return valueEndIndex;
}
function decodeScalarByIndex(token, dictionary) {
const dictionaryIndex = token - MapControlValue.COUNT;
if (dictionaryIndex < 0 || dictionaryIndex >= dictionary.length) throw new Error(`Scalar dictionary index out of range: ${token}`);
return dictionary[dictionaryIndex];
}
function pushAll(dictionary, values) {
for (const value of values) dictionary.push(value);
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/decoding/propertyDecoder.js
function decodePropertyColumn(data, offset, columnMetadata, numStreams, numFeatures, propertyColumnNames) {
if (columnMetadata.type === "scalarType") {
if (propertyColumnNames && !propertyColumnNames.has(columnMetadata.name)) {
skipColumn(numStreams, data, offset);
return null;
}
return decodeScalarPropertyColumn(numStreams, data, offset, numFeatures, columnMetadata.scalarType, columnMetadata);
}
if (columnMetadata.complexType?.physicalType === ComplexType.MAP) return decodeMapPropertyColumn(data, offset, columnMetadata, numStreams);
if (numStreams === 0) return null;
return decodeSharedDictionary(data, offset, columnMetadata, propertyColumnNames);
}
function decodeScalarPropertyColumn(numStreams, data, offset, numFeatures, column, columnMetadata) {
let nullabilityBuffer;
if (numStreams === 0) return null;
if (columnMetadata.nullable) {
const presentStreamMetadata = decodeStreamMetadata(data, offset);
const numValues = presentStreamMetadata.numValues;
const streamDataStart = offset.get();
const presentVector = decodeBooleanRle(data, numValues, presentStreamMetadata.byteLength, offset);
offset.set(streamDataStart + presentStreamMetadata.byteLength);
nullabilityBuffer = new BitVector(presentVector, presentStreamMetadata.numValues);
}
const sizeOrNullabilityBuffer = nullabilityBuffer ?? numFeatures;
switch (column.physicalType) {
case ScalarType.UINT_32:
case ScalarType.INT_32: return decodeInt32Column(data, offset, columnMetadata, column, sizeOrNullabilityBuffer);
case ScalarType.STRING: {
const stringDataStreams = columnMetadata.nullable ? numStreams - 1 : numStreams;
return decodeString$1(columnMetadata.name, data, offset, stringDataStreams, nullabilityBuffer) ?? null;
}
case ScalarType.BOOLEAN: return decodeBooleanColumn(data, offset, columnMetadata, numFeatures, sizeOrNullabilityBuffer);
case ScalarType.UINT_64:
case ScalarType.INT_64: return decodeInt64Column(data, offset, columnMetadata, sizeOrNullabilityBuffer, column);
case ScalarType.FLOAT: return decodeFloatColumn(data, offset, columnMetadata, sizeOrNullabilityBuffer);
case ScalarType.DOUBLE: return decodeDoubleColumn(data, offset, columnMetadata, sizeOrNullabilityBuffer);
default: throw new Error(`The specified data type for the field is currently not supported: ${column}`);
}
}
function decodeBooleanColumn(data, offset, column, _numFeatures, sizeOrNullabilityBuffer) {
const dataStreamMetadata = decodeStreamMetadata(data, offset);
const numValues = dataStreamMetadata.numValues;
const streamDataStart = offset.get();
const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : void 0;
const dataStream = decodeBooleanRle(data, numValues, dataStreamMetadata.byteLength, offset, nullabilityBuffer);
offset.set(streamDataStart + dataStreamMetadata.byteLength);
const dataVector = new BitVector(dataStream, numValues);
return new BooleanFlatVector(column.name, dataVector, sizeOrNullabilityBuffer);
}
function decodeFloatColumn(data, offset, column, sizeOrNullabilityBuffer) {
const dataStreamMetadata = decodeStreamMetadata(data, offset);
const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : void 0;
const dataStream = decodeFloatsLE(data, offset, dataStreamMetadata.numValues, nullabilityBuffer);
return new FloatFlatVector(column.name, dataStream, sizeOrNullabilityBuffer);
}
function decodeDoubleColumn(data, offset, column, sizeOrNullabilityBuffer) {
const dataStreamMetadata = decodeStreamMetadata(data, offset);
const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : void 0;
const dataStream = decodeDoublesLE(data, offset, dataStreamMetadata.numValues, nullabilityBuffer);
return new DoubleFlatVector(column.name, dataStream, sizeOrNullabilityBuffer);
}
function decodeInt64Column(data, offset, column, sizeOrNullabilityBuffer, scalarColumn) {
const dataStreamMetadata = decodeStreamMetadata(data, offset);
const vectorType = getVectorType(dataStreamMetadata, sizeOrNullabilityBuffer, data, offset, "int64");
const isSigned = scalarColumn.physicalType === ScalarType.INT_64;
if (vectorType === VectorType.FLAT) {
const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : void 0;
const dataStream = isSigned ? decodeSignedInt64Stream(data, offset, dataStreamMetadata, nullabilityBuffer) : decodeUnsignedInt64Stream(data, offset, dataStreamMetadata, nullabilityBuffer);
return new Int64FlatVector(column.name, dataStream, sizeOrNullabilityBuffer);
}
if (vectorType === VectorType.SEQUENCE) {
const id = decodeSequenceInt64Stream(data, offset, dataStreamMetadata);
return new Int64SequenceVector(column.name, id[0], id[1], dataStreamMetadata.numRleValues, isSigned);
}
const constValue = isSigned ? decodeSignedConstInt64Stream(data, offset, dataStreamMetadata) : decodeUnsignedConstInt64Stream(data, offset, dataStreamMetadata);
return new Int64ConstVector(column.name, constValue, sizeOrNullabilityBuffer, isSigned);
}
function decodeInt32Column(data, offset, column, scalarColumn, sizeOrNullabilityBuffer) {
const dataStreamMetadata = decodeStreamMetadata(data, offset);
const vectorType = getVectorType(dataStreamMetadata, sizeOrNullabilityBuffer, data, offset);
const isSigned = scalarColumn.physicalType === ScalarType.INT_32;
if (vectorType === VectorType.FLAT) {
const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : void 0;
const dataStream = isSigned ? decodeSignedInt32Stream(data, offset, dataStreamMetadata, void 0, nullabilityBuffer) : decodeUnsignedInt32Stream(data, offset, dataStreamMetadata, void 0, nullabilityBuffer);
return new Int32FlatVector(column.name, dataStream, sizeOrNullabilityBuffer);
}
if (vectorType === VectorType.SEQUENCE) {
const id = decodeSequenceInt32Stream(data, offset, dataStreamMetadata);
return new Int32SequenceVector(column.name, id[0], id[1], dataStreamMetadata.numRleValues, isSigned);
}
const constValue = isSigned ? decodeSignedConstInt32Stream(data, offset, dataStreamMetadata) : decodeUnsignedConstInt32Stream(data, offset, dataStreamMetadata);
return new Int32ConstVector(column.name, constValue, sizeOrNullabilityBuffer, isSigned);
}
function isNullabilityBuffer(sizeOrNullabilityBuffer) {
return sizeOrNullabilityBuffer instanceof BitVector;
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tileset/typeMap.js
/**
* The single varint32 that introduces every column in the tile metadata, identifying what kind of
* column follows. Ids occupy a small range of flagged codes, geometry has one code of its own, and
* scalar properties are laid out from `SCALAR_BASE` upwards, two codes per type.
*/
const ColumnTypeCode = {
/** Id columns occupy 0..3. */
ID: 0,
/** Set on an id column whose values can be null. */
ID_NULLABLE: 1,
/** Set on an id column holding 64-bit rather than 32-bit ids. */
ID_LONG: 2,
GEOMETRY: 4,
/** Scalar properties are `SCALAR_BASE + scalarType * 2 + (nullable ? 1 : 0)`. */
SCALAR_BASE: 10,
STRUCT: 30,
MAP: 31
};
/**
* The type code is a single varint32 that encodes:
* - Physical or logical type
* - Nullable flag
* - Whether the column has a name (typeCode >= ColumnTypeCode.SCALAR_BASE)
* - Whether the column has children (typeCode == 30 for STRUCT)
* - For ID types: whether it uses long (64-bit) IDs
*/
/**
* Decodes a type code into a Column structure.
*
* ID type codes (0..3):
* - Bit 0: nullable
* - Bit 1: longID (0/1 -> uint32 IDs, 2/3 -> uint64 IDs)
*
* ID columns are kept as logical types so they remain distinguishable
* from feature properties that may also be named "id".
*/
function decodeColumnType(typeCode) {
switch (typeCode) {
case ColumnTypeCode.ID:
case ColumnTypeCode.ID | ColumnTypeCode.ID_NULLABLE:
case ColumnTypeCode.ID | ColumnTypeCode.ID_LONG:
case ColumnTypeCode.ID | ColumnTypeCode.ID_LONG | ColumnTypeCode.ID_NULLABLE: return {
nullable: (typeCode & ColumnTypeCode.ID_NULLABLE) !== 0,
columnScope: ColumnScope.FEATURE,
type: "scalarType",
scalarType: {
longID: (typeCode & ColumnTypeCode.ID_LONG) !== 0,
type: "logicalType",
logicalType: LogicalScalarType.ID
}
};
case ColumnTypeCode.GEOMETRY: return {
nullable: false,
columnScope: ColumnScope.FEATURE,
type: "complexType",
complexType: {
type: "physicalType",
physicalType: ComplexType.GEOMETRY,
children: []
}
};
case ColumnTypeCode.STRUCT: return {
nullable: false,
columnScope: ColumnScope.FEATURE,
type: "complexType",
complexType: {
type: "physicalType",
physicalType: ComplexType.STRUCT,
children: []
}
};
case ColumnTypeCode.MAP: return {
nullable: true,
columnScope: ColumnScope.FEATURE,
type: "complexType",
complexType: {
type: "physicalType",
physicalType: ComplexType.MAP,
children: []
}
};
default: return mapScalarType(typeCode);
}
}
/**
* Returns true if this type code requires a name to be stored.
* ID (0-3) and GEOMETRY (4) columns have implicit names.
* All other types (>= ColumnTypeCode.SCALAR_BASE) require explicit names.
*/
function columnTypeHasName(typeCode) {
return typeCode >= ColumnTypeCode.SCALAR_BASE;
}
/**
* Returns true if this type code has child fields.
* STRUCT (typeCode 30) and MAP (typeCode 31) have children.
*/
function columnTypeHasChildren(typeCode) {
return typeCode === ColumnTypeCode.STRUCT || typeCode === ColumnTypeCode.MAP;
}
/**
* Determines if a stream count needs to be read for this column.
* Mirrors the logic in cpp/include/mlt/metadata/type_map.hpp lines 85-122
*/
function hasStreamCount(column) {
if (column.type === "scalarType") {
const scalarCol = column.scalarType;
if (scalarCol.type === "physicalType") switch (scalarCol.physicalType) {
case ScalarType.BOOLEAN:
case ScalarType.INT_8:
case ScalarType.UINT_8:
case ScalarType.INT_32:
case ScalarType.UINT_32:
case ScalarType.INT_64:
case ScalarType.UINT_64:
case ScalarType.FLOAT:
case ScalarType.DOUBLE: return false;
case ScalarType.STRING: return true;
default: return false;
}
if (scalarCol.type === "logicalType") return false;
} else if (column.type === "complexType") {
const complexCol = column.complexType;
if (complexCol.type === "physicalType") switch (complexCol.physicalType) {
case ComplexType.GEOMETRY:
case ComplexType.STRUCT:
case ComplexType.MAP: return true;
default: return false;
}
}
console.warn("Unexpected column type in hasStreamCount", column);
return false;
}
function isLogicalIdColumn(column) {
return column.type === "scalarType" && column.scalarType?.type === "logicalType" && column.scalarType.logicalType === LogicalScalarType.ID;
}
function isGeometryColumn(column) {
return column.type === "complexType" && column.complexType?.type === "physicalType" && column.complexType.physicalType === ComplexType.GEOMETRY;
}
/**
* Maps a scalar type code to a Column with ScalarType.
* Type codes 10-29 encode scalar types with nullable flag.
* Even codes are non-nullable, odd codes are nullable.
*/
function mapScalarType(typeCode) {
let physicalType;
switch (typeCode) {
case 10:
case 11:
physicalType = ScalarType.BOOLEAN;
break;
case 12:
case 13:
physicalType = ScalarType.INT_8;
break;
case 14:
case 15:
physicalType = ScalarType.UINT_8;
break;
case 16:
case 17:
physicalType = ScalarType.INT_32;
break;
case 18:
case 19:
physicalType = ScalarType.UINT_32;
break;
case 20:
case 21:
physicalType = ScalarType.INT_64;
break;
case 22:
case 23:
physicalType = ScalarType.UINT_64;
break;
case 24:
case 25:
physicalType = ScalarType.FLOAT;
break;
case 26:
case 27:
physicalType = ScalarType.DOUBLE;
break;
case 28:
case 29:
physicalType = ScalarType.STRING;
break;
default: return null;
}
return {
nullable: (typeCode & 1) !== 0,
columnScope: ColumnScope.FEATURE,
type: "scalarType",
scalarType: {
longID: false,
type: "physicalType",
physicalType
}
};
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/metadata/tileset/embeddedTilesetMetadataDecoder.js
const textDecoder = new TextDecoder();
const SUPPORTED_COLUMN_TYPES = "0-3(ID), 4(GEOMETRY), 10-29(scalars), 30(STRUCT), 31(MAP)";
const SUPPORTED_FIELD_TYPES = "10-29(scalars), 30(STRUCT), 31(MAP)";
/**
* Decodes a length-prefixed UTF-8 string.
* Layout: [len: varint32][bytes: len]
*/
function decodeString(src, offset) {
const length = decodeVarintInt32(src, offset, 1)[0];
if (length === 0) return "";
const start = offset.get();
const end = start + length;
const view = src.subarray(start, end);
offset.add(length);
return textDecoder.decode(view);
}
/**
* Converts a Column to a Field.
* Used when decoding Field metadata which has the same format as Column.
*/
function columnToField(column) {
const name = column.name;
const nullable = column.nullable;
return column.type === "scalarType" ? {
type: "scalarField",
scalarField: column.scalarType,
name,
nullable
} : {
type: "complexField",
complexField: column.complexType,
name,
nullable
};
}
/**
* Decodes a Field used as part of complex types (STRUCT children).
*/
function decodeField(src, offset) {
const typeCode = decodeVarintInt32(src, offset, 1)[0] >>> 0;
const base = typeCode >= ColumnTypeCode.SCALAR_BASE ? decodeColumnType(typeCode) : null;
if (!base) throw new Error(`Unsupported field type code ${typeCode}. Supported: ${SUPPORTED_FIELD_TYPES}`);
const column = {
...base,
name: decodeString(src, offset)
};
if (column.type === "complexType" && columnTypeHasChildren(typeCode)) {
const complexCol = column.complexType;
const childCount = decodeVarintInt32(src, offset, 1)[0] >>> 0;
complexCol.children = new Array(childCount);
for (let i = 0; i < childCount; i++) complexCol.children[i] = decodeField(src, offset);
}
return columnToField(column);
}
/**
* The typeCode encodes the column type, nullable flag, and whether it has name/children.
*/
function decodeColumn(src, offset) {
const typeCode = decodeVarintInt32(src, offset, 1)[0] >>> 0;
const base = decodeColumnType(typeCode);
if (!base) throw new Error(`Unsupported column type code ${typeCode}. Supported: ${SUPPORTED_COLUMN_TYPES}`);
let name;
if (columnTypeHasName(typeCode)) name = decodeString(src, offset);
else if (typeCode < ColumnTypeCode.GEOMETRY) name = "id";
else if (typeCode === ColumnTypeCode.GEOMETRY) name = "geometry";
else throw new Error(`Unsupported column type code ${typeCode}. Supported: ${SUPPORTED_COLUMN_TYPES}`);
const column = {
...base,
name
};
if (column.type === "complexType" && columnTypeHasChildren(typeCode)) {
const childCount = decodeVarintInt32(src, offset, 1)[0] >>> 0;
const complexCol = column.complexType;
complexCol.children = new Array(childCount);
for (let i = 0; i < childCount; i++) complexCol.children[i] = decodeField(src, offset);
}
return column;
}
/**
* Top-level decoder for embedded tileset metadata.
* Reads exactly ONE FeatureTableSchema from the stream.
*
* @param bytes The byte array containing the metadata
* @param offset The current offset in the byte array (will be advanced)
*/
function decodeEmbeddedTileSetMetadata(bytes, offset) {
const meta = {};
meta.featureTables = [];
const table = {};
table.name = decodeString(bytes, offset);
if (table.name.length === 0) throw new Error("Missing layer name");
const extent = decodeVarintInt32(bytes, offset, 1)[0] >>> 0;
const columnCount = decodeVarintInt32(bytes, offset, 1)[0] >>> 0;
table.columns = new Array(columnCount);
for (let j = 0; j < columnCount; j++) table.columns[j] = decodeColumn(bytes, offset);
meta.featureTables.push(table);
return [meta, extent];
}
//#endregion
//#region node_modules/@maplibre/mlt/dist/mltDecoder.js
/**
* Decodes a tile with embedded metadata (Tag 0x01 format).
* This is the primary decoder function for MLT tiles.
*
* @param tile The tile data to decode (will be decompressed if gzip-compressed)
* @param geometryScaling Optional geometry scaling parameters
* @param idWithinMaxSafeInteger If true, limits ID values to JavaScript safe integer range (53 bits)
*/
function decodeTile(tile, geometryScaling, idWithinMaxSafeInteger = true) {
const offset = new IntWrapper(0);
const featureTables = [];
while (offset.get() < tile.length) {
const blockLength = decodeVarintInt32(tile, offset, 1)[0] >>> 0;
const blockEnd = offset.get() + blockLength;
if (blockEnd > tile.length) throw new Error(`Block overruns tile: ${blockEnd} > ${tile.length}`);
const tag = decodeVarintInt32(tile, offset, 1)[0] >>> 0;
if (tag !== 1 && tag !== 2) {
offset.set(blockEnd);
continue;
}
const [metadata, extent] = decodeEmbeddedTileSetMetadata(tile, offset);
const featureTableMetadata = metadata.featureTables[0];
let idVector = null;
let geometryVector = null;
const propertyVectors = [];
let numFeatures = 0;
for (const columnMetadata of featureTableMetadata.columns) {
const columnName = columnMetadata.name;
if (isLogicalIdColumn(columnMetadata)) {
let nullabilityBuffer = null;
if (columnMetadata.nullable) {
const presentStreamMetadata = decodeStreamMetadata(tile, offset);
const streamDataStart = offset.get();
const values = decodeBooleanRle(tile, presentStreamMetadata.numValues, presentStreamMetadata.byteLength, offset);
offset.set(streamDataStart + presentStreamMetadata.byteLength);
nullabilityBuffer = new BitVector(values, presentStreamMetadata.numValues);
}
const idDataStreamMetadata = decodeStreamMetadata(tile, offset);
numFeatures = nullabilityBuffer ? nullabilityBuffer.size() : idDataStreamMetadata.decompressedCount;
idVector = decodeIdColumn(tile, columnMetadata, offset, columnName, idDataStreamMetadata, nullabilityBuffer ?? numFeatures, idWithinMaxSafeInteger);
} else if (isGeometryColumn(columnMetadata)) {
const numStreams = decodeVarintInt32(tile, offset, 1)[0];
if (numFeatures === 0) {
const savedOffset = offset.get();
numFeatures = decodeStreamMetadata(tile, offset).decompressedCount;
offset.set(savedOffset);
}
if (geometryScaling) geometryScaling.scale = geometryScaling.extent / extent;
geometryVector = decodeGeometryColumn(tile, numStreams, offset, numFeatures, geometryScaling);
} else {
const numStreams = hasStreamCount(columnMetadata) ? decodeVarintInt32(tile, offset, 1)[0] : 1;
if (numStreams === 0) continue;
const propertyVector = decodePropertyColumn(tile, offset, columnMetadata, numStreams, numFeatures, void 0);
if (propertyVector) {
if (Array.isArray(propertyVector)) for (const property of propertyVector) propertyVectors.push(property);
else propertyVectors.push(propertyVector);
}
}
}
const featureTable = new FeatureTable(featureTableMetadata.name, geometryVector, idVector, propertyVectors, extent);
featureTables.push(featureTable);
offset.set(blockEnd);
}
return featureTables;
}
function decodeIdColumn(tile, columnMetadata, offset, columnName, idDataStreamMetadata, sizeOrNullabilityBuffer, idWithinMaxSafeInteger = false) {
const idDataType = columnMetadata.scalarType?.longID ? ScalarType.UINT_64 : ScalarType.UINT_32;
const nullabilityBuffer = typeof sizeOrNullabilityBuffer === "number" ? void 0 : sizeOrNullabilityBuffer;
const vectorType = getVectorType(idDataStreamMetadata, sizeOrNullabilityBuffer, tile, offset, idDataType === ScalarType.UINT_64 ? "int64" : "int32");
if (idDataType === ScalarType.UINT_32) switch (vectorType) {
case VectorType.FLAT: return new Int32FlatVector(columnName, decodeUnsignedInt32Stream(tile, offset, idDataStreamMetadata, void 0, nullabilityBuffer), sizeOrNullabilityBuffer);
case VectorType.SEQUENCE: {
const id = decodeSequenceInt32Stream(tile, offset, idDataStreamMetadata);
return new Int32SequenceVector(columnName, id[0], id[1], idDataStreamMetadata.numRleValues, false);
}
case VectorType.CONST: return new Int32ConstVector(columnName, decodeUnsignedConstInt32Stream(tile, offset, idDataStreamMetadata), sizeOrNullabilityBuffer, false);
}
switch (vectorType) {
case VectorType.FLAT:
if (idWithinMaxSafeInteger) return new DoubleFlatVector(columnName, decodeUnsignedInt64AsFloat64Stream(tile, offset, idDataStreamMetadata, nullabilityBuffer), sizeOrNullabilityBuffer);
return new Int64FlatVector(columnName, decodeUnsignedInt64Stream(tile, offset, idDataStreamMetadata, nullabilityBuffer), sizeOrNullabilityBuffer);
case VectorType.SEQUENCE: {
const id = decodeSequenceInt64Stream(tile, offset, idDataStreamMetadata);
return new Int64SequenceVector(columnName, id[0], id[1], idDataStreamMetadata.numRleValues, false);
}
case VectorType.CONST: return new Int64ConstVector(columnName, decodeUnsignedConstInt64Stream(tile, offset, idDataStreamMetadata), sizeOrNullabilityBuffer, false);
}
throw new Error("Vector type not supported for id column.");
}
//#endregion
//#region src/source/vector_tile_mlt.ts
var MLTVectorTileFeature = class {
constructor(feature, extent) {
this._featureData = feature;
this.properties = this._featureData.properties || {};
switch (this._featureData.geometry?.type) {
case GEOMETRY_TYPE.POINT:
case GEOMETRY_TYPE.MULTIPOINT:
this.type = 1;
break;
case GEOMETRY_TYPE.LINESTRING:
case GEOMETRY_TYPE.MULTILINESTRING:
this.type = 2;
break;
case GEOMETRY_TYPE.POLYGON:
case GEOMETRY_TYPE.MULTIPOLYGON:
this.type = 3;
break;
default: this.type = 0;
}
this.extent = extent;
this.id = Number(this._featureData.id);
}
loadGeometry() {
const points = [];
for (const ring of this._featureData.geometry.coordinates) {
const pointRing = [];
for (const coord of ring) pointRing.push(new Point(coord.x, coord.y));
points.push(pointRing);
}
return points;
}
};
var MLTVectorTileLayer = class {
constructor(featureTable) {
this.features = [];
this.featureTable = featureTable;
this.name = featureTable.name;
this.extent = featureTable.extent;
this.version = 2;
this.features = featureTable.getFeatures();
this.length = this.features.length;
}
feature(i) {
return new MLTVectorTileFeature(this.features[i], this.extent);
}
};
var MLTVectorTile = class {
constructor(buffer) {
this.layers = {};
const features = decodeTile(new Uint8Array(buffer));
this.layers = features.reduce((acc, f) => ({
...acc,
[f.name]: new MLTVectorTileLayer(f)
}), {});
}
};
//#endregion
//#region src/data/feature_index.ts
/**
* An in memory index class to allow fast interaction with features
*/
var FeatureIndex = class {
constructor(tileID, promoteId) {
this.tileID = tileID;
this.x = tileID.canonical.x;
this.y = tileID.canonical.y;
this.z = tileID.canonical.z;
this.grid = new TransferableGridIndex(EXTENT$1, 16, 0);
this.grid3D = new TransferableGridIndex(EXTENT$1, 16, 0);
this.featureIndexArray = new FeatureIndexArray();
this.promoteId = promoteId;
}
insert(feature, geometry, featureIndex, sourceLayerIndex, bucketIndex, is3D) {
const key = this.featureIndexArray.length;
this.featureIndexArray.emplaceBack(featureIndex, sourceLayerIndex, bucketIndex);
const grid = is3D ? this.grid3D : this.grid;
for (const ring of geometry) {
const bbox = [
Infinity,
Infinity,
-Infinity,
-Infinity
];
for (const p of ring) {
bbox[0] = Math.min(bbox[0], p.x);
bbox[1] = Math.min(bbox[1], p.y);
bbox[2] = Math.max(bbox[2], p.x);
bbox[3] = Math.max(bbox[3], p.y);
}
if (bbox[0] < 8192 && bbox[1] < 8192 && bbox[2] >= 0 && bbox[3] >= 0) grid.insert(key, bbox[0], bbox[1], bbox[2], bbox[3]);
}
}
loadVTLayers() {
if (!this.vtLayers) {
switch (this.encoding) {
case "mlt":
this.vtLayers = new MLTVectorTile(this.rawTileData).layers;
break;
default: this.vtLayers = new VectorTile(new PbfReader(this.rawTileData)).layers;
}
this.sourceLayerCoder = new DictionaryCoder(this.vtLayers ? Object.keys(this.vtLayers).sort() : [GEOJSON_TILE_LAYER_NAME]);
}
return this.vtLayers;
}
query(args, styleLayers, serializedLayers, sourceFeatureState) {
this.loadVTLayers();
const params = args.params;
const pixelsToTileUnits = EXTENT$1 / args.tileSize / args.scale;
const filter = featureFilter(params.filter, "queryRenderedFeatures filter", params.globalState);
const queryGeometry = args.queryGeometry;
const queryPadding = args.queryPadding * pixelsToTileUnits;
const bounds = Bounds.fromPoints(queryGeometry);
const matching = this.grid.query(bounds.minX - queryPadding, bounds.minY - queryPadding, bounds.maxX + queryPadding, bounds.maxY + queryPadding);
const cameraBounds = Bounds.fromPoints(args.cameraQueryGeometry).expandBy(queryPadding);
const matching3D = this.grid3D.query(cameraBounds.minX, cameraBounds.minY, cameraBounds.maxX, cameraBounds.maxY, (bx1, by1, bx2, by2) => {
return polygonIntersectsBox(args.cameraQueryGeometry, bx1 - queryPadding, by1 - queryPadding, bx2 + queryPadding, by2 + queryPadding);
});
for (const key of matching3D) matching.push(key);
matching.sort(topDownFeatureComparator);
const result = {};
let previousIndex;
for (const index of matching) {
if (index === previousIndex) continue;
previousIndex = index;
const match = this.featureIndexArray.get(index);
let featureGeometry = null;
this.loadMatchingFeature(result, match.bucketIndex, match.sourceLayerIndex, match.featureIndex, filter, params.layers, params.availableImages, styleLayers, serializedLayers, sourceFeatureState, (feature, styleLayer, featureState) => {
featureGeometry ||= loadGeometry(feature);
return styleLayer.queryIntersectsFeature({
queryGeometry,
feature,
featureState,
geometry: featureGeometry,
zoom: this.z,
transform: args.transform,
pixelsToTileUnits,
pixelPosMatrix: args.pixelPosMatrix,
unwrappedTileID: this.tileID.toUnwrapped(),
getElevation: args.getElevation
});
});
}
return result;
}
loadMatchingFeature(result, bucketIndex, sourceLayerIndex, featureIndex, filter, filterLayerIDs, availableImages, styleLayers, serializedLayers, sourceFeatureState, intersectionTest) {
const layerIDs = this.bucketLayerIDs[bucketIndex];
if (filterLayerIDs && !layerIDs.some((id) => filterLayerIDs.has(id))) return;
const sourceLayerName = this.sourceLayerCoder.decode(sourceLayerIndex);
const feature = this.vtLayers[sourceLayerName].feature(featureIndex);
if (filter.needGeometry) {
const evaluationFeature = toEvaluationFeature(feature, true);
if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), evaluationFeature, this.tileID.canonical)) return;
} else if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) return;
const id = this.getId(feature, sourceLayerName);
for (const layerID of layerIDs) {
if (filterLayerIDs && !filterLayerIDs.has(layerID)) continue;
const styleLayer = styleLayers[layerID];
if (!styleLayer) continue;
let featureState = {};
if (id && sourceFeatureState) featureState = sourceFeatureState.getState(styleLayer.sourceLayer || "_geojsonTileLayer", id);
const serializedLayer = extend({}, serializedLayers[layerID]);
serializedLayer.paint = evaluateProperties(serializedLayer.paint, styleLayer.paint, feature, featureState, availableImages);
serializedLayer.layout = evaluateProperties(serializedLayer.layout, styleLayer.layout, feature, featureState, availableImages);
const intersectionZ = !intersectionTest || intersectionTest(feature, styleLayer, featureState);
if (!intersectionZ) continue;
const geojsonFeature = new GeoJSONFeature(feature, this.z, this.x, this.y, id);
geojsonFeature.layer = serializedLayer;
let layerResult = result[layerID];
if (layerResult === void 0) layerResult = result[layerID] = [];
layerResult.push({
featureIndex,
feature: geojsonFeature,
intersectionZ
});
}
}
lookupSymbolFeatures(symbolFeatureIndexes, serializedLayers, bucketIndex, sourceLayerIndex, filterParams, filterLayerIDs, availableImages, styleLayers) {
const result = {};
this.loadVTLayers();
const filter = featureFilter(filterParams.filterSpec, "queryRenderedFeatures symbol filter", filterParams.globalState);
for (const symbolFeatureIndex of symbolFeatureIndexes) this.loadMatchingFeature(result, bucketIndex, sourceLayerIndex, symbolFeatureIndex, filter, filterLayerIDs, availableImages, styleLayers, serializedLayers);
return result;
}
hasLayer(id) {
for (const layerIDs of this.bucketLayerIDs) for (const layerID of layerIDs) if (id === layerID) return true;
return false;
}
getId(feature, sourceLayerId) {
let id = feature.id;
if (this.promoteId) {
const propName = typeof this.promoteId === "string" ? this.promoteId : this.promoteId[sourceLayerId];
id = feature.properties[propName];
if (typeof id === "boolean") id = Number(id);
if (id === void 0 && feature.properties?.cluster && this.promoteId) id = Number(feature.properties.cluster_id);
}
return id;
}
};
register("FeatureIndex", FeatureIndex, { omit: ["rawTileData", "sourceLayerCoder"] });
function evaluateProperties(serializedProperties, styleLayerProperties, feature, featureState, availableImages) {
return mapObject(serializedProperties, (property, key) => {
const prop = styleLayerProperties instanceof PossiblyEvaluated ? styleLayerProperties.get(key) : null;
return prop?.evaluate ? prop.evaluate(feature, featureState, availableImages) : prop;
});
}
function topDownFeatureComparator(a, b) {
return b - a;
}
//#endregion
//#region src/tile/tile_cache.ts
/**
* @internal
* A [least-recently-used cache](https://en.wikipedia.org/wiki/Cache_algorithms)
* with hash lookup made possible by keeping a list of keys in parallel to
* an array of dictionary of values
*
* TileManager offloads currently unused tiles to this cache, and when a tile gets used again,
* it is also removed from this cache. Thus addition is the only operation that counts as "usage"
* for the purposes of LRU behaviour.
*/
var TileCache = class {
/**
* @param max - number of permitted values
* @param onRemove - callback called with items when they expire
*/
constructor(max, onRemove) {
this.max = max;
this.onRemove = onRemove;
this.reset();
}
/**
* Clear the cache
*
* @returns this cache
*/
reset() {
for (const key in this.data) for (const removedData of this.data[key]) {
if (removedData.timeout) clearTimeout(removedData.timeout);
this.onRemove(removedData.value);
}
this.data = {};
this.order = [];
return this;
}
/**
* Add a key, value combination to the cache, trimming its size if this pushes
* it over max length.
*
* @param tileID - lookup key for the item
* @param data - tile data
*
* @returns this cache
*/
add(tileID, data, expiryTimeout) {
const key = tileID.wrapped().key;
if (this.data[key] === void 0) this.data[key] = [];
const dataWrapper = {
value: data,
timeout: void 0
};
if (expiryTimeout !== void 0) dataWrapper.timeout = setTimeout(() => {
this.remove(tileID, dataWrapper);
}, expiryTimeout);
this.data[key].push(dataWrapper);
this.order.push(key);
if (this.order.length > this.max) {
const removedData = this._getAndRemoveByKey(this.order[0]);
if (removedData) this.onRemove(removedData);
}
return this;
}
/**
* Determine whether the value attached to `key` is present
*
* @param tileID - the key to be looked-up
* @returns whether the cache has this value
*/
has(tileID) {
return tileID.wrapped().key in this.data;
}
/**
* Get the value attached to a specific key and remove data from cache.
* If the key is not found, returns `null`
*
* @param tileID - the key to look up
* @returns the tile data, or null if it isn't found
*/
getAndRemove(tileID) {
if (!this.has(tileID)) return null;
return this._getAndRemoveByKey(tileID.wrapped().key);
}
_getAndRemoveByKey(key) {
const data = this.data[key].shift();
if (data.timeout) clearTimeout(data.timeout);
if (this.data[key].length === 0) delete this.data[key];
this.order.splice(this.order.indexOf(key), 1);
return data.value;
}
getByKey(key) {
const data = this.data[key];
return data ? data[0].value : null;
}
/**
* Get the value attached to a specific key without removing data
* from the cache. If the key is not found, returns `null`
*
* @param tileID - the key to look up
* @returns the tile data, or null if it isn't found
*/
get(tileID) {
if (!this.has(tileID)) return null;
return this.data[tileID.wrapped().key][0].value;
}
/**
* Remove a key/value combination from the cache.
*
* @param tileID - the key for the pair to delete
* @param value - If a value is provided, remove that exact version of the value.
* @returns this cache
*/
remove(tileID, value) {
if (!this.has(tileID)) return this;
const key = tileID.wrapped().key;
const dataIndex = value === void 0 ? 0 : this.data[key].indexOf(value);
const data = this.data[key][dataIndex];
this.data[key].splice(dataIndex, 1);
if (data.timeout) clearTimeout(data.timeout);
if (this.data[key].length === 0) delete this.data[key];
this.onRemove(data.value);
this.order.splice(this.order.indexOf(key), 1);
return this;
}
/**
* Change the max size of the cache.
*
* @param max - the max size of the cache
* @returns this cache
*/
setMaxSize(max) {
this.max = max;
while (this.order.length > this.max) {
const removedData = this._getAndRemoveByKey(this.order[0]);
if (removedData) this.onRemove(removedData);
}
return this;
}
/**
* Remove entries that do not pass a filter function. Used for removing
* stale tiles from the cache.
*
* @param filterFn - Determines whether the tile is filtered. If the supplied function returns false, the tile will be filtered out.
*/
filter(filterFn) {
const removed = [];
for (const key in this.data) for (const entry of this.data[key]) if (!filterFn(entry.value)) removed.push(entry);
for (const r of removed) this.remove(r.value.tileID, r);
}
};
var BoundedLRUCache = class {
constructor(maxEntries) {
this.maxEntries = maxEntries;
this.map = /* @__PURE__ */ new Map();
}
get(key) {
const value = this.map.get(key);
if (value !== void 0) {
this.map.delete(key);
this.map.set(key, value);
}
return value;
}
set(key, value) {
if (this.map.has(key)) this.map.delete(key);
else if (this.map.size >= this.maxEntries) {
const oldestKey = this.map.keys().next().value;
this.map.delete(oldestKey);
}
this.map.set(key, value);
}
clear() {
this.map.clear();
}
};
//#endregion
//#region src/symbol/clip_line.ts
/**
* Returns the part of a multiline that intersects with the provided rectangular box.
*
* @param lines - the lines to check
* @param x1 - the left edge of the box
* @param y1 - the top edge of the box
* @param x2 - the right edge of the box
* @param y2 - the bottom edge of the box
* @returns lines
*/
function clipLine(lines, x1, y1, x2, y2) {
const clippedLines = [];
for (const line of lines) {
let clippedLine;
for (let i = 0; i < line.length - 1; i++) {
let p0 = line[i];
let p1 = line[i + 1];
if (p0.x < x1 && p1.x < x1) continue;
else if (p0.x < x1) p0 = new Point(x1, p0.y + (p1.y - p0.y) * ((x1 - p0.x) / (p1.x - p0.x)))._round();
else if (p1.x < x1) p1 = new Point(x1, p0.y + (p1.y - p0.y) * ((x1 - p0.x) / (p1.x - p0.x)))._round();
if (p0.y < y1 && p1.y < y1) continue;
else if (p0.y < y1) p0 = new Point(p0.x + (p1.x - p0.x) * ((y1 - p0.y) / (p1.y - p0.y)), y1)._round();
else if (p1.y < y1) p1 = new Point(p0.x + (p1.x - p0.x) * ((y1 - p0.y) / (p1.y - p0.y)), y1)._round();
if (p0.x >= x2 && p1.x >= x2) continue;
else if (p0.x >= x2) p0 = new Point(x2, p0.y + (p1.y - p0.y) * ((x2 - p0.x) / (p1.x - p0.x)))._round();
else if (p1.x >= x2) p1 = new Point(x2, p0.y + (p1.y - p0.y) * ((x2 - p0.x) / (p1.x - p0.x)))._round();
if (p0.y >= y2 && p1.y >= y2) continue;
else if (p0.y >= y2) p0 = new Point(p0.x + (p1.x - p0.x) * ((y2 - p0.y) / (p1.y - p0.y)), y2)._round();
else if (p1.y >= y2) p1 = new Point(p0.x + (p1.x - p0.x) * ((y2 - p0.y) / (p1.y - p0.y)), y2)._round();
if (!clippedLine || !p0.equals(clippedLine[clippedLine.length - 1])) {
clippedLine = [p0];
clippedLines.push(clippedLine);
}
clippedLine.push(p1);
}
}
return clippedLines;
}
/**
* Clips the geometry to the given bounds.
* @param geometry - the geometry to clip
* @param type - the geometry type (1=POINT, 2=LINESTRING, 3=POLYGON)
* @param x1 - the left edge of the clipping box
* @param y1 - the top edge of the clipping box
* @param x2 - the right edge of the clipping box
* @param y2 - the bottom edge of the clipping box
* @returns the clipped geometry
*/
function clipGeometry(geometry, type, x1, y1, x2, y2) {
let clippedGeometry = clipGeometryOnAxis(geometry, type, x1, x2, 0);
clippedGeometry = clipGeometryOnAxis(clippedGeometry, type, y1, y2, 1);
return clippedGeometry;
}
/**
* Clip features between two vertical or horizontal axis-parallel lines:
* ```
* | |
* ___|___ | /
* / | \____|____/
* | |
*```
* @param geometry - the geometry to clip
* @param type - the geometry type (1=POINT, 2=LINESTRING, 3=POLYGON)
* @param start - the start line coordinate (x or y) to clip against
* @param end - the end line coordinate (x or y) to clip against
* @param axis - the axis to clip on (X or Y)
* @returns the clipped geometry
*/
function clipGeometryOnAxis(geometry, type, start, end, axis) {
switch (type) {
case 1: return clipPoints(geometry, start, end, axis);
case 2: return clipLines(geometry, start, end, axis, false);
case 3: return clipLines(geometry, start, end, axis, true);
}
return [];
}
function clipPoints(geometry, start, end, axis) {
const newGeometry = [];
for (const ring of geometry) for (const point of ring) {
const a = axis === 0 ? point.x : point.y;
if (a >= start && a <= end) newGeometry.push([point]);
}
return newGeometry;
}
/**
* Clips a line to the given start and end coordinates.
* @param line - the line to clip
* @param start - the start line coordinate (x or y) to clip against
* @param end - the end line coordinate (x or y) to clip against
* @param axis - the axis to clip on (X or Y)
* @param isPolygon - whether the line is part of a polygon
* @returns the clipped line(s)
*/
function clipLineInternal(line, start, end, axis, isPolygon) {
const intersectionPoint = axis === 0 ? intersectionPointX : intersectionPointY;
let slice = [];
const newLine = [];
for (let i = 0; i < line.length - 1; i++) {
const p1 = line[i];
const p2 = line[i + 1];
const pos1 = axis === 0 ? p1.x : p1.y;
const pos2 = axis === 0 ? p2.x : p2.y;
let exited = false;
if (pos1 < start) {
if (pos2 > start) slice.push(intersectionPoint(p1, p2, start));
} else if (pos1 > end) {
if (pos2 < end) slice.push(intersectionPoint(p1, p2, end));
} else slice.push(p1);
if (pos2 < start && pos1 >= start) {
slice.push(intersectionPoint(p1, p2, start));
exited = true;
}
if (pos2 > end && pos1 <= end) {
slice.push(intersectionPoint(p1, p2, end));
exited = true;
}
if (!isPolygon && exited) {
newLine.push(slice);
slice = [];
}
}
const last = line.length - 1;
const lastPos = axis === 0 ? line[last].x : line[last].y;
if (lastPos >= start && lastPos <= end) slice.push(line[last]);
if (isPolygon && slice.length > 0 && !slice[0].equals(slice[slice.length - 1])) slice.push(new Point(slice[0].x, slice[0].y));
if (slice.length > 0) newLine.push(slice);
return newLine;
}
function clipLines(geometry, start, end, axis, isPolygon) {
const newGeometry = [];
for (const line of geometry) {
const clippedLines = clipLineInternal(line, start, end, axis, isPolygon);
if (clippedLines.length > 0) newGeometry.push(...clippedLines);
}
return newGeometry;
}
function intersectionPointX(p1, p2, x) {
const t = (x - p1.x) / (p2.x - p1.x);
return new Point(x, p1.y + (p2.y - p1.y) * t);
}
function intersectionPointY(p1, p2, y) {
const t = (y - p1.y) / (p2.y - p1.y);
return new Point(p1.x + (p2.x - p1.x) * t, y);
}
//#endregion
//#region src/symbol/anchor.ts
var Anchor = class Anchor extends Point {
constructor(x, y, angle, segment) {
super(x, y);
this.angle = angle;
if (segment !== void 0) this.segment = segment;
}
clone() {
return new Anchor(this.x, this.y, this.angle, this.segment);
}
};
register("Anchor", Anchor);
//#endregion
//#region src/symbol/check_max_angle.ts
/**
* Labels placed around really sharp angles aren't readable. Check if any
* part of the potential label has a combined angle that is too big.
*
* @param line - The line to check
* @param anchor - The point on the line around which the label is anchored.
* @param labelLength - The length of the label in geometry units.
* @param windowSize - The check fails if the combined angles within a part of the line that is `windowSize` long is too big.
* @param maxAngle - The maximum combined angle that any window along the label is allowed to have.
*
* @returns whether the label should be placed
*/
function checkMaxAngle(line, anchor, labelLength, windowSize, maxAngle) {
if (anchor.segment === void 0 || labelLength === 0) return true;
let p = anchor;
let index = anchor.segment + 1;
let anchorDistance = 0;
while (anchorDistance > -labelLength / 2) {
index--;
if (index < 0) return false;
anchorDistance -= line[index].dist(p);
p = line[index];
}
anchorDistance += line[index].dist(line[index + 1]);
index++;
const recentCorners = [];
let recentAngleDelta = 0;
while (anchorDistance < labelLength / 2) {
const prev = line[index - 1];
const current = line[index];
const next = line[index + 1];
if (!next) return false;
let angleDelta = prev.angleTo(current) - current.angleTo(next);
angleDelta = Math.abs((angleDelta + 3 * Math.PI) % (Math.PI * 2) - Math.PI);
recentCorners.push({
distance: anchorDistance,
angleDelta
});
recentAngleDelta += angleDelta;
while (anchorDistance - recentCorners[0].distance > windowSize) recentAngleDelta -= recentCorners.shift().angleDelta;
if (recentAngleDelta > maxAngle) return false;
index++;
anchorDistance += current.dist(next);
}
return true;
}
//#endregion
//#region src/symbol/get_anchors.ts
function getLineLength(line) {
let lineLength = 0;
for (let k = 0; k < line.length - 1; k++) lineLength += line[k].dist(line[k + 1]);
return lineLength;
}
function getAngleWindowSize(shapedText, glyphSize, boxScale) {
return shapedText ? 3 / 5 * glyphSize * boxScale : 0;
}
function getShapedLabelLength(shapedText, shapedIcon) {
return Math.max(shapedText ? shapedText.right - shapedText.left : 0, shapedIcon ? shapedIcon.right - shapedIcon.left : 0);
}
function getCenterAnchor(line, maxAngle, shapedText, shapedIcon, glyphSize, boxScale) {
const angleWindowSize = getAngleWindowSize(shapedText, glyphSize, boxScale);
const labelLength = getShapedLabelLength(shapedText, shapedIcon) * boxScale;
let prevDistance = 0;
const centerDistance = getLineLength(line) / 2;
for (let i = 0; i < line.length - 1; i++) {
const a = line[i], b = line[i + 1];
const segmentDistance = a.dist(b);
if (prevDistance + segmentDistance > centerDistance) {
const t = (centerDistance - prevDistance) / segmentDistance;
const anchor = new Anchor(interpolateFactory.number(a.x, b.x, t), interpolateFactory.number(a.y, b.y, t), b.angleTo(a), i);
anchor._round();
if (!angleWindowSize || checkMaxAngle(line, anchor, labelLength, angleWindowSize, maxAngle)) return anchor;
else return;
}
prevDistance += segmentDistance;
}
}
function getAnchors(line, spacing, maxAngle, shapedText, shapedIcon, glyphSize, boxScale, overscaling, tileExtent) {
const angleWindowSize = getAngleWindowSize(shapedText, glyphSize, boxScale);
const shapedLabelLength = getShapedLabelLength(shapedText, shapedIcon);
const labelLength = shapedLabelLength * boxScale;
const isLineContinued = line[0].x === 0 || line[0].x === tileExtent || line[0].y === 0 || line[0].y === tileExtent;
if (spacing - labelLength < spacing / 4) spacing = labelLength + spacing / 4;
const fixedExtraOffset = glyphSize * 2;
return resample(line, !isLineContinued ? (shapedLabelLength / 2 + fixedExtraOffset) * boxScale * overscaling % spacing : spacing / 2 * overscaling % spacing, spacing, angleWindowSize, maxAngle, labelLength, isLineContinued, false, tileExtent);
}
function resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength, isLineContinued, placeAtMiddle, tileExtent) {
const halfLabelLength = labelLength / 2;
const lineLength = getLineLength(line);
let distance = 0;
let markedDistance = offset - spacing;
let anchors = [];
for (let i = 0; i < line.length - 1; i++) {
const a = line[i], b = line[i + 1];
const segmentDist = a.dist(b), angle = b.angleTo(a);
while (markedDistance + spacing < distance + segmentDist) {
markedDistance += spacing;
const t = (markedDistance - distance) / segmentDist, x = interpolateFactory.number(a.x, b.x, t), y = interpolateFactory.number(a.y, b.y, t);
if (x >= 0 && x < tileExtent && y >= 0 && y < tileExtent && markedDistance - halfLabelLength >= 0 && markedDistance + halfLabelLength <= lineLength) {
const anchor = new Anchor(x, y, angle, i);
anchor._round();
if (!angleWindowSize || checkMaxAngle(line, anchor, labelLength, angleWindowSize, maxAngle)) anchors.push(anchor);
}
}
distance += segmentDist;
}
if (!placeAtMiddle && !anchors.length && !isLineContinued) anchors = resample(line, distance / 2, spacing, angleWindowSize, maxAngle, labelLength, isLineContinued, true, tileExtent);
return anchors;
}
//#endregion
//#region src/symbol/quads.ts
const border = 1;
/**
* Create the quads used for rendering an icon.
*/
function getIconQuads(shapedIcon, iconRotate, isSDFIcon, hasIconTextFit) {
const quads = [];
const image = shapedIcon.image;
const pixelRatio = image.pixelRatio;
const imageWidth = image.paddedRect.w - 2;
const imageHeight = image.paddedRect.h - 2;
let icon = {
x1: shapedIcon.left,
y1: shapedIcon.top,
x2: shapedIcon.right,
y2: shapedIcon.bottom
};
const stretchX = image.stretchX || [[0, imageWidth]];
const stretchY = image.stretchY || [[0, imageHeight]];
const reduceRanges = (sum, range) => sum + range[1] - range[0];
const stretchWidth = stretchX.reduce(reduceRanges, 0);
const stretchHeight = stretchY.reduce(reduceRanges, 0);
const fixedWidth = imageWidth - stretchWidth;
const fixedHeight = imageHeight - stretchHeight;
let stretchOffsetX = 0;
let stretchContentWidth = stretchWidth;
let stretchOffsetY = 0;
let stretchContentHeight = stretchHeight;
let fixedOffsetX = 0;
let fixedContentWidth = fixedWidth;
let fixedOffsetY = 0;
let fixedContentHeight = fixedHeight;
if (image.content && hasIconTextFit) {
const content = image.content;
const contentWidth = content[2] - content[0];
const contentHeight = content[3] - content[1];
if (image.textFitWidth || image.textFitHeight) icon = applyTextFit(shapedIcon);
stretchOffsetX = sumWithinRange(stretchX, 0, content[0]);
stretchOffsetY = sumWithinRange(stretchY, 0, content[1]);
stretchContentWidth = sumWithinRange(stretchX, content[0], content[2]);
stretchContentHeight = sumWithinRange(stretchY, content[1], content[3]);
fixedOffsetX = content[0] - stretchOffsetX;
fixedOffsetY = content[1] - stretchOffsetY;
fixedContentWidth = contentWidth - stretchContentWidth;
fixedContentHeight = contentHeight - stretchContentHeight;
}
const iconLeft = icon.x1;
const iconTop = icon.y1;
const iconWidth = icon.x2 - iconLeft;
const iconHeight = icon.y2 - iconTop;
const makeBox = (left, top, right, bottom) => {
const leftEm = getEmOffset(left.stretch - stretchOffsetX, stretchContentWidth, iconWidth, iconLeft);
const leftPx = getPxOffset(left.fixed - fixedOffsetX, fixedContentWidth, left.stretch, stretchWidth);
const topEm = getEmOffset(top.stretch - stretchOffsetY, stretchContentHeight, iconHeight, iconTop);
const topPx = getPxOffset(top.fixed - fixedOffsetY, fixedContentHeight, top.stretch, stretchHeight);
const rightEm = getEmOffset(right.stretch - stretchOffsetX, stretchContentWidth, iconWidth, iconLeft);
const rightPx = getPxOffset(right.fixed - fixedOffsetX, fixedContentWidth, right.stretch, stretchWidth);
const bottomEm = getEmOffset(bottom.stretch - stretchOffsetY, stretchContentHeight, iconHeight, iconTop);
const bottomPx = getPxOffset(bottom.fixed - fixedOffsetY, fixedContentHeight, bottom.stretch, stretchHeight);
const tl = new Point(leftEm, topEm);
const tr = new Point(rightEm, topEm);
const br = new Point(rightEm, bottomEm);
const bl = new Point(leftEm, bottomEm);
const pixelOffsetTL = new Point(leftPx / pixelRatio, topPx / pixelRatio);
const pixelOffsetBR = new Point(rightPx / pixelRatio, bottomPx / pixelRatio);
const angle = iconRotate * Math.PI / 180;
if (angle) {
const sin = Math.sin(angle), cos = Math.cos(angle), matrix = [
cos,
-sin,
sin,
cos
];
tl._matMult(matrix);
tr._matMult(matrix);
bl._matMult(matrix);
br._matMult(matrix);
}
const x1 = left.stretch + left.fixed;
const x2 = right.stretch + right.fixed;
const y1 = top.stretch + top.fixed;
const y2 = bottom.stretch + bottom.fixed;
return {
tl,
tr,
bl,
br,
tex: {
x: image.paddedRect.x + border + x1,
y: image.paddedRect.y + border + y1,
w: x2 - x1,
h: y2 - y1
},
writingMode: void 0,
glyphOffset: [0, 0],
sectionIndex: 0,
pixelOffsetTL,
pixelOffsetBR,
minFontScaleX: fixedContentWidth / pixelRatio / iconWidth,
minFontScaleY: fixedContentHeight / pixelRatio / iconHeight,
isSDF: isSDFIcon
};
};
if (!hasIconTextFit || !image.stretchX && !image.stretchY) quads.push(makeBox({
fixed: 0,
stretch: -1
}, {
fixed: 0,
stretch: -1
}, {
fixed: 0,
stretch: imageWidth + 1
}, {
fixed: 0,
stretch: imageHeight + 1
}));
else {
const xCuts = stretchZonesToCuts(stretchX, fixedWidth, stretchWidth);
const yCuts = stretchZonesToCuts(stretchY, fixedHeight, stretchHeight);
for (let xi = 0; xi < xCuts.length - 1; xi++) {
const x1 = xCuts[xi];
const x2 = xCuts[xi + 1];
for (let yi = 0; yi < yCuts.length - 1; yi++) {
const y1 = yCuts[yi];
const y2 = yCuts[yi + 1];
quads.push(makeBox(x1, y1, x2, y2));
}
}
}
return quads;
}
function sumWithinRange(ranges, min, max) {
let sum = 0;
for (const range of ranges) sum += Math.max(min, Math.min(max, range[1])) - Math.max(min, Math.min(max, range[0]));
return sum;
}
function stretchZonesToCuts(stretchZones, fixedSize, stretchSize) {
const cuts = [{
fixed: -1,
stretch: 0
}];
for (const [c1, c2] of stretchZones) {
const last = cuts[cuts.length - 1];
cuts.push({
fixed: c1 - last.stretch,
stretch: last.stretch
});
cuts.push({
fixed: c1 - last.stretch,
stretch: last.stretch + (c2 - c1)
});
}
cuts.push({
fixed: fixedSize + border,
stretch: stretchSize
});
return cuts;
}
function getEmOffset(stretchOffset, stretchSize, iconSize, iconOffset) {
return stretchOffset / stretchSize * iconSize + iconOffset;
}
function getPxOffset(fixedOffset, fixedSize, stretchOffset, stretchSize) {
return fixedOffset - fixedSize * stretchOffset / stretchSize;
}
/**
* Create the quads used for rendering a text label.
*/
function getGlyphQuads(anchor, shaping, textOffset, layer, alongLine, feature, imageMap, allowVerticalPlacement) {
const textRotate = layer.layout.get("text-rotate").evaluate(feature, {}) * Math.PI / 180;
const quads = [];
for (const line of shaping.positionedLines) for (const positionedGlyph of line.positionedGlyphs) {
if (!positionedGlyph.rect) continue;
const textureRect = positionedGlyph.rect || {};
let rectBuffer = 4;
let isSDF = true;
let pixelRatio = 1;
let lineOffset = 0;
const rotateVerticalGlyph = (alongLine || allowVerticalPlacement) && positionedGlyph.vertical;
const halfAdvance = positionedGlyph.metrics.advance * positionedGlyph.scale / 2;
if (allowVerticalPlacement && shaping.verticalizable) {
const scaledGlyphOffset = (positionedGlyph.scale - 1) * 24;
const imageOffset = (24 - positionedGlyph.metrics.width * positionedGlyph.scale) / 2;
lineOffset = line.lineOffset / 2 - (positionedGlyph.imageName ? -imageOffset : scaledGlyphOffset);
}
if (positionedGlyph.imageName) {
const image = imageMap[positionedGlyph.imageName];
isSDF = image.sdf;
pixelRatio = image.pixelRatio;
rectBuffer = 1 / pixelRatio;
}
const glyphOffset = alongLine ? [positionedGlyph.x + halfAdvance, positionedGlyph.y] : [0, 0];
let builtInOffset = alongLine ? [0, 0] : [positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] - lineOffset];
let verticalizedLabelOffset = [0, 0];
if (rotateVerticalGlyph) {
verticalizedLabelOffset = builtInOffset;
builtInOffset = [0, 0];
}
const textureScale = positionedGlyph.metrics.isDoubleResolution ? 2 : 1;
const x1 = (positionedGlyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset[0];
const y1 = (-positionedGlyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset[1];
const x2 = x1 + textureRect.w / textureScale * positionedGlyph.scale / pixelRatio;
const y2 = y1 + textureRect.h / textureScale * positionedGlyph.scale / pixelRatio;
const tl = new Point(x1, y1);
const tr = new Point(x2, y1);
const bl = new Point(x1, y2);
const br = new Point(x2, y2);
if (rotateVerticalGlyph) {
const center = new Point(-halfAdvance, halfAdvance - -17);
const verticalRotation = -Math.PI / 2;
const xHalfWidthOffsetCorrection = 12 - halfAdvance;
const yImageOffsetCorrection = positionedGlyph.imageName ? xHalfWidthOffsetCorrection : 0;
const halfWidthOffsetCorrection = new Point(22 - xHalfWidthOffsetCorrection, -yImageOffsetCorrection);
const verticalOffsetCorrection = new Point(...verticalizedLabelOffset);
tl._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection);
tr._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection);
bl._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection);
br._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection);
}
if (textRotate) {
const sin = Math.sin(textRotate), cos = Math.cos(textRotate), matrix = [
cos,
-sin,
sin,
cos
];
tl._matMult(matrix);
tr._matMult(matrix);
bl._matMult(matrix);
br._matMult(matrix);
}
const pixelOffsetTL = new Point(0, 0);
const pixelOffsetBR = new Point(0, 0);
quads.push({
tl,
tr,
bl,
br,
tex: textureRect,
writingMode: shaping.writingMode,
glyphOffset,
sectionIndex: positionedGlyph.sectionIndex,
isSDF,
pixelOffsetTL,
pixelOffsetBR,
minFontScaleX: 0,
minFontScaleY: 0
});
}
return quads;
}
//#endregion
//#region src/symbol/collision_feature.ts
/**
* A CollisionFeature represents the area of the tile covered by a single label.
* It is used with CollisionIndex to check if the label overlaps with any
* previous labels. A CollisionFeature is mostly just a set of CollisionBox
* objects.
*/
var CollisionFeature = class {
/**
* Create a CollisionFeature, adding its collision box data to the given collisionBoxArray in the process.
* For line aligned labels a collision circle diameter is computed instead.
*
* @param anchor - The point along the line around which the label is anchored.
* @param shaped - The text or icon shaping results.
* @param boxScale - A magic number used to convert from glyph metrics units to geometry units.
* @param padding - The amount of padding to add around the label edges.
* @param alignLine - Whether the label is aligned with the line or the viewport.
*/
constructor(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaped, boxScale, padding, alignLine, rotate) {
this.boxStartIndex = collisionBoxArray.length;
if (alignLine) {
let top = shaped.top;
let bottom = shaped.bottom;
const collisionPadding = shaped.collisionPadding;
if (collisionPadding) {
top -= collisionPadding[1];
bottom += collisionPadding[3];
}
let height = bottom - top;
if (height > 0) {
height = Math.max(10, height);
this.circleDiameter = height;
}
} else {
const icon = shaped.image?.content && (shaped.image.textFitWidth || shaped.image.textFitHeight) ? applyTextFit(shaped) : {
x1: shaped.left,
y1: shaped.top,
x2: shaped.right,
y2: shaped.bottom
};
icon.y1 = icon.y1 * boxScale - padding[0];
icon.y2 = icon.y2 * boxScale + padding[2];
icon.x1 = icon.x1 * boxScale - padding[3];
icon.x2 = icon.x2 * boxScale + padding[1];
const collisionPadding = shaped.collisionPadding;
if (collisionPadding) {
icon.x1 -= collisionPadding[0] * boxScale;
icon.y1 -= collisionPadding[1] * boxScale;
icon.x2 += collisionPadding[2] * boxScale;
icon.y2 += collisionPadding[3] * boxScale;
}
if (rotate) {
const tl = new Point(icon.x1, icon.y1);
const tr = new Point(icon.x2, icon.y1);
const bl = new Point(icon.x1, icon.y2);
const br = new Point(icon.x2, icon.y2);
const rotateRadians = rotate * Math.PI / 180;
tl._rotate(rotateRadians);
tr._rotate(rotateRadians);
bl._rotate(rotateRadians);
br._rotate(rotateRadians);
icon.x1 = Math.min(tl.x, tr.x, bl.x, br.x);
icon.x2 = Math.max(tl.x, tr.x, bl.x, br.x);
icon.y1 = Math.min(tl.y, tr.y, bl.y, br.y);
icon.y2 = Math.max(tl.y, tr.y, bl.y, br.y);
}
collisionBoxArray.emplaceBack(anchor.x, anchor.y, icon.x1, icon.y1, icon.x2, icon.y2, featureIndex, sourceLayerIndex, bucketIndex);
}
this.boxEndIndex = collisionBoxArray.length;
}
};
//#endregion
//#region node_modules/tinyqueue/index.js
var TinyQueue = class {
constructor(data = [], compare = (a, b) => a < b ? -1 : a > b ? 1 : 0) {
this.data = data;
this.length = this.data.length;
this.compare = compare;
if (this.length > 0) for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
}
push(item) {
this.data.push(item);
this._up(this.length++);
}
pop() {
if (this.length === 0) return void 0;
const top = this.data[0];
const bottom = this.data.pop();
if (--this.length > 0) {
this.data[0] = bottom;
this._down(0);
}
return top;
}
peek() {
return this.data[0];
}
_up(pos) {
const { data, compare } = this;
const item = data[pos];
while (pos > 0) {
const parent = pos - 1 >> 1;
const current = data[parent];
if (compare(item, current) >= 0) break;
data[pos] = current;
pos = parent;
}
data[pos] = item;
}
_down(pos) {
const { data, compare } = this;
const halfLength = this.length >> 1;
const item = data[pos];
while (pos < halfLength) {
let bestChild = (pos << 1) + 1;
const right = bestChild + 1;
if (right < this.length && compare(data[right], data[bestChild]) < 0) bestChild = right;
if (compare(data[bestChild], item) >= 0) break;
data[pos] = data[bestChild];
pos = bestChild;
}
data[pos] = item;
}
};
//#endregion
//#region src/util/find_pole_of_inaccessibility.ts
/**
* Finds an approximation of a polygon's Pole Of Inaccessibility https://en.wikipedia.org/wiki/Pole_of_inaccessibility
* This is a copy of https://github.com/mapbox/polylabel adapted to use Points
*
* @param polygonRings - first item in array is the outer ring followed optionally by the list of holes, should be an element of the result of util/classify_rings
* @param precision - Specified in input coordinate units. If 0 returns after first run, if `> 0` repeatedly narrows the search space until the radius of the area searched for the best pole is less than precision
* @returns Pole of Inaccessibility.
*/
function findPoleOfInaccessibility(polygonRings, precision = 1) {
const bounds = Bounds.fromPoints(polygonRings[0]);
const cellSize = Math.min(bounds.width(), bounds.height());
let h = cellSize / 2;
const cellQueue = new TinyQueue([], compareMax);
const { minX, minY, maxX, maxY } = bounds;
if (cellSize === 0) return new Point(minX, minY);
for (let x = minX; x < maxX; x += cellSize) for (let y = minY; y < maxY; y += cellSize) cellQueue.push(new Cell(x + h, y + h, h, polygonRings));
const centroidCell = getCentroidCell(polygonRings);
let bestCell = centroidCell;
while (cellQueue.length) {
const cell = cellQueue.pop();
if (cell.d > bestCell.d || !bestCell.d) bestCell = cell;
if (cell.max - bestCell.d <= precision) continue;
h = cell.h / 2;
cellQueue.push(new Cell(cell.p.x - h, cell.p.y - h, h, polygonRings));
cellQueue.push(new Cell(cell.p.x + h, cell.p.y - h, h, polygonRings));
cellQueue.push(new Cell(cell.p.x - h, cell.p.y + h, h, polygonRings));
cellQueue.push(new Cell(cell.p.x + h, cell.p.y + h, h, polygonRings));
}
if (centroidCell.d > 0 && bestCell.d - centroidCell.d <= precision) return centroidCell.p;
return bestCell.p;
}
function compareMax(a, b) {
return b.max - a.max;
}
var Cell = class {
constructor(x, y, h, polygon) {
this.p = new Point(x, y);
this.h = h;
this.d = pointToPolygonDist(this.p, polygon);
this.max = this.d + this.h * Math.SQRT2;
}
};
function pointToPolygonDist(p, polygon) {
let inside = false;
let minDistSq = Infinity;
for (const ring of polygon) for (let i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
const a = ring[i];
const b = ring[j];
if (a.y > p.y !== b.y > p.y && p.x < (b.x - a.x) * (p.y - a.y) / (b.y - a.y) + a.x) inside = !inside;
minDistSq = Math.min(minDistSq, distToSegmentSquared(p, a, b));
}
return (inside ? 1 : -1) * Math.sqrt(minDistSq);
}
function getCentroidCell(polygon) {
let area = 0;
let x = 0;
let y = 0;
const points = polygon[0];
for (let i = 0, len = points.length, j = len - 1; i < len; j = i++) {
const a = points[i];
const b = points[j];
const f = a.x * b.y - b.x * a.y;
x += (a.x + b.x) * f;
y += (a.y + b.y) * f;
area += f * 3;
}
return new Cell(x / area, y / area, 0, polygon);
}
//#endregion
//#region src/style/style_layer/variable_text_anchor.ts
let TextAnchorEnum = /* @__PURE__ */ function(TextAnchorEnum) {
TextAnchorEnum[TextAnchorEnum["center"] = 1] = "center";
TextAnchorEnum[TextAnchorEnum["left"] = 2] = "left";
TextAnchorEnum[TextAnchorEnum["right"] = 3] = "right";
TextAnchorEnum[TextAnchorEnum["top"] = 4] = "top";
TextAnchorEnum[TextAnchorEnum["bottom"] = 5] = "bottom";
TextAnchorEnum[TextAnchorEnum["top-left"] = 6] = "top-left";
TextAnchorEnum[TextAnchorEnum["top-right"] = 7] = "top-right";
TextAnchorEnum[TextAnchorEnum["bottom-left"] = 8] = "bottom-left";
TextAnchorEnum[TextAnchorEnum["bottom-right"] = 9] = "bottom-right";
return TextAnchorEnum;
}({});
const baselineOffset = 7;
const INVALID_TEXT_OFFSET = Number.POSITIVE_INFINITY;
function evaluateVariableOffset(anchor, offset) {
function fromRadialOffset(anchor, radialOffset) {
let x = 0, y = 0;
if (radialOffset < 0) radialOffset = 0;
const hypotenuse = radialOffset / Math.SQRT2;
switch (anchor) {
case "top-right":
case "top-left":
y = hypotenuse - baselineOffset;
break;
case "bottom-right":
case "bottom-left":
y = -hypotenuse + baselineOffset;
break;
case "bottom":
y = -radialOffset + baselineOffset;
break;
case "top": y = radialOffset - baselineOffset;
}
switch (anchor) {
case "top-right":
case "bottom-right":
x = -hypotenuse;
break;
case "top-left":
case "bottom-left":
x = hypotenuse;
break;
case "left":
x = radialOffset;
break;
case "right": x = -radialOffset;
}
return [x, y];
}
function fromTextOffset(anchor, offsetX, offsetY) {
let x = 0, y = 0;
offsetX = Math.abs(offsetX);
offsetY = Math.abs(offsetY);
switch (anchor) {
case "top-right":
case "top-left":
case "top":
y = offsetY - baselineOffset;
break;
case "bottom-right":
case "bottom-left":
case "bottom": y = -offsetY + baselineOffset;
}
switch (anchor) {
case "top-right":
case "bottom-right":
case "right":
x = -offsetX;
break;
case "top-left":
case "bottom-left":
case "left": x = offsetX;
}
return [x, y];
}
return offset[1] !== INVALID_TEXT_OFFSET ? fromTextOffset(anchor, offset[0], offset[1]) : fromRadialOffset(anchor, offset[0]);
}
function getTextVariableAnchorOffset(layer, feature, canonical) {
const layout = layer.layout;
const variableAnchorOffset = layout.get("text-variable-anchor-offset")?.evaluate(feature, {}, canonical);
if (variableAnchorOffset) {
const sourceValues = variableAnchorOffset.values;
const destValues = [];
for (let i = 0; i < sourceValues.length; i += 2) {
const anchor = destValues[i] = sourceValues[i];
const offset = sourceValues[i + 1].map((t) => t * 24);
if (anchor.startsWith("top")) offset[1] -= baselineOffset;
else if (anchor.startsWith("bottom")) offset[1] += baselineOffset;
destValues[i + 1] = offset;
}
return new VariableAnchorOffsetCollection(destValues);
}
const variableAnchor = layout.get("text-variable-anchor");
if (variableAnchor) {
let textOffset;
if (layer._unevaluatedLayout.getValue("text-radial-offset") !== void 0) textOffset = [layout.get("text-radial-offset").evaluate(feature, {}, canonical) * 24, INVALID_TEXT_OFFSET];
else textOffset = layout.get("text-offset").evaluate(feature, {}, canonical).map((t) => t * 24);
const anchorOffsets = [];
for (const anchor of variableAnchor) anchorOffsets.push(anchor, evaluateVariableOffset(anchor, textOffset));
return new VariableAnchorOffsetCollection(anchorOffsets);
}
return null;
}
//#endregion
//#region src/symbol/symbol_layout.ts
function performSymbolLayout(args) {
args.bucket.createArrays();
const tileSize = 512 * args.bucket.overscaling;
args.bucket.tilePixelRatio = EXTENT$1 / tileSize;
args.bucket.compareText = {};
args.bucket.iconsNeedLinear = false;
const layer = args.bucket.layers[0];
const layout = layer.layout;
const unevaluatedLayoutValues = layer._unevaluatedLayout._values;
const sizes = {
layoutIconSize: unevaluatedLayoutValues["icon-size"].possiblyEvaluate(new EvaluationParameters(args.bucket.zoom + 1), args.canonical),
layoutTextSize: unevaluatedLayoutValues["text-size"].possiblyEvaluate(new EvaluationParameters(args.bucket.zoom + 1), args.canonical),
textMaxSize: unevaluatedLayoutValues["text-size"].possiblyEvaluate(new EvaluationParameters(18))
};
if (args.bucket.textSizeData.kind === "composite") {
const { minZoom, maxZoom } = args.bucket.textSizeData;
sizes.compositeTextSizes = [unevaluatedLayoutValues["text-size"].possiblyEvaluate(new EvaluationParameters(minZoom), args.canonical), unevaluatedLayoutValues["text-size"].possiblyEvaluate(new EvaluationParameters(maxZoom), args.canonical)];
}
if (args.bucket.iconSizeData.kind === "composite") {
const { minZoom, maxZoom } = args.bucket.iconSizeData;
sizes.compositeIconSizes = [unevaluatedLayoutValues["icon-size"].possiblyEvaluate(new EvaluationParameters(minZoom), args.canonical), unevaluatedLayoutValues["icon-size"].possiblyEvaluate(new EvaluationParameters(maxZoom), args.canonical)];
}
const lineHeight = layout.get("text-line-height") * 24;
const textAlongLine = layout.get("text-rotation-alignment") !== "viewport" && layout.get("symbol-placement") !== "point";
const keepUpright = layout.get("text-keep-upright");
const textSize = layout.get("text-size");
for (const feature of args.bucket.features) {
const fontstack = layout.get("text-font").evaluate(feature, {}, args.canonical).join(",");
const layoutTextSizeThisZoom = textSize.evaluate(feature, {}, args.canonical);
const layoutTextSize = sizes.layoutTextSize.evaluate(feature, {}, args.canonical);
const layoutIconSize = sizes.layoutIconSize.evaluate(feature, {}, args.canonical);
const shapedTextOrientations = {
horizontal: {},
vertical: void 0
};
const text = feature.text;
let textOffset = [0, 0];
if (text) {
const unformattedText = text.toString();
const spacing = layout.get("text-letter-spacing").evaluate(feature, {}, args.canonical) * 24;
const spacingIfAllowed = allowsLetterSpacing(unformattedText) ? spacing : 0;
const textAnchor = layout.get("text-anchor").evaluate(feature, {}, args.canonical);
const variableAnchorOffset = getTextVariableAnchorOffset(layer, feature, args.canonical);
if (!variableAnchorOffset) {
const radialOffset = layout.get("text-radial-offset").evaluate(feature, {}, args.canonical);
if (radialOffset) textOffset = evaluateVariableOffset(textAnchor, [radialOffset * 24, INVALID_TEXT_OFFSET]);
else textOffset = layout.get("text-offset").evaluate(feature, {}, args.canonical).map((t) => t * 24);
}
let textJustify = textAlongLine ? "center" : layout.get("text-justify").evaluate(feature, {}, args.canonical);
const maxWidth = layout.get("symbol-placement") === "point" ? layout.get("text-max-width").evaluate(feature, {}, args.canonical) * 24 : Infinity;
const addVerticalShapingForPointLabelIfNeeded = () => {
if (args.bucket.allowVerticalPlacement && allowsVerticalWritingMode(unformattedText)) shapedTextOrientations.vertical = shapeText(text, args.glyphMap, args.glyphPositions, args.imagePositions, fontstack, maxWidth, lineHeight, textAnchor, "left", spacingIfAllowed, textOffset, 2, true, layoutTextSize, layoutTextSizeThisZoom);
};
if (!textAlongLine && variableAnchorOffset) {
const justifications = /* @__PURE__ */ new Set();
if (textJustify === "auto") for (let i = 0; i < variableAnchorOffset.values.length; i += 2) justifications.add(getAnchorJustification(variableAnchorOffset.values[i]));
else justifications.add(textJustify);
let singleLine = false;
for (const justification of justifications) {
if (shapedTextOrientations.horizontal[justification]) continue;
if (singleLine) shapedTextOrientations.horizontal[justification] = shapedTextOrientations.horizontal[0];
else {
const shaping = shapeText(text, args.glyphMap, args.glyphPositions, args.imagePositions, fontstack, maxWidth, lineHeight, "center", justification, spacingIfAllowed, textOffset, 1, false, layoutTextSize, layoutTextSizeThisZoom);
if (shaping) {
shapedTextOrientations.horizontal[justification] = shaping;
singleLine = shaping.positionedLines.length === 1;
}
}
}
addVerticalShapingForPointLabelIfNeeded();
} else {
if (textJustify === "auto") textJustify = getAnchorJustification(textAnchor);
const shaping = shapeText(text, args.glyphMap, args.glyphPositions, args.imagePositions, fontstack, maxWidth, lineHeight, textAnchor, textJustify, spacingIfAllowed, textOffset, 1, false, layoutTextSize, layoutTextSizeThisZoom);
if (shaping) shapedTextOrientations.horizontal[textJustify] = shaping;
addVerticalShapingForPointLabelIfNeeded();
if (allowsVerticalWritingMode(unformattedText) && textAlongLine && keepUpright) shapedTextOrientations.vertical = shapeText(text, args.glyphMap, args.glyphPositions, args.imagePositions, fontstack, maxWidth, lineHeight, textAnchor, textJustify, spacingIfAllowed, textOffset, 2, false, layoutTextSize, layoutTextSizeThisZoom);
}
}
let shapedIcon;
let isSDFIcon = false;
if (feature.icon?.name) {
const image = args.imageMap[feature.icon.name];
if (image) {
shapedIcon = shapeIcon(args.imagePositions[feature.icon.name], layout.get("icon-offset").evaluate(feature, {}, args.canonical), layout.get("icon-anchor").evaluate(feature, {}, args.canonical));
isSDFIcon = !!image.sdf;
if (args.bucket.sdfIcons === void 0) args.bucket.sdfIcons = isSDFIcon;
else if (args.bucket.sdfIcons !== isSDFIcon) warnOnce("Style sheet warning: Cannot mix SDF and non-SDF icons in one buffer");
if (image.pixelRatio !== args.bucket.pixelRatio) args.bucket.iconsNeedLinear = true;
else if (layout.get("icon-rotate").constantOr(1) !== 0) args.bucket.iconsNeedLinear = true;
}
}
const shapedText = getDefaultHorizontalShaping(shapedTextOrientations.horizontal) || shapedTextOrientations.vertical;
args.bucket.iconsInText ||= shapedText ? shapedText.iconsInText : false;
if (shapedText || shapedIcon) addFeature(args.bucket, feature, shapedTextOrientations, shapedIcon, args.imageMap, sizes, layoutTextSize, layoutIconSize, textOffset, isSDFIcon, args.canonical, args.subdivisionGranularity);
}
if (args.showCollisionBoxes) args.bucket.generateCollisionDebugBuffers();
}
function getAnchorJustification(anchor) {
switch (anchor) {
case "right":
case "top-right":
case "bottom-right": return "right";
case "left":
case "top-left":
case "bottom-left": return "left";
}
return "center";
}
/**
* Given a feature and its shaped text and icon data, add a 'symbol
* instance' for each _possible_ placement of the symbol feature.
* (At render it selects which of these instances to
* show or hide based on collisions with symbols in other layers.)
*/
function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, imageMap, sizes, layoutTextSize, layoutIconSize, textOffset, isSDFIcon, canonical, subdivisionGranularity) {
let textMaxSize = sizes.textMaxSize.evaluate(feature, {});
if (textMaxSize === void 0) textMaxSize = layoutTextSize;
const layout = bucket.layers[0].layout;
const iconOffset = layout.get("icon-offset").evaluate(feature, {}, canonical);
const defaultHorizontalShaping = getDefaultHorizontalShaping(shapedTextOrientations.horizontal);
const glyphSize = 24, fontScale = layoutTextSize / glyphSize, textBoxScale = bucket.tilePixelRatio * fontScale, textMaxBoxScale = bucket.tilePixelRatio * textMaxSize / glyphSize, iconBoxScale = bucket.tilePixelRatio * layoutIconSize, symbolMinDistance = bucket.tilePixelRatio * layout.get("symbol-spacing"), textPadding = layout.get("text-padding") * bucket.tilePixelRatio, iconPadding = getIconPadding(layout, feature, canonical, bucket.tilePixelRatio), textMaxAngle = layout.get("text-max-angle") / 180 * Math.PI, textAlongLine = layout.get("text-rotation-alignment") !== "viewport" && layout.get("symbol-placement") !== "point", iconAlongLine = layout.get("icon-rotation-alignment") === "map" && layout.get("symbol-placement") !== "point", symbolPlacement = layout.get("symbol-placement"), textRepeatDistance = symbolMinDistance / 2;
const iconTextFit = layout.get("icon-text-fit");
let verticallyShapedIcon;
if (shapedIcon && iconTextFit !== "none") {
if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) verticallyShapedIcon = fitIconToText(shapedIcon, shapedTextOrientations.vertical, iconTextFit, layout.get("icon-text-fit-padding"), iconOffset, fontScale);
if (defaultHorizontalShaping) shapedIcon = fitIconToText(shapedIcon, defaultHorizontalShaping, iconTextFit, layout.get("icon-text-fit-padding"), iconOffset, fontScale);
}
const granularity = canonical ? subdivisionGranularity.line.getGranularityForZoomLevel(canonical.z) : 1;
const addSymbolAtAnchor = (line, anchor) => {
if (anchor.x < 0 || anchor.x >= 8192 || anchor.y < 0 || anchor.y >= 8192) return;
addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, imageMap, verticallyShapedIcon, bucket.layers[0], bucket.collisionBoxArray, feature.index, feature.sourceLayerIndex, bucket.index, textBoxScale, [
textPadding,
textPadding,
textPadding,
textPadding
], textAlongLine, textOffset, iconBoxScale, iconPadding, iconAlongLine, iconOffset, feature, sizes, isSDFIcon, canonical, layoutTextSize);
};
if (symbolPlacement === "line") for (const line of clipLine(feature.geometry, 0, 0, EXTENT$1, EXTENT$1)) {
const subdividedLine = subdivideVertexLine(line, granularity);
const anchors = getAnchors(subdividedLine, symbolMinDistance, textMaxAngle, shapedTextOrientations.vertical || defaultHorizontalShaping, shapedIcon, glyphSize, textMaxBoxScale, bucket.overscaling, EXTENT$1);
for (const anchor of anchors) {
const shapedText = defaultHorizontalShaping;
if (!shapedText || !anchorIsTooClose(bucket, shapedText.text, textRepeatDistance, anchor)) addSymbolAtAnchor(subdividedLine, anchor);
}
}
else if (symbolPlacement === "line-center") {
for (const line of feature.geometry) if (line.length > 1) {
const subdividedLine = subdivideVertexLine(line, granularity);
const anchor = getCenterAnchor(subdividedLine, textMaxAngle, shapedTextOrientations.vertical || defaultHorizontalShaping, shapedIcon, glyphSize, textMaxBoxScale);
if (anchor) addSymbolAtAnchor(subdividedLine, anchor);
}
} else if (feature.type === "Polygon") for (const polygon of classifyRings$1(feature.geometry, 0)) {
const poi = findPoleOfInaccessibility(polygon, 16);
addSymbolAtAnchor(subdivideVertexLine(polygon[0], granularity, true), new Anchor(poi.x, poi.y, 0));
}
else if (feature.type === "LineString") for (const line of feature.geometry) {
const subdividedLine = subdivideVertexLine(line, granularity);
addSymbolAtAnchor(subdividedLine, new Anchor(subdividedLine[0].x, subdividedLine[0].y, 0));
}
else if (feature.type === "Point") for (const points of feature.geometry) for (const point of points) addSymbolAtAnchor([point], new Anchor(point.x, point.y, 0));
}
function addTextVariableAnchorOffsets(textAnchorOffsets, variableAnchorOffset) {
const startIndex = textAnchorOffsets.length;
const values = variableAnchorOffset?.values;
if (values?.length > 0) for (let i = 0; i < values.length; i += 2) {
const anchor = TextAnchorEnum[values[i]];
const offset = values[i + 1];
textAnchorOffsets.emplaceBack(anchor, offset[0], offset[1]);
}
return [startIndex, textAnchorOffsets.length];
}
function addTextVertices(bucket, anchor, shapedText, imageMap, layer, textAlongLine, feature, textOffset, lineArray, writingMode, placementTypes, placedTextSymbolIndices, placedIconIndex, sizes, canonical) {
const glyphQuads = getGlyphQuads(anchor, shapedText, textOffset, layer, textAlongLine, feature, imageMap, bucket.allowVerticalPlacement);
const sizeData = bucket.textSizeData;
let textSizeData = null;
if (sizeData.kind === "source") {
textSizeData = [128 * layer.layout.get("text-size").evaluate(feature, {})];
if (textSizeData[0] > 32640) warnOnce(`${bucket.layerIds[0]}: Value for "text-size" is >= 255. Reduce your "text-size".`);
} else if (sizeData.kind === "composite") {
textSizeData = [128 * sizes.compositeTextSizes[0].evaluate(feature, {}, canonical), 128 * sizes.compositeTextSizes[1].evaluate(feature, {}, canonical)];
if (textSizeData[0] > 32640 || textSizeData[1] > 32640) warnOnce(`${bucket.layerIds[0]}: Value for "text-size" is >= 255. Reduce your "text-size".`);
}
bucket.addSymbols(bucket.text, glyphQuads, textSizeData, textOffset, textAlongLine, feature, writingMode, anchor, lineArray.lineStartIndex, lineArray.lineLength, placedIconIndex, canonical);
for (const placementType of placementTypes) placedTextSymbolIndices[placementType] = bucket.text.placedSymbolArray.length - 1;
return glyphQuads.length * 4;
}
function getDefaultHorizontalShaping(horizontalShaping) {
for (const justification in horizontalShaping) return horizontalShaping[justification];
return null;
}
/**
* Add a single label & icon placement.
*/
function addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, imageMap, verticallyShapedIcon, layer, collisionBoxArray, featureIndex, sourceLayerIndex, bucketIndex, textBoxScale, textPadding, textAlongLine, textOffset, iconBoxScale, iconPadding, iconAlongLine, iconOffset, feature, sizes, isSDFIcon, canonical, layoutTextSize) {
const lineArray = bucket.addToLineVertexArray(anchor, line);
let textCollisionFeature, iconCollisionFeature, verticalTextCollisionFeature, verticalIconCollisionFeature;
let numIconVertices = 0;
let numVerticalIconVertices = 0;
let numHorizontalGlyphVertices = 0;
let numVerticalGlyphVertices = 0;
let placedIconSymbolIndex = -1;
let verticalPlacedIconSymbolIndex = -1;
const placedTextSymbolIndices = {};
let key = (0, import_murmurhash_js.default)("");
if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) {
const verticalTextRotation = layer.layout.get("text-rotate").evaluate(feature, {}, canonical) + 90;
const verticalShaping = shapedTextOrientations.vertical;
verticalTextCollisionFeature = new CollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textBoxScale, textPadding, textAlongLine, verticalTextRotation);
if (verticallyShapedIcon) verticalIconCollisionFeature = new CollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticallyShapedIcon, iconBoxScale, iconPadding, textAlongLine, verticalTextRotation);
}
if (shapedIcon) {
const iconRotate = layer.layout.get("icon-rotate").evaluate(feature, {});
const hasIconTextFit = layer.layout.get("icon-text-fit") !== "none";
const iconQuads = getIconQuads(shapedIcon, iconRotate, isSDFIcon, hasIconTextFit);
const verticalIconQuads = verticallyShapedIcon ? getIconQuads(verticallyShapedIcon, iconRotate, isSDFIcon, hasIconTextFit) : void 0;
iconCollisionFeature = new CollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedIcon, iconBoxScale, iconPadding, false, iconRotate);
numIconVertices = iconQuads.length * 4;
const sizeData = bucket.iconSizeData;
let iconSizeData = null;
if (sizeData.kind === "source") {
iconSizeData = [128 * layer.layout.get("icon-size").evaluate(feature, {})];
if (iconSizeData[0] > 32640) warnOnce(`${bucket.layerIds[0]}: Value for "icon-size" is >= 255. Reduce your "icon-size".`);
} else if (sizeData.kind === "composite") {
iconSizeData = [128 * sizes.compositeIconSizes[0].evaluate(feature, {}, canonical), 128 * sizes.compositeIconSizes[1].evaluate(feature, {}, canonical)];
if (iconSizeData[0] > 32640 || iconSizeData[1] > 32640) warnOnce(`${bucket.layerIds[0]}: Value for "icon-size" is >= 255. Reduce your "icon-size".`);
}
bucket.addSymbols(bucket.icon, iconQuads, iconSizeData, iconOffset, iconAlongLine, feature, 0, anchor, lineArray.lineStartIndex, lineArray.lineLength, -1, canonical);
placedIconSymbolIndex = bucket.icon.placedSymbolArray.length - 1;
if (verticalIconQuads) {
numVerticalIconVertices = verticalIconQuads.length * 4;
bucket.addSymbols(bucket.icon, verticalIconQuads, iconSizeData, iconOffset, iconAlongLine, feature, 2, anchor, lineArray.lineStartIndex, lineArray.lineLength, -1, canonical);
verticalPlacedIconSymbolIndex = bucket.icon.placedSymbolArray.length - 1;
}
}
const justifications = Object.keys(shapedTextOrientations.horizontal);
for (const justification of justifications) {
const shaping = shapedTextOrientations.horizontal[justification];
if (!textCollisionFeature) {
key = (0, import_murmurhash_js.default)(shaping.text);
textCollisionFeature = new CollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textBoxScale, textPadding, textAlongLine, layer.layout.get("text-rotate").evaluate(feature, {}, canonical));
}
const singleLine = shaping.positionedLines.length === 1;
numHorizontalGlyphVertices += addTextVertices(bucket, anchor, shaping, imageMap, layer, textAlongLine, feature, textOffset, lineArray, shapedTextOrientations.vertical ? 1 : 3, singleLine ? justifications : [justification], placedTextSymbolIndices, placedIconSymbolIndex, sizes, canonical);
if (singleLine) break;
}
if (shapedTextOrientations.vertical) numVerticalGlyphVertices += addTextVertices(bucket, anchor, shapedTextOrientations.vertical, imageMap, layer, textAlongLine, feature, textOffset, lineArray, 2, ["vertical"], placedTextSymbolIndices, verticalPlacedIconSymbolIndex, sizes, canonical);
const textBoxStartIndex = textCollisionFeature ? textCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length;
const textBoxEndIndex = textCollisionFeature ? textCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length;
const verticalTextBoxStartIndex = verticalTextCollisionFeature ? verticalTextCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length;
const verticalTextBoxEndIndex = verticalTextCollisionFeature ? verticalTextCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length;
const iconBoxStartIndex = iconCollisionFeature ? iconCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length;
const iconBoxEndIndex = iconCollisionFeature ? iconCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length;
const verticalIconBoxStartIndex = verticalIconCollisionFeature ? verticalIconCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length;
const verticalIconBoxEndIndex = verticalIconCollisionFeature ? verticalIconCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length;
let collisionCircleDiameter = -1;
const getCollisionCircleHeight = (feature, prevHeight) => {
if (feature?.circleDiameter) return Math.max(feature.circleDiameter, prevHeight);
return prevHeight;
};
collisionCircleDiameter = getCollisionCircleHeight(textCollisionFeature, collisionCircleDiameter);
collisionCircleDiameter = getCollisionCircleHeight(verticalTextCollisionFeature, collisionCircleDiameter);
collisionCircleDiameter = getCollisionCircleHeight(iconCollisionFeature, collisionCircleDiameter);
collisionCircleDiameter = getCollisionCircleHeight(verticalIconCollisionFeature, collisionCircleDiameter);
const useRuntimeCollisionCircles = collisionCircleDiameter > -1 ? 1 : 0;
if (useRuntimeCollisionCircles) collisionCircleDiameter *= layoutTextSize / 24;
if (bucket.glyphOffsetArray.length >= SymbolBucket.MAX_GLYPHS) warnOnce("Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907");
if (feature.sortKey !== void 0) bucket.addToSortKeyRanges(bucket.symbolInstances.length, feature.sortKey);
const variableAnchorOffset = getTextVariableAnchorOffset(layer, feature, canonical);
const [textAnchorOffsetStartIndex, textAnchorOffsetEndIndex] = addTextVariableAnchorOffsets(bucket.textAnchorOffsets, variableAnchorOffset);
bucket.symbolInstances.emplaceBack(anchor.x, anchor.y, placedTextSymbolIndices.right >= 0 ? placedTextSymbolIndices.right : -1, placedTextSymbolIndices.center >= 0 ? placedTextSymbolIndices.center : -1, placedTextSymbolIndices.left >= 0 ? placedTextSymbolIndices.left : -1, placedTextSymbolIndices.vertical || -1, placedIconSymbolIndex, verticalPlacedIconSymbolIndex, key, textBoxStartIndex, textBoxEndIndex, verticalTextBoxStartIndex, verticalTextBoxEndIndex, iconBoxStartIndex, iconBoxEndIndex, verticalIconBoxStartIndex, verticalIconBoxEndIndex, featureIndex, numHorizontalGlyphVertices, numVerticalGlyphVertices, numIconVertices, numVerticalIconVertices, useRuntimeCollisionCircles, 0, textBoxScale, collisionCircleDiameter, textAnchorOffsetStartIndex, textAnchorOffsetEndIndex);
}
function anchorIsTooClose(bucket, text, repeatDistance, anchor) {
const compareText = bucket.compareText;
if (!(text in compareText)) compareText[text] = [];
else {
const otherAnchors = compareText[text];
for (let k = otherAnchors.length - 1; k >= 0; k--) if (anchor.dist(otherAnchors[k]) < repeatDistance) return true;
}
compareText[text].push(anchor);
return false;
}
//#endregion
export { tileCoordinatesToMercatorCoordinates as $, deepEqual$1 as $n, mul$3 as $r, TRANSITION_SUFFIX as $t, evaluateSizeForFeature as A, perspective as Ai, getVideo as An, remapSaturate as Ar, toEvaluationFeature as At, PbfReader as B, invert$5 as Bi, JSON_PREFIX as Bn, wrap$1 as Br, SegmentVector as Bt, isCustomStyleLayer as C, equals$6 as Ci, Event as Cn, nextPowerOfTwo as Cr, HEATMAP_FULL_RENDER_FBO_KEY as Ct, SymbolBucket as D, invert$2 as Di, getArrayBuffer as Dn, radiansToDegrees as Dr, RGBAImage as Dt, isSymbolStyleLayer as E, identity$2 as Ei, GLOBAL_DISPATCHER_ID as En, pointPlaneSignedDistance as Er, AlphaImage as Et, ImagePosition as F, translate$2 as Fi, removeProtocol as Fn, subscribe as Fr, Uniform4f as Ft, isFillExtrusionStyleLayer as G, bezier as Gn, length as Gr, PosArray as Gt, isLineStyleLayer as H, isOffscreenCanvasDistorted as Hi, angleToRotateBetweenVectors2D as Hn, pixelsToTileUnits as Hr, CollisionCircleLayoutArray as Ht, potpack as I, create$6 as Ii, config as In, threePlaneIntersection as Ir, UniformColor as It, cameraDirectionFromPitchBearing as J, createIdentityMat4f32 as Jn, zero as Jr, TriangleIndexArray as Jt, FillExtrusionBucket as K, clamp$2 as Kn, scale as Kr, QuadTriangleArray as Kt, isStyleImageWebGLData as L, fromRotation$2 as Li, AbortError as Ln, translatePosition as Lr, UniformColorArray as Lt, WritingMode as M, rotateY$3 as Mi, sameOrigin as Mn, rollPitchBearingToQuat as Mr, Uniform1i as Mt, getAnchorAlignment as N, rotateZ$3 as Ni, addProtocol as Nn, scaleZoom as Nr, Uniform2f as Nt, addDynamicAttributes as O, multiply$5 as Oi, getJSON as On, rayPlaneIntersection as Or, isCircleStyleLayer as Ot, ImageAtlas as P, scale$5 as Pi, getProtocol as Pn, sphericalToCartesian as Pr, Uniform3f as Pt, projectToWorldCoordinates as Q, createVec4f64 as Qn, slerp as Qr, Properties as Qt, renderStyleImage as R, create$8 as Ri, isAbortError as Rn, uniqueId as Rr, UniformFloatArray as Rt, createStyleLayer as S, create$5 as Si, ErrorEvent as Sn, mod as Sr, isHillshadeStyleLayer as St, isBackgroundStyleLayer as T, fromScaling as Ti, AJAXError as Tn, pick as Tr, renderColorRamp as Tt, LineBucket as U, offscreenCanvasSupported as Ui, arrayBufferToImage as Un, EXTENT$1 as Ur, LineStripIndexArray as Ut, collisionCircleLayout as V, rotate$4 as Vi, MAX_VALID_LATITUDE as Vn, zoomScale as Vr, CollisionBoxArray as Vt, GeoJSONVT as W, Point as Wi, arrayBufferToImageBitmap as Wn, create as Wr, Pos3dArray as Wt, getMercatorHorizon as X, createMat4f64 as Xn, fromValues$2 as Xr, isRasterStyleLayer as Xt, cameraMercatorCoordinateFromCenterAndRotation as Y, createIdentityMat4f64 as Yn, fromEuler as Yr, createLayout as Yt, maxMercatorHorizonAngle as Z, createVec3f64 as Zn, multiply$2 as Zr, DataConstantProperty as Zt, UnwrappedTileID as _, transformMat4$2 as _i, emptyStyle as _n, isTouchableEvent as _r, SubdivisionGranularityExpression as _t, clipLine as a, dot$5 as ai, register as an, evaluateZoomSnap as ar, mercatorXfromLng as at, isInBoundsForZoomLngLat as b, clone$6 as bi, interpolateFactory as bn, lerp as br, DEMData as bt, FeatureIndex as c, negate$2 as ci, validateAndEmit as cn, findLineIntersection as cr, LngLat as ct, DictionaryCoder as d, rotateY$2 as di, Color as dn, getEdgeTiles as dr, EXTENT_BOUNDS as dt, scale$3 as ei, Transitionable as en, defaultEasing as er, unprojectFromWorldCoordinates as et, GEOJSON_TILE_LAYER_NAME as f, rotateZ$2 as fi, ProjectionDefinition as fn, getImageData as fr, Bounds as ft, OverscaledTileID as g, transformMat3$1 as gi, diff as gn, isSafari as gr, SOUTH_POLE_Y as gt, CanonicalTileID as h, sub$2 as hi, derefLayers as hn, isPointableEvent as hr, NORTH_POLE_Y as ht, clipGeometry as i, cross$2 as ii, ZoomHistory as in, ensureError as ir, lngFromMercatorX as it, evaluateSizeForZoom as j, rotateX$3 as ji, makeRequest as jn, rollPitchBearingEqual as jr, Uniform1f as jt, getOverlapMode as k, ortho as ki, getReferrer as kn, readImageUsingVideoFrame as kr, polygonIntersectsPolygon as kt, MLTVectorTile as l, normalize$4 as li, validateStyle as ln, getAABB as lr, earthRadius as lt, fromVectorTileJs as m, scaleAndAdd$2 as mi, createExpression as mn, isImageBitmap as mr, FillBucket as mt, performSymbolLayout as n, add$4 as ni, rtlWorkerPlugin as nn, differenceOfAnglesDegrees as nr, altitudeFromMercatorZ as nt, BoundedLRUCache as o, len$4 as oi, SPEC_SOURCE_TYPES as on, extend as or, mercatorYfromLat as ot, GeoJSONWrapper as p, scale$4 as pi, ValidationError as pn, getRollPitchBearing as pr, isFillStyleLayer as pt, calculateTileMatrix as q, clone as qn, sqrLen as qr, RasterBoundsArray as qt, TextAnchorEnum as r, clone$5 as ri, codePointUsesLocalIdeographFontFamily as rn, distanceOfAnglesRadians as rr, latFromMercatorY as rt, TileCache as s, length$4 as si, emitValidationErrors as sn, filterObject as sr, mercatorZfromAltitude as st, getAnchorJustification as t, transformMat4$1 as ti, EvaluationParameters as tn, degreesToRadians as tr, MercatorCoordinate as tt, GeoJSONFeature as u, rotateX$2 as ui, validateStyleAndEmit as un, getAngleDelta as ur, VectorTile as ut, calculateTileKey as v, transformQuat$1 as vi, featureFilter as vn, isTouchableOrPointableType as vr, SubdivisionGranularitySetting as vt, validateCustomStyleLayer as w, exactEquals$5 as wi, Evented as wn, parseCacheControl as wr, isHeatmapStyleLayer as wt, Actor as x, copy$5 as xi, latest as xn, mapObject as xr, Texture as xt, compareTileId as y, zero$2 as yi, groupByLayout as yn, isWorker as yr, isColorReliefStyleLayer as yt, parseGlyphPbf as z, determinant$3 as zi, throwIfAborted as zn, warnOnce as zr, UniformMatrix4f as zt };
//# sourceMappingURL=maplibre-gl-shared-dev.mjs.map